元数据解析器 MetaDataParse
定义元数据解析接口,获取资源表元数据实体。依据自身业务创建 DefaultMetaDataParse 实现 MetaDataParse 接口。
public interface MetaDataParse {
* 获取所有表元数据
* @return List<Table>
List<Table> tables();
* 获取指定表元数据
* @return List<Table>
List<Table> tables(List<String> tableCodes);
* 获取指定表元数据
* @param tableCode 表名
* @return Table
Table table(String tableCode);
* 设置解析器数据源
* @param dataSource 数据源
void setDataSource(DataSource dataSource);
复制代码
元数据缓存管理 MetaDataCacheManager
定义元数据缓存管理接口。依据自身业务创建 DefaultMetaDataCacheManager 实现 MetaDataCacheManager 接口。
public interface MetaDataCacheManager {
* 资源表数据加入缓存
* @param table 资源表数据
void put(Table table);
* 删除资源表缓存
* @param code 资源表编码
void remove(String code);
* 缓存中获取资源表数据
* @param code 资源表编码
* @return 资源表数据
Table get(String code);
* 清空全部缓存
void clear();
* 设置 configuration
* @param configuration mybatis配置
void setConfiguration(CustomConfiguration configuration);
复制代码
元数据构造器 MetaStatementBuilder
创建 MetaStatementBuilder 实现对元数据对象的构建及缓存,使用 CustomConfiguration、MetaDataParse、MetaDataCacheManager 创建 元数据构造器对象。如果未传入MetaDataParse和MetaDataCacheManager时,使用默认的 DefaultMetaDataParse 和 DefaultMetaDataCacheManager。
public class MetaStatementBuilder {
private static final Log LOGGER = LogFactory.getLog(MetaStatementBuilder.class);
protected final CustomConfiguration configuration;
protected final MetaDataParse parse;
private final MetaDataCacheManager cacheManager;
public static MetaStatementBuilder getInstance(CustomConfiguration configuration, MetaDataParse parse, MetaDataCacheManager cacheManager) {
return new MetaStatementBuilder(configuration,
Optional.ofNullable(parse).orElse(new DefaultMetaDataParse()),
Optional.ofNullable(cacheManager).orElse(new DefaultMetaDataCacheManager())
public MetaStatementBuilder(CustomConfiguration configuration, MetaDataParse parse, MetaDataCacheManager cacheManager) {
this.configuration = configuration;
this.parse = parse;
this.cacheManager = cacheManager;
this.parse.setDataSource(configuration.getEnvironment().getDataSource());
this.cacheManager.setConfiguration(configuration);
* 重新获取 table 元数据
* @param tableCode 表名
* @return Table
public Table builderTable(String tableCode) {
cacheManager.removeTable(tableCode);
return table(tableCode);
* 获取 table 元数据
* @param tableCode 表名
* @return Table
public Table table(String tableCode) {
Optional.ofNullable(tableCode).orElseThrow(() -> new CacheException("Meta tableCode is null."));
Table table = cacheManager.getTable(tableCode);
if (table == null) {
LOGGER.debug("Meta table[" + tableCode + "] cache init.");
table = parse.table(tableCode);
Optional.ofNullable(table).orElseThrow(() -> new CacheException("Meta table[" + tableCode + "] parse fail."));
cacheManager.put(table);
keyGen(table);
return table;
* 生成主键策略
* @param table 资源表表元数据
* @return org.apache.ibatis.executor.keygen.KeyGenerator
private KeyGenerator keyGen(Table table) {
public CustomConfiguration getConfiguration() {
return configuration;
public MetaDataCacheManager getCacheManager() {
return cacheManager;
public MetaDataParse getParse() {
return parse;
复制代码
修改 SqlSessionFactoryBean
在 SqlSessionFactoryBean 中加入元数据解析器、元数据缓存属性。方便配置自定义的元数据解析器和元数据缓存实现。
* 元数据解析器
private
MetaDataParse metaDataParse;
* 元数据缓存
private
MetaDataCacheManager cacheManager;
复制代码
在 buildSqlSessionFactory 方法中使用 metaDataParse 和 cacheManager 创建元数据构造器 MetaStatementBuilder,并将 MetaStatementBuilder 设置到 CustomConfiguration 中。
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
······
if (targetConfiguration instanceof CustomConfiguration) {
CustomConfiguration customConfiguration = (CustomConfiguration) targetConfiguration;
customConfiguration.setMetaStatementBuilder(MetaStatementBuilder.getInstance(customConfiguration, metaDataParse, cacheManager)
.builder());
······
复制代码
MetaSqlNode 使用 MetaStatementBuilder 生成语句
MetaSqlNode 继承了 MyBatis 的 SqlNode 接口。添加自定义语句不是通过 DynamicContext.appendSql() ,
而是拼装动态 XML 脚本,然后通过 CustomXMLScriptBuilder 将 XML转换为SqlNode,最后调用转换获取的 SqlNode.apply(DynamicContext) 将sql添加到 DynamicContext
。
下面以修改逻辑举例
public class MetaSqlNode implements SqlNode {
······
@Override
public boolean apply(DynamicContext context) {
if (!StringUtils.isEmpty(test) && !evaluator
.evaluateBoolean
(test, context.getBindings())) {
return false;
table
= configuration
.getMetaStatementBuilder
()
.table
(table);
SqlNode sqlNode = new
TextSqlNode
("");
if (type == TypeEnum.update) {
sqlNode =
updateScript
(table);
return sqlNode
.apply
(context);
* 动态更新语句节点,需指定ID
* @param table Table对象
* @param hasWhere 是否包含条件
* @return org.apache.ibatis.scripting.xmltags.SqlNode
private SqlNode
updateScript
(Table table, boolean hasWhere) {
Id id =
table
.getId
();
if (id == null || id.getCode() == null) {
throw new
RuntimeException
(String.format("TableCode[%s] id not find!", table.getCode()));
List<Column>
columns
=
table
.getColumnList
()
.stream
()
.filter
(p -> !table.getId()
.getCode
()
.equalsIgnoreCase
(p.getCode()))
.collect
(Collectors.toList());
StringBuilder sqlScript = new
StringBuilder
("<script> UPDATE ")
.append
(table.getCode());
sqlScript
.append
(" <trim prefix=\"set\" suffixOverrides=\",\"> ");
columns
.forEach
(p -> {
sqlScript.append("<if test=\"")
.append
(String.format(MAP_PARAMETER, p.getCode()))
.append
("!=null\">")
.append
(p.getCode())
.append
("=#{")
.append
(String.format(MAP_PARAMETER, p.getCode()))
.append
("},</if>");
sqlScript
.append
("</trim>");
sqlScript
.append
("WHERE ");
sqlScript
.append
(id.getCode())
.append
("=#{")
.append
(String.format(MAP_PARAMETER, id.getCode()))
.append
("}");
sqlScript
.append
("</script>");
return
scriptToSqlNode
(sqlScript);
* 动态sql语句转SqlNode节点
* @param sqlScript 动态sql语句
* @return org.apache.ibatis.scripting.xmltags.SqlNode
private SqlNode
scriptToSqlNode
(StringBuilder sqlScript) {
XPathParser parser = new
XPathParser
(sqlScript.toString(), false, configuration
.getVariables
(), new
CustomXMLMapperEntityResolver
());
CustomXMLScriptBuilder builder = new
CustomXMLScriptBuilder
(configuration, parser.evalNode("/script"), null);
return builder
.parseDynamicTags
();
······
复制代码
实际使用
Spring 按如下配置自定义元数据解析器与元数据缓存管理实现,
<bean id="sqlSessionFactory" class="com.my.ibatis.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configurationProperties" >
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations" value="classpath*:mybatis.properties"/>
</bean>
</property>
<property name="metaDataParse">
<bean class=" Class extends MetaDataParse "/>
</property>
<property name="metaDataParse">
<bean class=" Class extends MetaDataCacheManager "/>
</property>
</bean>
复制代码
元数据解析器 MetaDataParse 可以通过查询数据库中存储的表结构数据来构建 metadata,也可以扫描实体类的注解来构建 metadata。缓存管理 MetaDataCacheManager 可以使用简单的 ConcurrentHashMap 来实现,也可以是 Ehcache等缓存框架实现。
20小时前