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

时间:2025-02-21 10:09:49

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参数一定要加,不然报错。

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