mybatis的批量修改,首先针对修改的场景说一下,该批量修改只能批量修改单表,无法多表连接修改多表。
设计思路:传入参数:List<T> list 当前要批量修改的数据集合 ; Class<T> t 对象
1.通过自定义注解,在类名即属性命名上打上对应注解:参数为数据库中的表名以及对应的列名
2.在工具类方法利用反射获取到表名,列名以及类中对应列名的属性名,然后通过传入的list集合中对象的主键ID获取到数据库中存储的对应的对象。
3.将这些对象一一对比,观察哪些属性发生了改变,将这些属性记录下来,然后将需要修改的属性修改。
解释的可能不清楚,直接上代码:
首先是俩自定义注解:主要是为了记录数据库表名与数据库表中属性
package ;
import .*;
@Target()
@Retention()
@Documented
public @interface DbColumn {
/**
* 属性名称
* @return
*/
String name() default "";
/**
* 是否确认修改
*/
boolean canModify() default false;
}
package ;
import .*;
/**
* 此注解操作数据库
*/
@Target()
@Retention()
@Documented
public @interface DbTable {
String name() default "";
}
看看这些注解的用法:@DbTable标注在类上,name指向的是数据库中对应表名,@DbColumn是标注在属性上的,name指的是该属性对应表上的列名,canModify指的是该对象是否可以修改,默认false,不可以修改。
package ;
import ;
import ;
@DbTable(name = "t_teacher")
public class Teacher {
/**
* 主键ID
*/
private Long id;
/**
* 姓名
*/
@DbColumn(name = "f_name",canModify = true)
private String name;
/**
* 年龄
*/
@DbColumn(name = "f_age",canModify = true)
private Integer age;
/**
* 性别
*/
@DbColumn(name = "f_subject",canModify = false)
private String subject;
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
= age;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
= subject;
}
public Long getId() {
return id;
}
public void setId(Long id) {
= id;
}
}
接下来上批量修改的方法代码,
/**
* 实现任意类的批量修改
* @param list 要修改的对象集合
* @param t 修改对象的字节码
*/
public void batchUpdate(List<T> list,Class<T> t) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if((list)){
throw new RuntimeException("批量修改集合不能为空");
}
//思路:1.通过反射获取list集合中对应的ID,2.通过ID在数据库中查询到对应的数据
//3.对比新旧数据,将改变后的数据改入数据库中
String getId = "getId";
Method method = (getId, null);
if(method == null){
throw new RuntimeException("没有找到对应getId方法");
}
//获取到对应的ID集合
List<Object> idList = new ArrayList<>();
for(T obj: list){
Object invoke = (obj, null);
(invoke);
}
//获取自定义注解的表名,属性以及是否可以修改
DbTable dbTable = ();
if(dbTable == null){
throw new RuntimeException("DbTable注解无法获取表名");
}
//获取表名
String tableName = ();
Field[] fields = ();
if(fields == null || == 0){
throw new RuntimeException("未找到要修改的属性");
}
Map<String, String> linkedMap = new LinkedHashMap<>();
for(Field field :fields){
DbColumn dbColumn = ();
if(dbColumn != null){
String columnName = ();
boolean canModify = ();
if(!canModify){
continue;
}
//要修改的表列名称
((),columnName);
}
}
//通过表名,表列名称,对应的ID数组查询出对应属性
if((linkedMap)){
throw new RuntimeException("未找到要修改的属性");
}
//这里的泛型返回类型出了一些问题,我想要返回一个对象,类名无法通过参数传递,也无法用object来返回,最后,只能使用map返回
List<Map<String,Object>> oldList = (tableName,linkedMap,idList,());
if(((oldList))){
throw new RuntimeException("要修改的数据不存在");
}
Map<Long, Map<String, Object>> oldMap = new HashMap<>();
for(Map<String,Object> oldObj: oldList){
((Long) ("id"),oldObj);
}
//我先考虑一下关于批量修改的设计思想方面 修改的话是将对象要修改的属性给统计到一个map中,key为属性名,value为属性值
List<Map<String, Object>> updateObjs = new ArrayList<>();
for(T newObj: list){
//存放修改后的属性和值
Map<String, Object> updateAttrabutes = new HashMap<>();
Long id = (Long)(newObj, null);
//旧据中包含该主键ID
if((id)){
//获取到的旧对象 String为属性名 Object为属性对应的值 新对象为newObj
Map<String, Object> oldObj = (id);
for(<String, Object> oldAttrbute:()){
//通过反射获取新对象中对应的值,与旧对象开始比较
String key = ();
Object oldValue = ();
Object newValue = getMethodValue(key, t, newObj);
//从前后台传递过来的对象有可能是空值(非必填项)
if(!(oldValue != null?():"").equals(newValue != null?():"")) {
String columnName = (key);
(columnName, newValue);
}
}
if(!(updateAttrabutes)){
("f_id",id);
(updateAttrabutes);
}
}
}
if(!(updateObjs)){
(tableName,updateObjs);
}
}
/**
* 传入属性名获取属性值的方法
* @param attName 传入的属性名
*@param t 传入的字节码
*@param obj 要调用的实体类对象
* @return 获取到的属性值
*/
private Object getMethodValue(String attName,Class<T> t,T obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder builder = new StringBuilder("get");
String beginName = (0, 1).toUpperCase();
String endName = (1,());
String getter = (beginName).append(endName).toString();
Method method = (getter, null);
if(method == null){
throw new RuntimeException("未找到当前属性方法:"+getter);
}
Object invoke = (obj, null);
return invoke;
}
queryUpdateAttribute,batchUpdate两个方法对应的sql如下
<update >
<foreach collection="updateObjs" item="item" separator=";">
update ${tableName}
<set>
<foreach collection="item" index="key" item="value" separator="," open="" close="">
<if test="key != 'f_id'">
${key} = #{value}
</if>
</foreach>
</set>
where f_id = #{item.f_id}
</foreach>
</update>
<select resultType="map">
select
f_id as id,
<foreach collection="linkedMap" item="column" separator="," index="attribute">
${column} as #{attribute}
</foreach>
from
${tableName}
where f_id in
<foreach collection="idList" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
对了,记得使用批量修改的时候要改一下项目的配置,因为项目默认是不支持在xml文件中进行批量修改的,所以我们需要加个配置,解决方法:/mayfla/article/details/78774897
最后就修改成功了,一个针对于所有类的批量修改完成。
接下来就是一个测试类的事:
@Test
public void test03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
List<Teacher> teachers = new ArrayList<>();
Teacher teacher = new Teacher();
(new Long(19));
("嘉德伯爵");
(22);
("计算机");
Teacher teacher1 = new Teacher();
(new Long(20));
("月色真美");
(21);
("IT");
(teacher);
(teacher1);
/* (());*/
(teachers,);
}
现附加数据源详细配置:
spring:
datasource:
# 数据源基本配置
username: root
password: 123
driver-class-name:
url: jdbc:mysql://localhost:3306/student?allowMultiQueries=true
type:
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: =true;=500
注意:地址一定要这么配置:url: jdbc:mysql://localhost:3306/student?allowMultiQueries=true,allowMultiQueries参数一定要加,不然报错。
楼主亲测,可用,有什么不了解的可以发表评论,很高兴为你解答。