MyBatis adds metadata custom element tags
Create metadata entities
Create metadata entity classes as needed to implement the Serializable interface for storing metadata and caching.
The metadata parser MetaDataParse
Define metadata parsing interfaces to get resource table metadata entities. Create DefaultMetaDataParse to implement MetaDataParse interface according to its own business.
public interface MetaDataParse {
/** * get all table metadata **@return List<Table>
*/
List<Table> tables(a);
/** * get the specified table metadata **@return List<Table>
*/
List<Table> tables(List<String> tableCodes);
/** * get the specified table metadata **@paramTableCode table name *@return Table
*/
Table table(String tableCode);
/** * Sets the parser data source **@paramDataSource */
void setDataSource(DataSource dataSource);
}
Copy the code
MetaDataCacheManager MetaDataCacheManager
Define the metadata cache management interface. Based on its own to create DefaultMetaDataCacheManager MetaDataCacheManager interface.
public interface MetaDataCacheManager {
/** * Add resource table data to cache **@paramTable Resource table data */
void put(Table table);
/** * Delete the resource table cache **@paramCode Resource table code */
void remove(String code);
/** * Get resource table data from cache **@paramCode Resource table code *@returnResource table data */
Table get(String code);
/**
* 清空全部缓存
*/
void clear(a);
/**
* 设置 configuration
*
* @paramConfiguration Mybatis Configuration */
void setConfiguration(CustomConfiguration configuration);
}
Copy the code
Metadata constructor MetaStatementBuilder
Create MetaStatementBuilder to achieve the construction and cache of metadata objects, using CustomConfiguration, MetaDataParse, MetaDataCacheManager to create metadata constructor objects. If it is not the incoming MetaDataParse and MetaDataCacheManager, use the default DefaultMetaDataParse and 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;
// Metadata parser
this.parse = parse;
// Metadata cache manager
this.cacheManager = cacheManager;
// Set the parser data source
this.parse.setDataSource(configuration.getEnvironment().getDataSource());
// Set the parser data source
this.cacheManager.setConfiguration(configuration);
}
/** * Retrieve the table metadata **@paramTableCode table name *@return Table
*/
public Table builderTable(String tableCode) {
cacheManager.removeTable(tableCode);
return table(tableCode);
}
/** * obtain table metadata **@paramTableCode table name *@return Table
*/
public Table table(String tableCode) {
// Check whether the value is null
Optional.ofNullable(tableCode).orElseThrow(() -> new CacheException("Meta tableCode is null."));
// From the cache
Table table = cacheManager.getTable(tableCode);
if (table == null) {
LOGGER.debug("Meta table[" + tableCode + "] cache init.");
// Query metadata
table = parse.table(tableCode);
Optional.ofNullable(table).orElseThrow(() -> new CacheException("Meta table[" + tableCode + "] parse fail."));
/ / put the cache
cacheManager.put(table);
// Generate a primary key policy
keyGen(table);
}
return table;
}
/** * Generate primary key policy **@paramTable Resource table Table metadata *@return org.apache.ibatis.executor.keygen.KeyGenerator
*/
private KeyGenerator keyGen(Table table) {
// TODO generates a custom primary key policy
}
public CustomConfiguration getConfiguration(a) {
return configuration;
}
public MetaDataCacheManager getCacheManager(a) {
return cacheManager;
}
public MetaDataParse getParse(a) {
returnparse; }}Copy the code
Modify SqlSessionFactoryBean
Add metadata parser, metadata cache properties to SqlSessionFactoryBean. Easy to configure custom metadata parser and metadata cache implementation.
/** * metadata parser */
private MetaDataParse metaDataParse;
/** * Metadata cache */
private MetaDataCacheManager cacheManager;
Copy the code
MetaStatementBuilder is created using metaDataParse and cacheManager in the buildSqlSessionFactory method. And set MetaStatementBuilder to CustomConfiguration.
protected SqlSessionFactory buildSqlSessionFactory(a) throws Exception {· · · · · ·// Metadata manipulation related extensions
if (targetConfiguration instanceof CustomConfiguration) {
// Cast
CustomConfiguration customConfiguration = (CustomConfiguration) targetConfiguration;
// Create MetaStatementBuilder and add it to ConfigurationcustomConfiguration.setMetaStatementBuilder(MetaStatementBuilder.getInstance(customConfiguration, metaDataParse, cacheManager) .builder()); }......}Copy the code
MetaSqlNode uses MetaStatementBuilder to generate statements
MetaSqlNode inherits MyBatis SqlNode interface. Instead of adding custom statements via DynamicContext.appendSQL (), assemble dynamic XML scripts and convert XML to SqlNode via CustomXMLScriptBuilder, Finally, call sqlNode. apply(DynamicContext) to add SQL to DynamicContext.
The following is an example of modifying logic
public class MetaSqlNode implements SqlNode {· · · · · ·@Override
public boolean apply(DynamicContext context) {
// Support the Test statement
if(! StringUtils.isEmpty(test) && ! evaluator.evaluateBoolean(test, context.getBindings())) {return false;
}
// Get the resource table
table = configuration.getMetaStatementBuilder().table(table);
// Default null statement
SqlNode sqlNode = new TextSqlNode("");
if (type == TypeEnum.update) {
// Update statement
sqlNode = updateScript(table);
}
return sqlNode.apply(context);
}
/** * dynamic update statement node, specify ID **@paramTable Table object *@paramHasWhere contains conditions *@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()));
}
// Select the column to be inserted, ignoring the primary key fieldList<Column> columns = table.getColumnList().stream() .filter(p -> ! table.getId().getCode().equalsIgnoreCase(p.getCode())).collect(Collectors.toList());// Concatenate statements
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);
}
/** * Dynamic SQL statement to SqlNode **@paramSqlScript dynamic SQL statement *@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);
returnbuilder.parseDynamicTags(); }......}Copy the code
The actual use
Spring customizes the metadata parser and metadata cache management implementation as follows:
<! -- Mybatis SessionFactory--> <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 name="metaDataParse"> <bean class=" class extends MetaDataCacheManager"Copy the code
The metadata parser MetaDataParse can build metadata by querying table structure data stored in the database, or by scanning annotations of entity classes to build metadata. Cache management MetaDataCacheManager can be implemented using a simple ConcurrentHashMap or a cache framework such as Ehcache.