首页 > Java > java教程 > 将 JPA 实体转换为 Mendix

将 JPA 实体转换为 Mendix

Linda Hamilton
发布: 2025-01-13 18:04:42
原创
312 人浏览过

最近在探索 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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板