开发者

自己手写Mybatis通用batchInsert问题

开发者 https://www.devze.com 2022-11-28 14:22 出处:网络 作者: OK_boom
目录自己手写Mybatis通用batchInsert使用Mapper通用insert方法遇到的问题环境insert抛出不能为标识列插入显式值的异常自己手写Mybatis通用batchInsert写完才...
目录
  • 自己手写MyBATis通用batchInsert
  • 使用Mapper通用insert方法遇到的问题
    • 环境
    • insert抛出不能为标识列插入显式值的异常

自己手写Mybatis通用batchInsert

写完才在群里有人告知本来tk mybatis就提供了批量insert的功能,那就放上来做个纪念吧.

先写个数据字典(其实tk mybatis自身也有相应的功能)。

/**
* Mybatis 带缓冲功能的数据字典
* Created by rocklee on 2019/8/10 23:27
*/
public class MybatisDictionary {
 private static ConcurrentHashMap<Class<?>, String> entityClass2TableName=new ConcurrentHashMap<>();
 private static ConcurrentHashMap<Class<?>, String> entityClass2Columns=new ConcurrentHashMap<>();
 private static ConcurrentHashMap<Class<?>, List<String>> entityClass2FieldsList=new ConcurrentHashMap<>();
 private static ConcurrentHashMap<Class<?>, Hashtable<Field,String>> entityClass2Fields=new ConcurrentHashMap<>();

 public static String getTableName(Class<?> pvEntityClass){
  String lvsRet=entityClass2TableName.get(pvEntityClass);
  if (lvsRet!=null)return lvsRet;
  Table lvTable= pvEntityClass.getAnnotation(Table.class);
  if (lvTable==null){
   throw new RuntimeException(pvEntityClass.getName()+"不是有效的Entity类,缺少@Table标识");
  }
  lvsRet=lvTable.name();
  entityClass2TableName.put(pvEntityClass,lvsRet);
  return lvsRet;
 }
 public static String getColumns(Class<?> pvEntityClass){
  String lvsRet=entityClass2Columns.get(pvEntityClass);
  if (lvsRet!=null)return lvsRet;
  List<String> lvItems=new ArrayList<>();
  for (Field field:pvEntityClass.getDeclaredFields()){
   Column lvColumn=field.getAnnotation(Column.class);
   if (lvColumn==null)continue;
   lvItems.add(lvColumn.name());
  }
  lvsRet= Util.toSWMBCNvbwpbtring(lvItems,",");
  entityClass2Columns.put(pvEntityClass,lvsRet);
  return lvsRet;
 }
 public static List<String> getFieldsList(Class<?> pvEntityClass){
  List<String> lvRet=entityClass2FieldsList.get(pvEntityClass);
  if (lvRet!=null)return lvRet;
  lvRet=new ArrayList<>();
  for (Field field:pvEntityClass.getDeclaredFields()){
   Column lvColumn=field.getAnnotation(Column.class);
   if (lvColumn==null)continue;
   lvRet.add(field.getName());
  }
  entityClass2FieldsList.put(pvEntityClass,lvRet);
  return lvRet;
 }
 public static Hashtable<Field,String>getFields(Class<?> pvEntityClass){
  Hashtable<Field,String> lvRet=entityClass2Fields.get(pvEntityClass);
  if (lvRet!=null)return lvRet;
  lvRet=new Hashtable<Field,String>();
  for (Field field:pvEntityClass.getDeclaredFields()){
   Column lvColumn=field.getAnnotation(Column.class);
   if (lvColumn==null)continue;
   lvRet.put(field,lvColumn.name());
  }
  entityClass2Fields.put(pvEntityClass,lvRet);
  return lvRet;
 }
}

再写SqlProvider:

/** 批处理SQL处理器
* Created by rocklee on 2019/8/10 23:11
*/
public class BatchSqlProvider extends MapperTempWMBCNVbWPblate {
 public BatchSqlProvider(Class<?&开发者_自学开发gt; mapperClass, MapperHelper mapperHelper) {
  super(mapperClass, mapperHelper);
 }

 public String batchInsert(MappedStatement ms) {
  final Class<?> entityClass = getEntityClass(ms);
  StringBuilder sb=new StringBuilder();
  sb.append("INSERT INTO "+MybatisDictionary.getTableName(entityClass));
  sb.append("\n<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n");
  sb.append(MybatisDictionary.getColumns(entityClass));
  sb.append("</trim> \n");
  sb.append("\nVALUES\n");
  sb.append("<foreach collection=\"list\" item=\"record\" separator=\",\">");
  sb.append("\n");
  sb.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n");
  String lvsValFields="#{record."+ Util.toString(MybatisDictionary.getFieldsList(entityClass),"},#{record.")
      +"}";
  sb.append(lvsValFields);
  sb.append("\n</trim>\n</foreach>");
  return sb.toString() ;
 }

}

Mapper: 

/**
* Created by rocklee on 2019/8/11 0:24
*/
@RegisterMapper
public interface BatchMapper<T> {
 /***
 * 批量插入
 * @param pvToInsertList
 * @return
 */
 @InspythonertProvider(type = BatchSqlProvider.class, method = "dynamicSQLwww.devze.com")
 int batchInsert(List<? extends T> pvToInsertList);
}

这是基于tk的MapperTemplate 写的sqlprovider,传入的是MappedStatement,这时候返回的SQL不是raw SQL,还能支持<if>,<foreach>这些mybatis表达式, 而如果用与mapper接口相同的参数方式返回sql,那这些表达式则不会被mybatis解释,而直接传到database服务器那边, 导致异常。

最后要提一下, tkmybatis带自了一个insertListMapper,我们extends它就可以实现批量insert了:

自己手写Mybatis通用batchInsert问题

使用Mapper通用insert方法遇到的问题

环境

  • spingboot
  • sqlserver
  • mybatis
  • Mapper

insert抛出不js能为标识列插入显式值的异常

原因:表的自增主键,通常情况下不需要直接在insert语句中指定设值。查看控制台打印语句,发现是对该字段做了插入。

解决方法:该表对应实体中,在不需要做插入的字段上增加@Column(insertable = false)。

延伸:@Column还有一些值可供设值,

  • name 被标注字段在数据库表中所对应字段的名称;
  • unique 表示该字段是否为唯一标识,默认为false;
  • nullable表示该字段是否可以为null值,默认为true;
  • insertable表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
  • updatable表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。
  • columnDefinition表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。
  • table 定义了包含当前字段的表名。
  • length表示字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。
  • precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。
  • insert不能返回自增id的问题

这个问题,用过给自增id加 @GeneratedValue(generator =’“JDBC”)等,无效;

只能先不用Mapper通用的方法,自己定义一个insert接口,在XML中设置usergeneratedkeys=true ,keyProperty=“id” 这样会把自增得到的id值注入到实体id参数中。

(该问题待解决,有知道的朋友指点下,注意数据库是SqlServer)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号