mybatis实现对象的批量插入,先写一个批量查询的sql::
Insert into t_table (column1,column2) values (attribute1,attribute2),(attribute3,attribute4);
其中t_table是表名,column1,column2是表列属性,attribute1,attribute2这些则是要插入的字段。
为了实现任意表中的任意属性插入,需要把t_table,column1,column2,以及attribute1,attribute2以参数的形式从java代码中代入数据库,怎么代入呢?
我们可以通过自定义注解方式处理:
package com.bo.annotate;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DbColumn {
/**
* 表列名称
* @return
*/
String name() default "";
/**
* 是否确认修改
*/
//这个是做批量修改用的,暂时不需要用到
boolean canModify() default false;
}
@DbColumn定义的是属性上使用的注解,其中name指的是表列的列名,在插入数据库中需要明确插入哪些列。
package com.bo.annotate;
import java.lang.annotation.*;
/**
* 此注解操作数据库
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DbTable {
String name() default "";
}
@DbTable这个注解在类上方使用,表示这个类对应的表名,其中name属性指的就是表名。
数据库表结构如下:
CREATE TABLE `t_teacher` (
`f_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`f_name` varchar(255) DEFAULT NULL COMMENT '教师姓名',
`f_age` bigint(20) DEFAULT NULL COMMENT '学生年龄',
`f_subject` varchar(255) DEFAULT NULL,
PRIMARY KEY (`f_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8
接下来在我们的pojo实体类上打注解,如下:
package com.bo.pojo;
import com.bo.annotate.DbColumn;
import com.bo.annotate.DbTable;
@DbTable(name = "t_teacher")
public class Teacher {
/**
* 姓名
*/
@DbColumn(name = "f_name",canModify = false)
private String name;
/**
* 年龄
*/
@DbColumn(name = "f_age",canModify = false)
private Integer age;
/**
* 性别
*/
@DbColumn(name = "f_subject",canModify = false)
private String subject;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
其中,数据库表的名称是t_teacher, 而f_name,f_age这些都是表中对应的列名,我们可以通过自定义注解来获取到要插入的表名与表中属性。
接下来就是编写插入操作的方法了:
public void batchInstert(List<T> list) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if(CollectionUtils.isEmpty(list)){
throw new RuntimeException("批量添加集合不能为空");
}
T t = list.get(0);
Class<?> aclass = t.getClass();
boolean annotationPresent = aclass.isAnnotationPresent(DbTable.class);
if(annotationPresent == false){
throw new RuntimeException("批量添加未获取数据库注解");
}
DbTable dbTable = aclass.getAnnotation(DbTable.class);
List<String> columns = new ArrayList<>();
List<String> attributes = new ArrayList<>();
Field[] fields = aclass.getDeclaredFields();
for(Field field:fields){
boolean annotationPresentField = field.isAnnotationPresent(DbColumn.class);
if(annotationPresentField == true){
//获取当前数据库属性名称
DbColumn dbColumn = field.getAnnotation(DbColumn.class);
columns.add(dbColumn.name());
//获取当前类属性名称
String name = field.getName();
attributes.add(name);
}
}
//获取到标注批量添加属性的级别
String tableName = dbTable.name();
//TODO 问题应该出现在属性的注入顺序上以及mybatis遍历map上
List<Map<String, Object>> mapList = new ArrayList<>();
for(T obj:list){
Map<String, Object> map = new LinkedHashMap<>();
StringBuilder builder = new StringBuilder();
for(String attribute:attributes){
String attFirst = attribute.substring(0, 1).toUpperCase();
String attLast = attribute.substring(1, attribute.length());
String getter = builder.append("get").
append(attFirst).append(attLast).toString();
Method method = obj.getClass().getMethod(getter,null);
Object invoke = method.invoke(obj, null);
map.put(attribute, invoke);
builder.setLength(0);
}
mapList.add(map);
}
logger.info("tableName:"+tableName);
dataBaseUtilsMapper.batchInsert(tableName,columns,mapList);
}
batchInsert的sql是这么写的:
<insert id="batchInsert">
insert into ${tableName}
<foreach collection="columns" index="index" separator="," open="(" close=")" item="item">
${item}
</foreach>
values
<foreach collection="mapList" index="index" separator="," item="map">
<foreach collection="map" index="key" separator="," open="(" close=")" item="attribute">
#{attribute}
</foreach>
</foreach>;
</insert>
先声明一下为何tableName,item要用${}接收,经过楼主的亲身体验,如果用#{}来进行传递的话,那么字符串会带上“ ”,sql执行过程中表名肯定是没有“ ”的,所以用${}来接收。
写个测试类:
@Test
public void test01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
List<Teacher> teachers = new ArrayList<>();
Teacher teacher = new Teacher();
teacher.setName("嘉德");
teacher.setAge(22);
teacher.setSubject("计算机");
Teacher teacher1 = new Teacher();
teacher1.setName("月色");
teacher1.setAge(21);
teacher1.setSubject("IT");
teachers.add(teacher);
teachers.add(teacher1);
/* log.warn(teacher.getName());*/
utils.batchInstert(teachers);
}
执行后的结果:
这样一个批量添加就完成了,楼主亲写亲测,可用,希望能帮助大家,也希望大家能指出不足,新手上路,多多关照。