添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

元数据解析器 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) {
        //校验是否为null
        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."));
            //put缓存
            cacheManager.put(table);
            //生成主键策略
            keyGen(table);
        return table;
     * 生成主键策略
     * @param table 资源表表元数据
     * @return org.apache.ibatis.executor.keygen.KeyGenerator
    private KeyGenerator keyGen(Table table) {
        // TODO 生成自定义主键策略
    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;
        //创建 MetaStatementBuilder 并添加到 Configuration
        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) { //支持Test语句 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 按如下配置自定义元数据解析器与元数据缓存管理实现,

<!-- 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>
    <!-- 元数据缓存管理 -->
    <property name="metaDataParse">
        <bean class=" Class extends MetaDataCacheManager "/>
    </property>
</bean>
复制代码

元数据解析器 MetaDataParse 可以通过查询数据库中存储的表结构数据来构建 metadata,也可以扫描实体类的注解来构建 metadata。缓存管理 MetaDataCacheManager 可以使用简单的 ConcurrentHashMap 来实现,也可以是 Ehcache等缓存框架实现。

20小时前
私信