前言: mybatis-plus 是一款很好用的crud基础框架,但是我在api中没有找到插入或者更新,那么我想着基于mybatis-plus 自定义一个方法出来用,因为插入或者更新在字段数量多的时候写xml是非常麻烦的事情。
传统写法:
INSERT INTO test(`id`,`name`,`address`)
VALUES('4','修改10','北京'),
( '1', '张三' ,1)
ON DUPLICATE KEY UPDATE
name=VALUES(name),address=VALUES(address);
基于MP的写法,是需要用到MP里面的SQL注入器
MP SQL 注入器文档地址: SQL注入器 | MyBatis-Plus
1. 新建自定义注解 DuplicateSql
说明:自定义注解是为了插入更新基于那些字段(自定更新字段使用)
/**
* @author yueF_L
* @version 1.0
* @date 2022-08-30 10:21
* 自定义注解DuplicateSql
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DuplicateSql {
/**
* 列名称,对应数据库字段
*
* @return
*/
String columnName() default "";
}
2. 自定义批量插入更新方法实现
/**
* 批量插入更新方法实现
*/
public class MysqlInsertOrUpdateBath extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s ON DUPLICATE KEY UPDATE %s</script>";
final String tableName = tableInfo.getTableName();
final String filedSql = prepareFieldSql(tableInfo);
final String modelValuesSql = prepareModelValuesSql(tableInfo);
final String modelKeySql = prepareDuplicateKeySql(modelClass);
final String duplicateKeySql = prepareDuplicateKeySql(tableInfo);
final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql, StringUtils.isBlank(modelKeySql) ? duplicateKeySql : modelKeySql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertOrUpdateBath", sqlSource, new NoKeyGenerator(), null, null);
}
/**
* 根据自定义字段更新
*
* @param modelClass
* @return
*/
private String prepareDuplicateKeySql(Class<?> modelClass) {
final StringBuilder duplicateKeySql = new StringBuilder();
// 获取所有的字段信息
Field[] declaredFields = modelClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
DuplicateSql duplicateSql = declaredField.getAnnotation(DuplicateSql.class);
if (ObjectUtil.isNotEmpty(duplicateSql)) {
String columnName = duplicateSql.columnName();
duplicateKeySql.append(columnName).append("=values(").append(columnName).append("),");
}
}
if (StringUtils.isNotBlank(duplicateKeySql)) {
duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
}
return duplicateKeySql.toString();
}
/**
* 准备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("=values(").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();
}
/**
* 准备属性名
*
* @param tableInfo
* @return
*/
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> {
fieldSql.append(x.getColumn()).append(",");
});
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareModelValuesSql(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
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("</foreach>");
return valueSql.toString();
}
}
3. 注册自定义方法SQL注入器
/**
* @author yueF_L
* @version 1.0
* @date 2022-08-29 17:18
* 注册自定义方法SQL注入器
*/
@Component
public class CustomizedSqlInjector extends DefaultSqlInjector {
/**
* 如果只需增加方法,保留mybatis plus自带方法,
* 可以先获取super.getMethodList(),再添加add
*/
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new MysqlInsertOrUpdateBath());
return methodList;
}
}
4. 自定义Mapper RootMapper
/**
* @author yueF_L
* @version 1.0
* @date 2022-08-29 17:11
* 根Mapper,给表Mapper继承用的,可以自定义通用方法
*/
public interface RootMapper<T> extends BaseMapper<T> {
/**
* 自定义批量新增或更新
* 需要配合自定注解 DuplicateSql 使用
*
* @param list
* @return
*/
Boolean mysqlInsertOrUpdateBath(@Param("list") List<T> list);
}