From 6013dc9db28b3dbcfd8175cddd6126ff75e7d1bb Mon Sep 17 00:00:00 2001 From: canonical Date: Sat, 14 Sep 2024 18:30:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84EntityDefDirTransformer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/compare/nop-vfs-ofbiz.md | 7 +- docs/dev-guide/codegen.md | 26 +++- docs/dev-guide/orm/orm-basic.md | 7 + .../transform/EntityDefDirTransformer.java | 82 +++++------ ...delToOrmModel.java => EntityDefModel.java} | 138 ++++++++++++++---- ....java => TestEntityDefDirTransformer.java} | 4 +- 6 files changed, 188 insertions(+), 76 deletions(-) rename nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/{EntityDefModelToOrmModel.java => EntityDefModel.java} (53%) rename nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/{TestEntityDefModelToOrmModel.java => TestEntityDefDirTransformer.java} (83%) diff --git a/docs/compare/nop-vfs-ofbiz.md b/docs/compare/nop-vfs-ofbiz.md index c742e3308..a1dbc141f 100644 --- a/docs/compare/nop-vfs-ofbiz.md +++ b/docs/compare/nop-vfs-ofbiz.md @@ -56,7 +56,12 @@ OFBiz使用的xsd语法非常臃肿,而且引入了完全不必要的顺序依 ## 服务引擎(Service Engine) -与程序语言无关 +与程序语言无关。输入是Map,输出也是Map + +You can also override a service by using the same name down in the deployment context (which is first framework, then themes, then applications, then specialpurpose, then hot-deploy) + +ECA (Event Condition Action) is much like a trigger. When a service is called, a lookup is performed to see if any ECAs are defined for this event. + ## 组件系统(Widget System) diff --git a/docs/dev-guide/codegen.md b/docs/dev-guide/codegen.md index 0cf55a7e7..d0226ad1e 100644 --- a/docs/dev-guide/codegen.md +++ b/docs/dev-guide/codegen.md @@ -36,7 +36,7 @@ public class NopOrmCodeGen { public static void main(String[] args) { AppConfig.getConfigProvider().updateConfigValue(CoreConfigs.CFG_CORE_MAX_INITIALIZE_LEVEL, CoreConstants.INITIALIZER_PRIORITY_ANALYZE); - + CoreInitialization.initialize(); try { File projectDir = MavenDirHelper.projectDir(NopOrmCodeGen.class); @@ -187,6 +187,30 @@ if(globalVar){ 2. `{data.@mapper}` 属性表达式存在一种特殊的约定。对于HashSet或者LinkedHashSet类型,`@mapper`属性会判断集合中是否存在该文本值。 相当于 `((Set)data).contains('mapper')` +### xgen模板文件 +xgen本质上就是xpl模板语言,它动态执行输出内容。xpl模板语言中可以通过``,``等标签执行逻辑,并通过``来导入标签库。 +xpl还提供了``这种标签用于原样输出它的body内容,即使其中包含`c:script`等标签。 + +例如 +```xml + + + let n = 100; // 设置一个变量,可以执行复杂的XScript代码 + + + + +
+ + +``` +上面的代码会输出100个`
`节点。 + +`c:script`就是编译成一个Expression,然后`expr.invoke(scope)`, 在当前的scope中执行。scope中的数据就是 `@init.xrun`中准备的。在`@init.xurn`中`assign("x",1")`可以向scope中设置变量。另外`builder.defineGlobalVar('basePackagePath', pkgPath)`等也会设置变量。 + +xpl模板语言的介绍参见[xpl.md](xlang/xpl.md) + + ## 二. 差量化的代码生成器 如果我们不把代码生成器看作是某种一次性的、临时使用的外围工具,而是把它作为元编程的一个有机组成部分,则代码生成器必然是支持增量生成的。所谓增量生成,是指代码生成器允许反复执行,且同时允许手工修改输出产物,自动生成和手工修改的部分都可以看作是对初次生成结果的增量化修改,并且它们会自动合并在一起。 diff --git a/docs/dev-guide/orm/orm-basic.md b/docs/dev-guide/orm/orm-basic.md index bcd4242bc..3d31ade65 100644 --- a/docs/dev-guide/orm/orm-basic.md +++ b/docs/dev-guide/orm/orm-basic.md @@ -269,6 +269,13 @@ transactionTemplate.runInTransaction(txn->{ }) ``` +## OrmEntity实体属性 +NopORM中的所有实体类都从OrmEntity类继承。OrmEntity不支持JSON序列化,但是提供了帮助函数可以获取实体上的字段值。 + +实体上当前的字段值是 `entity.orm_initedValues()`, 修改前的值是 `orm_dirtyOldValues()`。 + + + ## 与MyBatis的区别 NopORM是一个类似JPA的完整的ORM引擎,因此它使用OrmSession来管理所有加载到内存中的实体,整体使用类似于JPA和Hibernate,相比于MyBatis要少很多手工调用步骤。 diff --git a/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefDirTransformer.java b/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefDirTransformer.java index 4d25bed0f..79e564676 100644 --- a/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefDirTransformer.java +++ b/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefDirTransformer.java @@ -4,72 +4,66 @@ import io.nop.core.lang.xml.XNode; import io.nop.core.lang.xml.parse.XNodeParser; import io.nop.core.resource.impl.FileResource; +import io.nop.ofbiz.migration.OfbizMigrationConstants; +import io.nop.orm.model.OrmModel; import io.nop.xlang.xdsl.DslModelHelper; import java.io.File; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Set; public class EntityDefDirTransformer { - private final Map ormNodes = new HashMap<>(); - private final Map entityNodes = new HashMap<>(); - private final Map> unknownRelations = new HashMap<>(); + private final OrmModel baseModel = (OrmModel) DslModelHelper.loadDslModelFromPath(OfbizMigrationConstants.PATH_OFBIZ_BASE_ORM); - // 记录哪些ORM模型增加了哪些外部实体定义 - private final Map> externalEntityMap = new HashMap<>(); + private final Map entityDefModels = new HashMap<>(); + + private final Map entityMap = new HashMap<>(); public void transformDir(File dir, File targetDir) { + loadFromDir(dir); - // 先加载所有的entitydef模型,记录未识别的relation实体引用 - for (File file : dir.listFiles()) { - XNode node = XNodeParser.instance().parseFromResource(new FileResource(file)); - EntityDefModelToOrmModel trans = new EntityDefModelToOrmModel(); - XNode ormNode = trans.transform(node); - ormNodes.put(StringHelper.fileNameNoExt(file.getName()), ormNode); + entityDefModels.values().forEach(defModel -> { + defModel.collectEntities(entityMap); + }); - trans.mergeTo(entityNodes, unknownRelations); - } + resolveRelations(); - resolveUnknownRelations(); + entityDefModels.values().forEach(EntityDefModel::addExternalEntities); saveOrmNodes(targetDir); } - void resolveUnknownRelations() { - unknownRelations.forEach((name, list) -> { - XNode refEntityNode = entityNodes.get(name); - if (refEntityNode != null) { - String fullName = refEntityNode.attrText("name"); - // 将relation的refEntityName设置为全名 - for (XNode refNode : list) { - refNode.setAttr("refEntityName", fullName); - - // relation -> relations -> entity - XNode ormNode = refNode.root(); - Set externals = externalEntityMap.computeIfAbsent(ormNode, k -> new HashSet<>()); - if (externals.add(fullName)) { - // 缺少external定义 - XNode entities = ormNode.makeChild("entities"); - XNode externalNode = refEntityNode.cloneInstance(); - externalNode.setAttr("notGenCode", true); - entities.appendChild(externalNode); - } - } - } else { - throw new IllegalArgumentException("nop.err.ofbiz.unresolved-relation-entity-name:" + name + "," + list.get(0)); + public void loadFromDir(File dir) { + // 先加载所有的entitydef模型,记录未识别的relation实体引用 + for (File file : dir.listFiles()) { + String name = file.getName(); + if (!name.endsWith("-entitymodel.xml")) { + continue; } + String moduleName = StringHelper.removeTail(name, "-entitymodel.xml"); + + XNode node = XNodeParser.instance().parseFromResource(new FileResource(file)); + EntityDefModel defModel = new EntityDefModel(node, baseModel); + entityDefModels.put(moduleName, defModel); + } + } + + void resolveRelations() { + this.entityDefModels.values().forEach(defModel -> { + defModel.resolveRelations(refEntityName -> { + XNode node = entityMap.get(refEntityName); + if (node == null) + throw new IllegalArgumentException("nop.err.ofbiz.unresolved-relation-entity-name:" + refEntityName); + return node; + }); }); } void saveOrmNodes(File targetDir) { - ormNodes.forEach((name, node) -> { - File file = new File(targetDir, name + ".orm.xml"); - node.saveToResource(new FileResource(file), null); - - // DslModelHelper.loadDslModel(new FileResource(file)); + entityDefModels.forEach((name, node) -> { + File file = new File(targetDir, "ofbiz/" + name + "/app.orm.xml"); + node.getOrmNode().saveToResource(new FileResource(file), null); + DslModelHelper.loadDslModel(new FileResource(file)); }); } } diff --git a/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModelToOrmModel.java b/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModel.java similarity index 53% rename from nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModelToOrmModel.java rename to nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModel.java index 4f5945d99..da4c90ae6 100644 --- a/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModelToOrmModel.java +++ b/nop-ofbiz-migration/src/main/java/io/nop/ofbiz/migration/transform/EntityDefModel.java @@ -1,42 +1,67 @@ package io.nop.ofbiz.migration.transform; import io.nop.commons.util.StringHelper; +import io.nop.commons.util.objects.Pair; import io.nop.core.lang.xml.XNode; import io.nop.ofbiz.migration.OfbizMigrationConstants; import io.nop.orm.model.OrmDomainModel; import io.nop.orm.model.OrmModel; -import io.nop.xlang.xdsl.DslModelHelper; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Function; -public class EntityDefModelToOrmModel { - private final OrmModel baseModel = (OrmModel) DslModelHelper.loadDslModelFromPath(OfbizMigrationConstants.PATH_OFBIZ_BASE_ORM); +public class EntityDefModel { + private final OrmModel baseModel; + private final XNode node; + private final XNode ormNode; + private final Map> entityNodes = new HashMap<>(); + private final Map> viewEntityNodes = new HashMap<>(); + private final Map externalNodes = new HashMap<>(); - private final Map entityNodes = new HashMap<>(); - private final Map> unknownRelations = new HashMap<>(); + public EntityDefModel(XNode node, OrmModel baseModel) { + this.node = node; + this.baseModel = baseModel; + this.ormNode = transform(node); + } - public Map getEntityNodes() { - return entityNodes; + public XNode getDefNode() { + return node; } - public Map> getUnknownRelations() { - return unknownRelations; + public XNode getOrmNode() { + return ormNode; } - public void mergeTo(Map entityNodes, Map> unknownRelations) { - entityNodes.putAll(this.getEntityNodes()); - this.getUnknownRelations().forEach((name, list) -> { - unknownRelations.computeIfAbsent(name, k -> new ArrayList<>()).addAll(list); + public void collectEntities(Map map) { + entityNodes.forEach((name, pair) -> { + if (map.containsKey(name)) + throw new IllegalArgumentException("nop.err.ofbiz.duplicate-entity-name:name=" + name + "," + pair.getSecond()); + map.put(name, pair.getSecond()); + }); + + viewEntityNodes.forEach((name, pair) -> { + if (map.containsKey(name)) + throw new IllegalArgumentException("nop.err.ofbiz.duplicate-entity-name:name=" + name + "," + pair.getSecond()); + map.put(name, pair.getSecond()); }); } - public XNode transform(XNode node) { + public void addExternalEntities() { + // 缺少external定义 + XNode entities = ormNode.makeChild("entities"); + for (XNode node : this.externalNodes.values()) { + XNode externalNode = node.cloneInstance(); + externalNode.getChildren().removeIf(child -> !child.getTagName().equals("columns")); + externalNode.setAttr("notGenCode", true); + entities.appendChild(externalNode); + } + } + + private XNode transform(XNode node) { XNode ret = XNode.make("orm"); ret.setAttr("x:extends", OfbizMigrationConstants.PATH_OFBIZ_BASE_ORM); ret.setAttr("x:schema", "/nop/schema/orm/orm.xdef"); @@ -51,18 +76,20 @@ public XNode transform(XNode node) { entities.appendChild(entityNode); if (entityNodes.containsKey(entityName)) throw new IllegalArgumentException("nop.err.ofbiz.duplicate-entity-name:" + entityName); - entityNodes.put(entityName, entityNode); + entityNodes.put(entityName, Pair.of(child, entityNode)); } } for (XNode child : node.getChildren()) { - if (child.getTagName().equals("entity")) { + if (child.getTagName().equals("view-entity")) { + XNode entityNode = transformViewEntity(child); String entityName = child.attrText("entity-name"); - XNode entityNode = entityNodes.get(entityName); - transformRelation(entityNode, child); + entities.appendChild(entityNode); + if (entityNodes.containsKey(entityName)) + throw new IllegalArgumentException("nop.err.ofbiz.duplicate-entity-name:" + entityName); + viewEntityNodes.put(entityName, Pair.of(child, entityNode)); } } - return ret; } @@ -100,6 +127,42 @@ private XNode transformEntity(XNode node) { return ret; } + private XNode transformViewEntity(XNode node) { + XNode ret = XNode.make("entity"); + ret.setAttr("tableView", true); + ret.setAttr("noPrimaryKey", true); + ret.setAttr("readonly", true); + + String packageName = node.attrText("package-name"); + String entityName = node.attrText("entity-name"); + String name = StringHelper.fullClassName(entityName, packageName); + String tableName = node.attrText("table-name"); + if (StringHelper.isEmpty(tableName)) { + tableName = StringHelper.camelCaseToUnderscore(entityName, true); + } else { + tableName = tableName.toUpperCase(Locale.ROOT); + } + String title = node.attrText("title"); + ret.setAttr("name", name); + ret.setAttr("className", name); + ret.setAttr("tableName", tableName); + ret.setAttr("displayName", title); + + return ret; + } + + private void transformViewAlias(XNode viewEntityNode, XNode entityNode) { + Map aliasMap = new HashMap<>(); + + viewEntityNode.childrenByTag("member-entity").forEach(child -> { + String alias = child.attrText("entity-alias"); + String entityName = child.attrText("entity-name"); + aliasMap.put(alias, entityName); + }); + + + } + private XNode transformField(XNode node, Set pkNames) { String name = node.attrText("name"); String type = node.attrText("type"); @@ -139,7 +202,17 @@ private Set getPrimaryKeys(XNode node) { return ret; } - private void transformRelation(XNode ret, XNode node) { + public void resolveRelations(Function refEntityResolver) { + entityNodes.values().forEach(pair -> { + transformRelation(pair.getSecond(), pair.getFirst(), refEntityResolver); + }); + + viewEntityNodes.values().forEach(pair -> { + transformRelation(pair.getSecond(), pair.getFirst(), refEntityResolver); + }); + } + + private void transformRelation(XNode ret, XNode node, Function externalEntityResolver) { XNode relations = ret.addChild("relations"); for (XNode child : node.getChildren()) { if (child.getTagName().equals("relation")) { @@ -157,18 +230,13 @@ private void transformRelation(XNode ret, XNode node) { if (title.equals("class")) title = "className"; - XNode entityModel = entityNodes.get(refEntityName); - if (entityModel != null) { - refEntityName = entityModel.attrText("name"); - } else { - unknownRelations.computeIfAbsent(refEntityName, k -> new ArrayList<>()).add(child); - } + String fullName = resolveRefEntity(refEntityName, externalEntityResolver); String relType = type.equals("one") || type.equals("one-nopk") ? "to-one" : "to-many"; XNode relation = relations.addChild(relType); relation.setAttr("name", title); - relation.setAttr("refEntityName", refEntityName); + relation.setAttr("refEntityName", fullName); if (relType.equals("to-one")) relation.setAttr("constraint", fkName); addJoin(relation, child); @@ -176,6 +244,18 @@ private void transformRelation(XNode ret, XNode node) { } } + private String resolveRefEntity(String refEntityName, Function externalEntityResolver) { + Pair pair = entityNodes.get(refEntityName); + if (pair != null) + return pair.getSecond().attrText("name"); + pair = viewEntityNodes.get(refEntityName); + if (pair != null) + return pair.getSecond().attrText("name"); + XNode node = externalEntityResolver.apply(refEntityName); + externalNodes.put(refEntityName, node); + return node.attrText("name"); + } + private void addJoin(XNode relation, XNode bizRel) { XNode join = relation.addChild("join"); for (XNode child : bizRel.getChildren()) { diff --git a/nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefModelToOrmModel.java b/nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefDirTransformer.java similarity index 83% rename from nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefModelToOrmModel.java rename to nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefDirTransformer.java index 0cd6c6ace..d1afdd578 100644 --- a/nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefModelToOrmModel.java +++ b/nop-ofbiz-migration/src/test/java/io/nop/ofbiz/transform/TestEntityDefDirTransformer.java @@ -3,12 +3,14 @@ import io.nop.api.core.annotations.autotest.NopTestConfig; import io.nop.autotest.junit.JunitBaseTestCase; import io.nop.ofbiz.migration.transform.EntityDefDirTransformer; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.File; +@Disabled @NopTestConfig(localDb = true) -public class TestEntityDefModelToOrmModel extends JunitBaseTestCase { +public class TestEntityDefDirTransformer extends JunitBaseTestCase { @Test