写完才在群里有人告知本来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.toString(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 MapperTemplate {
public BatchSqlProvider(Class<?> 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
*/
@InsertProvider(type = BatchSqlProvider.class, method = "dynamicSQL")
int batchInsert(List<? extends T> pvToInsertList);
}
这是基于tk的MapperTemplate 写的sqlprovider,传入的是MappedStatement,这时候返回的SQL不是raw SQL,还能支持<if>,<foreach>这些mybatis表达式, 而如果用与mapper接口相同的参数方式返回sql,那这些表达式则不会被mybatis解释,而直接传到database服务器那边, 导致异常。
==============================
最后要提一下, tkmybatis带自了一个insertListMapper,我们extends它就可以实现批量insert了: