其他
技术分享|Java SDK 动态类型
一、背景
随着天际平台的诞生,承接了整个公司的技术底座。建模也同样需要从只支持ERP的产品线,扩充到支撑公司SaaS类业务。为了更多产品,客户能用上建模,享受低代码能力带来的便捷,我们引入Java SDK作为低代码的后端开发语言。配合上原有的.Net Core的改造,完成整个建模跨平台,跨语言开发,和容器化部署。
二、常用方案
表结构变更
在调整表结构上,SQL 语言已经有 DDL 语法来专门进行处理,直接使用 DDL 语法对数据库发送命令,即可即时对数据库表结构进行调整。
SQL 变更
重新生成代码 | 预建字段 | 使用 EAV 模型 | |
---|---|---|---|
动态性 | 静态 | 静态 | 动态 |
性能 | 好 | 好 | 差 |
动态性:描述 SQL 变更是否对服务进行重启,静态即为需要,动态即为不需要。 性能:描述在表数据增删查改时相对于通常写法(数据库第 3 范式上的增删查改)的效率差别,好即为相同,差即为效率有下降,不可能出现性能提升情况。 总体上重新生成代码和预建字段都无法实现动态特性,使用 EAV 模型使数据库的表结构复杂化,在进行查询时会性能下降。
三、解决方案
表结构变更
SQL 变更
增删查改 SQL 构建方式
动态性支持
实现分析
四、ORM 动态化及 Java SDK 实现
(一)实现分析
映射关系定义的形式
/xml形式
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/ORM"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/ORM_2_1.xsd"
version="2.1">
...
<entity access="FIELD" class="...">
<attributes>
<basic access="FIELD" name="...">
<column name="..."/>
</basic>
...
</attributes>
</entity>
...
</entity-mappings>
//注解形式
@Entity
@Table(name = "...")
public class Entity {
@Id
@Column(name = "id")
private String id;
@Column(name = "name")
protected String name;
...
}
分析 |
运行时替换 EntityManagerFactory
/**
* 扩展 EMF 构建器实现
*/
public class ExtensionEntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuilder {
...
//实体对象接入
final List<Class> loadedAnnotatedClasses = (List<Class>) configurationValues.remove(AvailableSettings.LOADED_CLASSES);
if (loadedAnnotatedClasses != null) {
for (Class cls : loadedAnnotatedClasses) {
if (AttributeConverter.class.isAssignableFrom(cls)) {
if (attributeConverterDefinitions == null) {
attributeConverterDefinitions = new ArrayList<>();
}
attributeConverterDefinitions.add(AttributeConverterDefinition.from((Class<? extends AttributeConverter>) cls));
} else {
metadataSources.addAnnotatedClass(cls);
}
}
}
...
//xml 接入
final List<String> explicitORMXmlInputStream = (List<String>) configurationValues.remove(XML_FILE_CONTENT);
if (explicitORMXmlInputStream != null) {
explicitORMXmlInputStream.forEach(content -> {
try (InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) {
metadataSources.addInputStream(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
...
}
//对象结构
/**
* OneToMany 节点
*/
@Getter
@Setter
@JacksonXmlRootElement(localName = "one-to-many")
public class OneToMany extends RelationAttribute {
@JacksonXmlProperty(localName = "mapped-by", isAttribute = true)
private String mappedBy;
...
}
//对象转 xml
import com.fasterxml.jackson.datafORMat.xml.XmlMapper;
...
/**
* Xml工具类
*/
public class XmlUtil {
...
/**
* 序列化
*/
public static String write(Object o) throws JsonProcessingException {
return xmlMapper.writeValueAsString(o);
}
...
public static void main(String[] args) {
XmlUtil.write(xmlObject);
}
}
运行时实体对象结构
/**
* 创建 Javassist 类型
*/
public CtClass createClass(String className, String superClassName) {
...
//构建类型文件
ClassFile classFile = new ClassFile(false, className, superClassName);
...
//设置访问性
classFile.setAccessFlags(AccessFlag.PUBLIC);
...
//添加构造函数
ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
...
//返回
return classFile;
}
/**
* 补充字段信息
*/
public void createField(CtClass targetCc, CtClass fieldType, String fieldName) {
...
//创建字段
CtField field = new CtField(fieldType, fieldName, targetCc);
...
//设置访问性
field.setModifiers(Modifier.PRIVATE);
...
//绑定getter和setter方法
addGetterAndSetterMethodOfField(field);
...
//添加字段
targetCc.addField(field);
}
/**
* 编译到 Jvm
*/
public Class<?> toClass(CtClass targetCc) {
...
return targetCc.toClass();
}
public class Supplier {
private UUID supplierGUID;
private String name;
private UUID number;
private String phone;
}
{ "name":"zonas", "number":"00000000-0000-0000-0000-000000000000", "phone":"135***2601", "email":"gao**@mingyuanyun.com"}
六、局限及其优化思考
(一)局限
(二)优化思考
框架兼容负担
EntityManagerFactory 频繁构建性能开销
高同学: 研发工程师,目前负责建模平台相关工作。