mybatis-plusカスタムsqlテンプレート、カスタムバッチ更新または挿入


文書ディレクトリ
  • バックグラウンド
  • 解決策
  • カスタムステップ
  • 3.1はmapperに類似する定義である.xmlテンプレート
  • 3.2 springboot起動時にカスタムsql
  • を動的にロード
  • 3.3独自のbaseMapperをカスタマイズし、公式のbaseMapper
  • に置き換えます.
  • 3.4独自のサービスをカスタマイズし、公式のサービスImpl
  • に置き換えます.
  • 4
  • を使用
    背景
    mybatis-plusはmybatisを強化しましたが、使用時には以下の問題も発生します.
    1、主キーをカスタマイズする必要がある
    2、連合主キーに従って一括更新できない、例えば以下のsql
    insert into `test`  (`id`,`plan_id``)  VALUES  (1,2),(3,4)
    ON DUPLICATE KEY UPDATE
    id=values(id),plan_id=values(plan_id)
    

    3、mybatis-plusが持っているsaveOrUpdateBatchを使うと、性能もあまりよくない
    解決策
    カスタムテンプレートはmybatis-plus 3.0以上で、公式にはソリューションsql注入器が提供されています.
    オフィシャルリファレンス:https://gitee.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-deluxe
    カスタムステップ
    3.1類似mapperを定義する.xmlテンプレート
    次のコードは、tableinfoに基づいてxml版のsqlを動的に生成することを行います.生成後は手動で書く必要はありません
    insert into test ( id ,`plan_id``) VALUES (1,2),(3,4) ON DUPLICATE KEY UPDATE id=values(id),plan_id=values(plan_id)
    import com.baomidou.mybatisplus.core.injector.AbstractMethod;
    import com.baomidou.mybatisplus.core.metadata.TableInfo;
    import org.apache.ibatis.executor.keygen.NoKeyGenerator;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.SqlSource;
    import org.springframework.util.StringUtils;
    
    public class MysqlInsertOrUpdateBath extends AbstractMethod {
    
       @Override
       public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
          final String sql = "insert into %s %s values %s ON DUPLICATE KEY UPDATE %s";
          final String tableName = tableInfo.getTableName();
          final String filedSql = prepareFieldSql(tableInfo);
          final String modelValuesSql = prepareModelValuesSql(tableInfo);
          final String duplicateKeySql =prepareDuplicateKeySql(tableInfo);
          final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql,duplicateKeySql);
          SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
          return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertOrUpdateBath", sqlSource, new NoKeyGenerator(), null, null);
       }
    
       /**
        *   ON DUPLICATE KEY UPDATE sql
        * @param tableInfo
        * @return
        */
       private String prepareDuplicateKeySql(TableInfo tableInfo) {
          final StringBuilder duplicateKeySql = new StringBuilder();
          if(!StringUtils.isEmpty(tableInfo.getKeyColumn())) {
             duplicateKeySql.append(tableInfo.getKeyColumn()).append("=(").append(tableInfo.getKeyColumn()).append("),");
          }
    
          tableInfo.getFieldList().forEach(x -> {
             duplicateKeySql.append(x.getColumn())
                            .append("=(")
                            .append(x.getColumn())
                            .append("),");
          });
          duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
          return duplicateKeySql.toString();
       }
    
       /**
        *      
        * @param tableInfo
        * @return
        */
       private String prepareDuplicateKeySql(TableInfo tableInfo) {
          final StringBuilder duplicateKeySql = new StringBuilder();
          if(!StringUtils.isEmpty(tableInfo.getKeyColumn())) {
             duplicateKeySql.append(tableInfo.getKeyColumn()).append("=(").append(tableInfo.getKeyColumn()).append("),");
          }
    
          tableInfo.getFieldList().forEach(x -> {
             duplicateKeySql.append(x.getColumn())
                            .append("=VALUES(")
                            .append(x.getColumn())
                            .append("),");
          });
          duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
          return duplicateKeySql.toString();
       }
    
       private String prepareModelValuesSql(TableInfo tableInfo){
          final StringBuilder valueSql = new StringBuilder();
          valueSql.append("");
          if(!StringUtils.isEmpty(tableInfo.getKeyProperty())) {
             valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
          }
          tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
          valueSql.delete(valueSql.length() - 1, valueSql.length());
          valueSql.append("");
          return valueSql.toString();
       }
    }
    

    3.2 springboot起動時にカスタムsqlを動的にロードする
    import com.baomidou.mybatisplus.core.injector.AbstractMethod;
    import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    @Component
    public class MysqlInjector extends DefaultSqlInjector {
       @Override
       public List getMethodList() {
          List methodList = super.getMethodList();
          methodList.add(new MysqlInsertOrUpdateBath());
          return methodList;
       }
    }
    

    3.3自分のbaseMapperをカスタマイズし、公式のbaseMapperを置き換える
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    
    import java.util.List;
    
    /**
     *     mapper
     * @param 
     */
    public interface BridgeBaseMapper extends BaseMapper {
       int mysqlInsertOrUpdateBath(List list);
    }
    
    

    3.4自分のサービスをカスタマイズし、公式のサービスImplに置き換える
    import com.baomidou.mybatisplus.core.toolkit.Assert;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Collection;
    import java.util.List;
    
    public class BridgeBaseService1, T> extends ServiceImpl {
      //            mapper
       @Transactional(rollbackFor = Exception.class)
       @Override
       public boolean saveOrUpdateBatch(Collection entityList, int batchSize){
          Assert.notEmpty(entityList, "error: entityList must not be empty");
          List objs = Lists.newArrayList(entityList);
          List> partitions = Lists.partition(objs, batchSize);
          for(List list:partitions) {
             this.baseMapper.mysqlInsertOrUpdateBath(list);
          }
          return true;
       }
    }
    

    4使用
    ビジネスのサービスは、カスタムのBridgeBaseServices 1を継承します.公式のサービスインプラントを継承するのと同じように酸っぱいです