(二)mybatis实现任意对象的批量修改

mybatis的批量修改,首先针对修改的场景说一下,该批量修改只能批量修改单表,无法多表连接修改多表。

设计思路:传入参数:List<T> list 当前要批量修改的数据集合  ;   Class<T> t  T.class对象

1.通过自定义注解,在类名即属性命名上打上对应注解:参数为数据库中的表名以及对应的列名

2.在工具类方法利用反射获取到表名,列名以及类中对应列名的属性名,然后通过传入的list集合中对象的主键ID获取到数据库中存储的对应的对象。

3.将这些对象一一对比,观察哪些属性发生了改变,将这些属性记录下来,然后将需要修改的属性修改。

解释的可能不清楚,直接上代码:

首先是俩自定义注解:主要是为了记录数据库表名与数据库表中属性

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;

}
package com.bo.annotate;

import java.lang.annotation.*;

/**
 * 此注解操作数据库
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DbTable {
    String name() default "";
}

看看这些注解的用法:@DbTable标注在类上,name指向的是数据库中对应表名,@DbColumn是标注在属性上的,name指的是该属性对应表上的列名,canModify指的是该对象是否可以修改,默认false,不可以修改。

package com.bo.pojo;

import com.bo.annotate.DbColumn;
import com.bo.annotate.DbTable;

@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) {
        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;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

接下来上批量修改的方法代码,

/**
     * 实现任意类的批量修改
     * @param list 要修改的对象集合
     * @param t    修改对象的字节码
     */
    public void batchUpdate(List<T> list,Class<T> t) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if(CollectionUtils.isEmpty(list)){
            throw new RuntimeException("批量修改集合不能为空");
        }
        //思路:1.通过反射获取list集合中对应的ID,2.通过ID在数据库中查询到对应的数据
        //3.对比新旧数据,将改变后的数据改入数据库中
        String getId = "getId";
        Method method = t.getMethod(getId, null);
        if(method == null){
            throw new RuntimeException("没有找到对应getId方法");
        }

        //获取到对应的ID集合
        List<Object> idList = new ArrayList<>();
        for(T obj: list){
            Object invoke = method.invoke(obj, null);
            idList.add(invoke);
        }

        //获取自定义注解的表名,属性以及是否可以修改
        DbTable dbTable = t.getAnnotation(DbTable.class);
        if(dbTable == null){
            throw new RuntimeException("DbTable注解无法获取表名");
        }
        //获取表名
        String tableName = dbTable.name();

        Field[] fields = t.getDeclaredFields();

        if(fields == null || fields.length == 0){
            throw new RuntimeException("未找到要修改的属性");
        }

        Map<String, String> linkedMap = new LinkedHashMap<>();
        for(Field field :fields){
            DbColumn dbColumn = field.getAnnotation(DbColumn.class);
            if(dbColumn != null){
                String columnName = dbColumn.name();
                boolean canModify = dbColumn.canModify();
                if(!canModify){
                    continue;
                }
                //要修改的表列名称
                linkedMap.put(field.getName(),columnName);
            }
        }

        //通过表名,表列名称,对应的ID数组查询出对应属性
        if(CollectionUtils.isEmpty(linkedMap)){
            throw new RuntimeException("未找到要修改的属性");
        }
        //这里的泛型返回类型出了一些问题,我想要返回一个对象,类名无法通过参数传递,也无法用object来返回,最后,只能使用map返回
        List<Map<String,Object>> oldList = dataBaseUtilsMapper.queryUpdateAttribute(tableName,linkedMap,idList,t.getName());
        if(CollectionUtils.isEmpty((oldList))){
            throw new RuntimeException("要修改的数据不存在");
        }
        Map<Long, Map<String, Object>> oldMap = new HashMap<>();
        for(Map<String,Object> oldObj: oldList){
            oldMap.put((Long) oldObj.get("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)method.invoke(newObj, null);
            //旧据中包含该主键ID
            if(oldMap.containsKey(id)){
                //获取到的旧对象 String为属性名 Object为属性对应的值 新对象为newObj
                Map<String, Object> oldObj = oldMap.get(id);
                for(Map.Entry<String, Object> oldAttrbute:oldObj.entrySet()){
                    //通过反射获取新对象中对应的值,与旧对象开始比较
                    String key = oldAttrbute.getKey();
                    Object oldValue  = oldAttrbute.getValue();
                    Object newValue = getMethodValue(key, t, newObj);
                    //从前后台传递过来的对象有可能是空值(非必填项)
                    if(!(oldValue != null?oldValue.toString():"").equals(newValue != null?newValue.toString():"")) {
                        String columnName = linkedMap.get(key);
                        updateAttrabutes.put(columnName, newValue);
                    }

                }
                if(!CollectionUtils.isEmpty(updateAttrabutes)){
                    updateAttrabutes.put("f_id",id);
                    updateObjs.add(updateAttrabutes);
                }
            }
        }
        if(!CollectionUtils.isEmpty(updateObjs)){
            dataBaseUtilsMapper.batchUpdate(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 = attName.substring(0, 1).toUpperCase();
        String endName = attName.substring(1,attName.length());
        String getter = builder.append(beginName).append(endName).toString();
        Method method = t.getMethod(getter, null);
        if(method == null){
            throw new RuntimeException("未找到当前属性方法:"+getter);
        }
        Object invoke = method.invoke(obj, null);
        return invoke;
    }
queryUpdateAttribute,batchUpdate两个方法对应的sql如下
<update id="batchUpdate">
        <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 id="queryUpdateAttribute" 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文件中进行批量修改的,所以我们需要加个配置,解决方法:https://blog.csdn.net/mayfla/article/details/78774897

最后就修改成功了,一个针对于所有类的批量修改完成。

接下来就是一个测试类的事:

@Test
    public void test03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        List<Teacher> teachers = new ArrayList<>();
        Teacher teacher = new Teacher();
        teacher.setId(new Long(19));
        teacher.setName("嘉德伯爵");
        teacher.setAge(22);
        teacher.setSubject("计算机");
        Teacher teacher1 = new Teacher();
        teacher1.setId(new Long(20));
        teacher1.setName("月色真美");
        teacher1.setAge(21);
        teacher1.setSubject("IT");
        teachers.add(teacher);
        teachers.add(teacher1);
        /* log.warn(teacher.getName());*/
        utils.batchUpdate(teachers,Teacher.class);
    }

 

现附加数据源详细配置:

spring:
  datasource:
#   数据源基本配置
    username: root
    password: 123
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/student?allowMultiQueries=true
    type: com.alibaba.druid.pool.DruidDataSource
#   数据源其他配置
    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: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

注意:地址一定要这么配置:url: jdbc:mysql://localhost:3306/student?allowMultiQueries=true,allowMultiQueries参数一定要加,不然报错。

楼主亲测,可用,有什么不了解的可以发表评论,很高兴为你解答。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值