mybatis-plus自定义sql模板,自定义批量更新或插入

文章目录

  • 背景
  • 解决办法
  • 自定义步骤
    • 3.1 定义类似mapper.xml模板
    • 3.2 在springboot 启动时动态加载自定义的sql
    • 3.3 自定义自己的baseMapper,替换官方的baseMapper
    • 3.4 自定义自己的service,替换官方的serviceImpl
  • 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 = "";
      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 自定义自己的service,替换官方的serviceImpl

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 使用

业务的service继承自定义的BridgeBaseService1.就和继承官方的serviceImpl一样酸爽

你可能感兴趣的:(springboot,mysql,mybatis)