將 JPA 實體轉換為 Mendix
Jan 13, 2025 pm 06:04 PM最近在探索 Mendix 時,我注意到他們有一個 Platform SDK,讓您可以透過 API 與 mendix 應用程式模型互動。
這給了我一個想法,探索它是否可以用於創建我們的領域模型。具體來說,是基於現有的傳統應用程式創建領域模型。
如果進一步推廣,這可用於將任何現有應用程式轉換為 Mendix 並從那裡繼續開發。
將 Java/Spring Web 應用程式轉換為 Mendix
因此,我創建了一個帶有簡單 API 和資料庫層的小型 Java/Spring Web 應用程式。為了簡單起見,它使用嵌入式 H2 資料庫。
在這篇文章中,我們將只轉換 JPA 實體。讓我們來看看它們:
@Entity @Table(name = "CAT") class Cat { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private int age; private String color; @OneToOne private Human humanPuppet; ... constructor ... ... getters ... } @Entity @Table(name = "HUMAN") public class Human { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; ... constructor ... ... getters ... }
如你所見,它們非常簡單:一隻有名字、年齡、顏色的貓和它的人類傀儡,因為正如我們所知,貓統治著世界。
它們都有一個自動產生的 ID 欄位。貓與人類有一對一的聯繫,這樣它就可以隨時稱呼它的人類。 (如果它不是 JPA 實體,我會放置一個 meow() 方法,但讓我們將其留到將來)。
應用程式功能齊全,但現在我們只對資料層感興趣。
提取 json 中的實體元數據
這可以用幾種不同的方式來完成:
- 透過靜態分析套件中的實體。
- 透過使用反射在運行時讀取這些實體。
我選擇了選項 2,因為它更快,而且我無法輕鬆找到可以執行選項 1 的庫。
接下來,我們需要決定建造後如何公開 json。為了簡單起見,我們只需將其寫入文件即可。一些替代方法可能是:
- 透過 api 公開它。這更加複雜,因為您還需要確保端點受到良好的保護,因為我們不能公開暴露我們的元資料。
- 透過一些管理工具公開它,例如 Spring Boot Actuator 或 jmx。它更安全,但仍然需要時間來設定。
現在讓我們來看看實際的程式碼:
public class MendixExporter { public static void exportEntitiesTo(String filePath) throws IOException { AnnotatedTypeScanner typeScanner = new AnnotatedTypeScanner(false, Entity.class); Set<Class<?>> entityClasses = typeScanner.findTypes(JavaToMendixApplication.class.getPackageName()); log.info("Entity classes are: {}", entityClasses); List<MendixEntity> mendixEntities = new ArrayList<>(); for (Class<?> entityClass : entityClasses) { List<MendixAttribute> attributes = new ArrayList<>(); for (Field field : entityClass.getDeclaredFields()) { AttributeType attributeType = determineAttributeType(field); AssociationType associationType = determineAssociationType(field, attributeType); String associationEntityType = determineAssociationEntityType(field, attributeType); attributes.add( new MendixAttribute(field.getName(), attributeType, associationType, associationEntityType)); } MendixEntity newEntity = new MendixEntity(entityClass.getSimpleName(), attributes); mendixEntities.add(newEntity); } writeToJsonFile(filePath, mendixEntities); } ... }
我們首先尋找應用程式中標有 JPA 的 @Entity 註解的所有類別。
然後,對於每堂課,我們:
- 使用entityClass.getDeclaredFields()取得聲明的欄位。
- 循環該類別的每個欄位。
對於每個字段,我們:
-
確定屬性的類型:
private static final Map<Class<?>, AttributeType> JAVA_TO_MENDIX_TYPE = Map.ofEntries( Map.entry(String.class, AttributeType.STRING), Map.entry(Integer.class, AttributeType.INTEGER), ... ); // we return AttributeType.ENTITY if we cannot map to anything else
登入後複製登入後複製本質上,我們只是透過在 JAVA_TO_MENDIX_TYPE 映射中尋找 java 類型與我們的自訂枚舉值進行匹配。
-
接下來,我們檢查這個屬性是否實際上是一個關聯(指向另一個@Entity)。如果是這樣,我們確定關聯的類型:一對一、一對多、多對多:
@Entity @Table(name = "CAT") class Cat { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private int age; private String color; @OneToOne private Human humanPuppet; ... constructor ... ... getters ... } @Entity @Table(name = "HUMAN") public class Human { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; ... constructor ... ... getters ... }
登入後複製登入後複製登入後複製為此,我們只需檢查先前映射的屬性類型。如果它是 Entity,這僅意味著在先前的步驟中我們無法將其對應到任何原始 java 類型、String 或 Enum。
然後我們還需要決定它是什麼類型的關聯。檢查很簡單:如果是 List 類型,則它是一對多,否則是一對一(尚未實現「多對多」)。 然後我們為找到的每個欄位建立一個 MendixAttribute 物件。
完成後,我們只需為實體建立一個 MendixEntity 物件並指派屬性清單。
MendixEntity 和 MendixAttribute 是我們稍後將用來對應 json 的類別:
public class MendixExporter { public static void exportEntitiesTo(String filePath) throws IOException { AnnotatedTypeScanner typeScanner = new AnnotatedTypeScanner(false, Entity.class); Set<Class<?>> entityClasses = typeScanner.findTypes(JavaToMendixApplication.class.getPackageName()); log.info("Entity classes are: {}", entityClasses); List<MendixEntity> mendixEntities = new ArrayList<>(); for (Class<?> entityClass : entityClasses) { List<MendixAttribute> attributes = new ArrayList<>(); for (Field field : entityClass.getDeclaredFields()) { AttributeType attributeType = determineAttributeType(field); AssociationType associationType = determineAssociationType(field, attributeType); String associationEntityType = determineAssociationEntityType(field, attributeType); attributes.add( new MendixAttribute(field.getName(), attributeType, associationType, associationEntityType)); } MendixEntity newEntity = new MendixEntity(entityClass.getSimpleName(), attributes); mendixEntities.add(newEntity); } writeToJsonFile(filePath, mendixEntities); } ... }
最後,我們儲存一個List
將實體匯入 Mendix
有趣的部分來了,我們如何讀取上面產生的 json 檔案並從中建立 mendix 實體?
Mendix 的 Platform SDK 有一個 Typescript API 可以與之互動。
首先,我們將建立物件來表示我們的實體和屬性,以及關聯和屬性類型的列舉:
private static final Map<Class<?>, AttributeType> JAVA_TO_MENDIX_TYPE = Map.ofEntries( Map.entry(String.class, AttributeType.STRING), Map.entry(Integer.class, AttributeType.INTEGER), ... ); // we return AttributeType.ENTITY if we cannot map to anything else
接下來,我們需要使用 appId 來取得我們的應用程序,建立臨時工作副本,打開模型,並找到我們感興趣的領域模型:
private static AssociationType determineAssociationType(Field field, AttributeType attributeType) { if (!attributeType.equals(AttributeType.ENTITY)) return null; if (field.getType().equals(List.class)) { return AssociationType.ONE_TO_MANY; } else { return AssociationType.ONE_TO_ONE; } }
SDK 實際上會從 git 中提取我們的 mendix 應用程式並進行處理。
讀取 json 檔案後,我們將循環實體:
public record MendixEntity( String name, List<MendixAttribute> attributes) { } public record MendixAttribute( String name, AttributeType type, AssociationType associationType, String entityType) { public enum AttributeType { STRING, INTEGER, DECIMAL, AUTO_NUMBER, BOOLEAN, ENUM, ENTITY; } public enum AssociationType { ONE_TO_ONE, ONE_TO_MANY } }
這裡我們使用domainmodels.Entity.createIn(domainModel);在我們的域模型中建立一個新實體並為其分配一個名稱。我們可以指派更多屬性,例如文件、索引,甚至實體在領域模型中呈現的位置。
我們在單獨的函數中處理屬性:
interface ImportedEntity { name: string; generalization: string; attributes: ImportedAttribute[]; } interface ImportedAttribute { name: string; type: ImportedAttributeType; entityType: string; associationType: ImportedAssociationType; } enum ImportedAssociationType { ONE_TO_ONE = "ONE_TO_ONE", ONE_TO_MANY = "ONE_TO_MANY" } enum ImportedAttributeType { INTEGER = "INTEGER", STRING = "STRING", DECIMAL = "DECIMAL", AUTO_NUMBER = "AUTO_NUMBER", BOOLEAN = "BOOLEAN", ENUM = "ENUM", ENTITY = "ENTITY" }
這裡我們唯一需要付出一些努力的就是將屬性類型對應到有效的 mendix 類型。
接下來我們處理關聯。首先,由於在我們的Java實體中關聯是透過欄位宣告的,因此我們需要區分哪些欄位是簡單屬性,哪些欄位是關聯。為此,我們只需要檢查它是實體類型還是原始類型:
const client = new MendixPlatformClient(); const app = await client.getApp(appId); const workingCopy = await app.createTemporaryWorkingCopy("main"); const model = await workingCopy.openModel(); const domainModelInterface = model.allDomainModels().filter(dm => dm.containerAsModule.name === MyFirstModule")[0]; const domainModel = await domainModelInterface.load();
讓我們建立關聯:
function createMendixEntities(domainModel: domainmodels.DomainModel, entitiesInJson: any) { const importedEntities: ImportedEntity[] = JSON.parse(entitiesInJson); importedEntities.forEach((importedEntity, i) => { const mendixEntity = domainmodels.Entity.createIn(domainModel); mendixEntity.name = importedEntity.name; processAttributes(importedEntity, mendixEntity); }); importedEntities.forEach(importedEntity => { const mendixParentEntity = domainModel.entities.find(e => e.name === importedEntity.name) as domainmodels.Entity; processAssociations(importedEntity, domainModel, mendixParentEntity); }); }
除了名稱之外,我們還有 4 個重要的屬性需要設定:
- 父實體。這是目前實體。
-
子實體。在最後一步中,我們為每個 java 實體建立了 mendix 實體。現在我們只需要根據實體中java欄位的類型找到符合的實體:
function processAttributes(importedEntity: ImportedEntity, mendixEntity: domainmodels.Entity) { importedEntity.attributes.filter(a => a.type !== ImportedAttributeType.ENTITY).forEach(a => { const mendixAttribute = domainmodels.Attribute.createIn(mendixEntity); mendixAttribute.name = capitalize(getAttributeName(a.name, importedEntity)); mendixAttribute.type = assignAttributeType(a.type, mendixAttribute); }); }
登入後複製 關聯型別。如果是一對一的,它會對應到一個引用。如果是一對多,則對應到參考集。我們現在將跳過多對多。
協會所有者。一對一和多對多重關聯都具有相同的所有者類型:兩者。對於一對一,所有者類型必須為預設。
Mendix Platform SDK 將在我們的 mendix 應用程式的本機工作副本中建立實體。現在我們只需要告訴它提交更改:
@Entity @Table(name = "CAT") class Cat { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private int age; private String color; @OneToOne private Human humanPuppet; ... constructor ... ... getters ... } @Entity @Table(name = "HUMAN") public class Human { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; ... constructor ... ... getters ... }
幾秒鐘後,您可以在 Mendix Studio Pro 中開啟應用程式並驗證結果:
現在你已經看到了:貓和人的實體,它們之間存在一對一的關聯。
如果您想親自嘗試或查看完整程式碼,請造訪此儲存庫。
對未來的想法
- 在這個範例中,我使用了 Java/Spring 應用程式進行轉換,因為我最精通它,但任何應用程式都可以使用。 只需能夠讀取類型資料(靜態或運行時)來提取類別和欄位名稱就足夠了。
- 我很好奇嘗試讀取一些 Java 邏輯並將其匯出到 Mendix 微流程。我們可能無法真正轉換業務邏輯本身,但我們應該能夠獲得它的結構(至少是業務方法簽名?)。
- 本文中的程式碼可以推廣並製作成一個函式庫:json 格式可以保持不變,並且可以有一個函式庫用於匯出 java 類型,另一個函式庫用於匯入 mendix 實體。
- 我們可以使用相同的方法進行相反的操作:將 mendix 轉換為另一種語言。
結論
Mendix Platform SDK 是一項強大的功能,允許以程式設計方式與 mendix 應用程式互動。他們列出了一些範例用例,例如導入/導出程式碼、分析應用程式複雜性。
如果您有興趣,請看一下。
對於本文,您可以在此處找到完整程式碼。
以上是將 JPA 實體轉換為 Mendix的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱門文章

熱門文章

熱門文章標籤

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?

2025年的前4個JavaScript框架:React,Angular,Vue,Svelte

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?
