多態性,作為面向對象編程中的一個基本概念,允許不同類型的對象被視為共同超類的實例。這種靈活性對於創建易於擴展和維護的系統至關重要。雖然傳統的 SQL 數據庫結合 Jakarta 持久性(JPA)可以處理多態數據,但 NoSQL 數據庫提供了明顯的優勢。與需要嚴格模式定義的 SQL 數據庫不同,NoSQL 數據庫採用無模式的方法,自然支持動態和靈活的數據結構。當與 Jakarta NoSQL 集成時,這種靈活性顯得尤為吸引人,因為該工具提供了通過自定義轉換器定義和管理多態字段的強大支持。
在許多企業應用中,通常需要管理不同類型的數據對象。例如,一個電子商務平台可能處理各種支付方式,如信用卡、數位錢包和銀行轉帳,每種方式都有其特定的屬性。同樣,大型企業的資產管理系統處理不同類型的資產,如不動產、機械設備和智慧財產權,每種資產都有其獨特的屬性。醫療保健系統必須容納各種數據類型,從個人信息到醫療記錄和測試結果。利用具有多態字段的 NoSQL 數據庫可以將這些多樣的數據類型統一存儲和管理。NoSQL 數據庫的無模式特性使其比關聯數據庫更容易適應變化的需求。
本教程將展示如何使用 Jakarta NoSQL 來管理多态字段,並使用自定義轉換器。我們將包含示例代碼,以整合 使用 Helidon 和 Oracle NoSQL 的 REST 服務。本示例將展示在無模式 NoSQL 數據庫環境中高效管理各種數據類型的多态性實際應用。
使用 NoSQL 和 Jakarta NoSQL 進行多態
本教程將探尋 Java 世界的 NoSQL 和無模式功能,使用 Oracle NoSQL、Java Helidon 和 Rest API。我們將創建一個 Machine
实体,其中將提供一個 engine
字段,我們將其轉化為 JSON。感謝 Oracle NoSQL 的自然靈活性和其無模式設計,這種方法无缝結合。
第一步是創建 Helidon 項目,您可以用 Helidon Starter: Helidon Starter。
創建 Microprofile compliant 項目後,下一步是 Include the Oracle NoSQL driver: Oracle NoSQL Driver。
在屬性文件中,因為我們將在本地下運行,我們需要兩個屬性:一個用來定義 Host connection,第二個用來定義數據庫名稱:
jnosql.document.database=machines
jnosql.oracle.nosql.host=http://localhost:8080
同時,將端口更新為使用 8181:
server.port=8181
下一步是配置和運行Oracle NoSQL。為了讓過程更簡單,我們將使用Docker,但重要的是要注意Oracle NoSQL也支持在Oracle基礎設施上部署到雲:
docker run -d --name oracle-instance -p 8080:8080 ghcr.io/oracle/nosql:latest-ce
通過使用Docker,我們簡化了設定過程,並確保我們的Oracle NoSQL實例在受控制的環境中運行。這種設定為開發和測試目的提供了一個實際的方法,強調了將Oracle NoSQL部署在不同的環境,包括雲基礎設施中的靈活性。
在設定和數據庫之後,下一個步驟是定義Entity
並創建Converter
實現。在這個示例中,我們將展示Jakarta NoSQL和Jakarta JSON-B的无缝集成,展示不同的Jakarta規範如何有效地一起工作。
最初步驟是定義Machine
实体,它包括一個多态Engine
字段。
@Entity
@JsonbVisibility(FieldAccessStrategy.class)
public class Machine {
@Id
private String id;
@Column
@Convert(EngineConverter.class)
private Engine engine;
@Column
private String manufacturer;
@Column
private int year;
// Getters and setters
}
接下來,我們定義Engine
類別,並使用JsonbTypeInfo
來處理多態性:
@JsonbTypeInfo(
key = "type",
value = {
@JsonbSubtype(alias = "gas", type = GasEngine.class),
@JsonbSubtype(alias = "electric", type = ElectricEngine.class)
}
)
@JsonbVisibility(FieldAccessStrategy.class)
public abstract class Engine {
// 常見引擎屬性
// Getters and setters
}
引擎轉 converter可能會因提供者而有所不同,它可以作為String
、Map
、BSON
等使用。
設定完配置和數據庫後,下一步是創建應用程序和數據庫的橋接。Eclipse JNoSQL通過一個接口整合了兩個規格,JavaKarata NoSQL和JavaKarata Data,以處理這件事。注解處理必要的步驟。
首先,我們定義了MachineRepository
接口:
@Repository
public interface MachineRepository extends BasicRepository<Machine, String> {
@Query("from Machine where engine.type = :type")
List<Machine> findByType(@Param("type") String type);
}
這個倉庫接口讓我們能直接在JSON數據中搜索元素,而無需解決任何問題,並且您還可以進一步擴展它,而無需創建剛性的結構。
接下來,我們定義了控制器以暴露這個資源:
@Path("/machines")
@ApplicationScoped
public class MachineResource {
private static final Logger LOGGER = Logger.getLogger(MachineResource.class.getName());
public static final Order<Machine> ORDER_MANUFACTURER = Order.by(Sort.asc("manufacturer"));
private final MachineRepository repository;
@Inject
public MachineResource(@Database(DatabaseType.DOCUMENT) MachineRepository repository) {
this.repository = repository;
}
@GET
public List<Machine> getMachines(@QueryParam("page") @DefaultValue("1") int page, @QueryParam("page_size") @DefaultValue("10") int pageSize) {
LOGGER.info("Get machines from page " + page + " with page size " + pageSize);
Page<Machine> machines = this.repository.findAll(PageRequest.ofPage(page).size(pageSize), ORDER_MANUFACTURER);
return machines.content();
}
@GET
@Path("gas")
public List<Machine> getGasMachines() {
return this.repository.findByType("gas");
}
@GET
@Path("electric")
public List<Machine> getElectricMachines() {
return this.repository.findByType("electric");
}
@GET
@Path("{id}")
public Machine get(@PathParam("id") String id) {
LOGGER.info("Get machine by id " + id);
return this.repository.findById(id)
.orElseThrow(() -> new WebApplicationException("Machine not found with id: " + id, Response.Status.NOT_FOUND));
}
@PUT
public void save(Machine machine) {
LOGGER.info("Saving a machine " + machine);
this.repository.save(machine);
}
}
這個控制器暴露了用於管理機械设备的端點,包括通過類型和分頁獲取機械设备。
最後,使用以下curl
命令執行應用程序並插入數據:
curl --location --request PUT 'http://localhost:8181/machines' \
--header 'Content-Type: application/json' \
--data '{
"id": "1",
"model": "Thunderbolt V8",
"engine": {
"type": "gas",
"horsepower": 450
},
"manufacturer": "Mustang",
"year": 2021,
"weight": 1600.0
}'
curl --location --request PUT 'http://localhost:8181/machines' \
--header 'Content-Type: application/json' \
--data '{
"id": "2",
"model": "Eagle Eye EV",
"engine": {
"type": "electric",
"horsepower": 300
},
"manufacturer": "Tesla",
"year": 2022,
"weight": 1400.0
}'
curl --location --request PUT 'http://localhost:8181/machines' \
--header 'Content-Type: application/json' \
--data '{
"id": "3",
"model": "Road Runner GT",
"engine": {
"type": "gas",
"horsepower": 400
},
"manufacturer": "Chevrolet",
"year": 2020,
"weight": 1700.0
}'
curl --location --request PUT 'http://localhost:8181/machines' \
--header 'Content-Type: application/json' \
--data '{
"id": "4",
"model": "Solaris X",
"engine": {
"type": "electric",
"horsepower": 350
},
"manufacturer": "Nissan",
"year": 2023,
"weight": 1350.0
}'
curl --location --request PUT 'http://localhost:8181/machines' \
--header 'Content-Type: application/json' \
--data '{
"id": "5",
"model": "Fusion Hybrid 2024",
"engine": {
"type": "electric",
"horsepower": 320
},
"manufacturer": "Toyota",
"year": 2024,
"weight": 1450.0
}'
這個設定讓您能在JSON中按照類型進行搜索,並且分頁容易實現。有關分頁技術的更多信息,請參考這篇文章。
插入一些數據後,您可以探索並理解如何進行搜索:
- 取得帶有分頁的機器
- 取得所有機器
- 取得氣體機器
最後的想法
使用Jakarta NoSQL和Jakarta JSON-B將多態性與NoSQL資料庫整合,提供了靈活性和效率來管理不同類型的資料。利用NoSQL的無模式特性,這種方法簡化了開發並增強了應用程序的適應性。如需完整的示例和源代碼,請訪問soujava/helidon-oracle-json-types。
Source:
https://dzone.com/articles/intro-to-polymorphism-with-db-engines-in-nosql