首頁 >Java >java教程 >將 JPA 實體轉換為 Mendix

將 JPA 實體轉換為 Mendix

Linda Hamilton
Linda Hamilton原創
2025-01-13 18:04:42319瀏覽

最近在探索 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 中的實體元數據

這可以用幾種不同的方式來完成:

  1. 透過靜態分析套件中的實體。
  2. 透過使用反射在運行時讀取這些實體。

我選擇了選項 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 註解的所有類別。
然後,對於每堂課,我們:

  1. 使用entityClass.getDeclaredFields()取得聲明的欄位。
  2. 循環該類別的每個欄位。

對於每個字段,我們:

  1. 確定屬性的類型:

    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 類型與我們的自訂枚舉值進行匹配。

  2. 接下來,我們檢查這個屬性是否實際上是一個關聯(指向另一個@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 類型,則它是一對多,否則是一對一(尚未實現「多對多」)。

  3. 然後我們為找到的每個欄位建立一個 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;使用 Jackson 轉換為 json 檔案。

將實體匯入 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 個重要的屬性需要設定:

  1. 父實體。這是目前實體。
  2. 子實體。在最後一步中,我們為每個 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);
        });
    }
    
  3. 關聯型別。如果是一對一的,它會對應到一個引用。如果是一對多,則對應到參考集。我們現在將跳過多對多。

  4. 協會所有者。一對一和多對多重關聯都具有相同的所有者類型:兩者。對於一對一,所有者類型必須為預設。

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 中開啟應用程式並驗證結果:
Converting JPA entities to Mendix

現在你已經看到了:貓和人的實體,它們之間存在一對一的關聯。

如果您想親自嘗試或查看完整程式碼,請造訪此儲存庫。

對未來的想法

  1. 在這個範例中,我使用了 Java/Spring 應用程式進行轉換,因為我最精通它,但任何應用程式都可以使用。 只需能夠讀取類型資料(靜態或運行時)來提取類別和欄位名稱就足夠了。
  2. 我很好奇嘗試讀取一些 Java 邏輯並將其匯出到 Mendix 微流程。我們可能無法真正轉換業務邏輯本身,但我們應該能夠獲得它的結構(至少是業務方法簽名?)。
  3. 本文中的程式碼可以推廣並製作成一個函式庫:json 格式可以保持不變,並且可以有一個函式庫用於匯出 java 類型,另一個函式庫用於匯入 mendix 實體。
  4. 我們可以使用相同的方法進行相反的操作:將 mendix 轉換為另一種語言。

結論

Mendix Platform SDK 是一項強大的功能,允許以程式設計方式與 mendix 應用程式互動。他們列出了一些範例用例,例如導入/導出程式碼、分析應用程式複雜性。
如果您有興趣,請看一下。
對於本文,您可以在此處找到完整程式碼。

以上是將 JPA 實體轉換為 Mendix的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn