app开发者平台在数字化时代的重要性与发展趋势解析
1193
2023-05-09
MyBatis 添加元数据自定义元素标签的实现代码
开发背景
现有系统中维护了一套业务表相关列、键的元数据,希望通过读取元数据实现自动封装 SQL 语句、自定义主键策略。实现方案为入侵式修改 MyBatis,增加元素标签meta,支持业务开发中可以在XML映射文件中使用。
meta元素设计如下:
<!ELEMENT meta EMPTY>
<!ATTLIST meta
test CDATA #IMPLIED
type (update|insert|select|columns|pk-col|load|load-columns) #IMPLIED
ignore CDATA #IMPLIED
table CDATA #IMPLIED
func CDATA #IMPLIED
alias CDATA #IMPLIED
>
期望示例如下:
select
from USER
where = #{__PK_VALUE}
开发准备
新建项目并引入 mybatis 、 mybatis-spring 两个核心依赖。
增加自定义元素
创建 MetaHandler 和 MetaSqlNode
public class MetaHandler implements NodeHandler {
private final CustomConfiguration configuration;
protected MetaHandler(CustomConfiguration configuration) {
this.configuration = configuration;
}
@Override
public void handleNode(XNode nodeToHandle, List
final String test = nodeToHandle.getStringAttribute("test");
final String type = nodeToHandle.getStringAttribute("type");
final String ignore = nodeToHandle.getStringAttribute("ignore");
final String table = nodeToHandle.getStringAttribute("table"http://);
final String func = nodeToHandle.getStringAttribute("func");
String alias = nodeToHandle.getStringAttribute("alias");
if (!StringUtils.isEmpty(alias)) {
alias = alias.trim();
//是否无效 防止注入
boolean invalid = alias.contains(" ") || alias.contains(".");
if (invalid) {
throw new RuntimeException("alias is invalid : " + alias);
}
}
MetaSqlNode metaSqlNode = new MetaSqlNode(configuration, test, type, ignore, table, func, alias);
targetContents.add(metaSqlNode);
}
}
public class MetaSqlNode implements SqlNode {
/**
* mybatis核心数据
*/
private final CustomConfiguration configuration;
/**
* 判断语句校验器
*/
private final ExpressionEvaluator evaluator;
/**
* 判断语句,同if标签
*/
private final String test;
/**
* 生成语句类型 update|insert|select|columns|pk-col|load|load-columns
*/
private final TypeEnum type;
/**
* 忽略的列
*/
private final String ignore;
/**
* 表名,未指定则从调用参数中获取
*/
private final String table;
/**
* 功能,未指定则从调用参数中获取
*/
private final String func;
/**
* 动态列别名
*/
private final String alias;
public MetaSqlNode(CustomConfiguration configuration, String test, String type, String ignore, String table, String func, String alias) {
this.evaluator = new ExpressionEvaluator();
this.configuration = configuration;
this.test = test;
this.type = TypeEnum.parse(type);
this.ignore = ignore;
this.table = table;
this.func = func;
this.alias = alias;
}
@Override
public boolean apply(DynamicContext context) {
// TODO 解析type与table,向context中添加语句
context.appendSql(" insert ");
}
}
创建 CustomXMLScriptBuilder
内容复制自org.apache.ibatis.scripting.xmltags.XMLScriptBuilder,在 initNodeHandlerMap 方法中添加 MetaHandler。
private void initNodeHandlerMap() {
nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("where", new WhereHandler());
nodeHandlerMap.put("set", new SetHandler());
nodeHandlerMap.put("foreach", new ForEachHandler());
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("choose", new ChooseHandler());
nodeHandlerMap.put("when", new IfHandler());
nodeHandlerMap.put("otherwise", new OtherwiseHandler());
nodeHandlerMap.put("bind", new BindHandler());
//增加元数据标签解析器
if (configuration instanceof CustomConfiguration) {
nodeHandlerMap.put("meta", new MetaHandler((CustomConfiguration) configuration));
}
}
创建 CustomXMLLanguageDriver
内容复制自org.apache.ibatis.scripting.xmltags.XMLLanguageDriver,在 createSqlSource 方法中使用 CustomXMLScriptBuilder 来解析Xml生成 SqlSource。
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType) {
CustomXMLScriptBuilder builder = new CustomXMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
创建 CustomConfiguration
继承org.apache.ibatis.session.Configuration,内容复制自 Configuration。将构造方法中的 XMLLanguageDriver 修改为 CustomConfiguration。
public CustomConfiguration() {
//默认使用自定义 LanguageDriver
typeAliasRegistry.registerAlias("XML", CustomXMLLanguageDriver.class);
//默认使用自定义 LanguageDriver
languageRegistry.setDefaultDriverClass(CustomXMLLanguageDriver.class);
}
创建 CustomXMLConfigBuilder
内容复制自org.apache.ibatis.builder.xml.XMLConfigBuilder,支持通过 XML 配置来创建 CustomConfiguration。
public class CustomXMLConfigBuilder extends BaseBuilder {
private CustomXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 使用 CustomConfiguration
super(new CustomConfiguration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
}
创建 SqlSessionFactory
复制自org.mybatis.spring.SqlSessionFactoryBean,将 buildSqlSessionFactory 方法中的 Configuration 替换为 CustomConfiguration。
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
CustomXMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else http://if (this.configLocation != null) {
// 使用 CustomXMLConfigBuilder 创建 CustomConfiguration
xmlConfigBuilder = new CustomXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(
() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
// 使用 CustomConfiguration
targetConfiguration = new CustomConfiguration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
修改DTD约束
MyBatis 的约束文件并不支持自定义的 meta 元素,需要使用 CDATA 来处理。示例如下:
<![CDATA[[
]]>
如果不想要 CDATA,则需要通过修改DTD约束来完成。
在 classes 下指定位置添加DTD约束文件org/apache/ibatis/builder/xml/mybatis-3-config.dtd达到覆盖 MyBatis DTD的效果。
重写代码来使用指定 DTD 。
创建 CustomXMLMapperEntityResolver
复制自org.apache.ibatis.builder.xml.XMLMapperEntityResolver,将MYBATIS_MAPPER_DTD修改为指向本地 mybatis-3-mapper.dtd 文件,并在DTD文件中添加 meta 元素的约束。
public class CustomXMLMapperEntityResolver implements EntityResolver {
private static final String MYBATIS_MAPPER_DTD = "com/my/ibatis/builder/xml/mybatis-3-mapper.dtd";
}
<!ELEMENT meta EMPTY>
<!ATTLIST meta
test CDATA #IMPLIED
type (update|insert|select|columns|pk-col|load|load-columns) #IMPLIED
ignore CDATA #IMPLIED
table CDATA #IMPLIED
func CDATA #IMPLIED
alias CDATA #IMPLIED
>
CustomXMLLanguageDriver
Mapper动态语句注解处理使用 CustomXMLMapperEntityResolver。
/**
* Mapper动态语句注解调用
*
* ""
*
* @param configuration mybatis配置
* @param script 动态语句字符串
* @param parameterType 参数类型
* @return org.apache.ibatis.mapping.SqlSource
*/
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType) {
// issue #3
if (script.startsWith("
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~