Fluent Mybatis 批量更新的使用

 更新时间:2021年08月05日 10:06:47   作者:稻草江南  
本文主要介绍了Fluent Mybatis 批量更新的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

批量更新同一张表的数据

更新多条数据,每条数据都不一样

背景描述

通常需要一次更新多条数据有两个方式

在业务代码中循环遍历,逐条更新
一次性更新所有数据, 采用批量sql方式,一次执行。

更准确的说是一条sql语句来更新所有数据,逐条更新的操作放到数据库端,在业务代码端展现的就是一次性更新所有数据。

这两种方式各有利弊,程序中for循环实现就不说了,这里主要介绍第二种方式在fluent mybatis中的实现,以及和mybatis实现的对比。

java中for循环实现方式

public class UpdateBatchTest extends BaseTest {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void testBatchJavaEach() {
        /** 构造多个更新 **/
        List<IUpdate> updates = this.newListUpdater();
        for (IUpdate update : updates) {
            mapper.updateBy(update);
        }
    }
    
    /**
     * 构造多个更新操作
     */
    private List<IUpdate> newListUpdater() {
        StudentUpdate update1 = new StudentUpdate()
            .update.userName().is("user name23").end()
            .where.id().eq(23L).end();
        StudentUpdate update2 = new StudentUpdate()
            .update.userName().is("user name24").end()
            .where.id().eq(24L).end();
        return Arrays.asList(update1, update2);
    }
}

这种方式在大批量更新时, 最大的问题就是效率,逐条更新,每次都会连接数据库,然后更新,再释放连接资源。

一条SQL,服务端逐条更新

mybatis实现方式

通过mybatis提供的循环标签,一次构造多条update的sql,一次提交服务器进行执行。

<update id="updateStudentBatch"  parameterType="java.util.List">  
    <update id="updateStudentBatch"  parameterType="java.util.List">
        <foreach collection="list" item="item" index="index" open="" close="" separator=";">
            update student
            <set>
                user_name=#{item.userName}
            </set>
            where id = #{item.id}
        </foreach>
    </update>   
</update>

定义Mapper

public interface StudentBatchMapper {
    void updateStudentBatch(List list);
}

执行测试验证

public class UpdateBatchTest extends BaseTest {
    @Autowired
    private StudentBatchMapper batchMapper;

    @Test
    public void updateStudentBatch() {
        List<StudentEntity> students = Arrays.asList(
            new StudentEntity().setId(23L).setUserName("user name23"),
            new StudentEntity().setId(24L).setUserName("user name24"));
        batchMapper.updateStudentBatch(students);
        /** 验证SQL参数 **/
        db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2)
            .id.values(23L, 24L)
            .userName.values("user name23", "user name24")
        );
    }
}

使用FluentMybatis实现方式

使用fluent mybatis进行批量更新很简单,只需要在#updateBy方法中传入 IUpdate数组即可

public class UpdateBatchTest extends BaseTest {
    @Autowired
    private StudentMapper mapper;

    @DisplayName("批量更新同一张表")
    @Test
    public void testUpdateBatch_same() {
        IUpdate[] updates = this.newListUpdater().toArray(new IUpdate[0]);
        mapper.updateBy(updates);
        /** 验证SQL语句 **/
        db.sqlList().wantFirstSql().eq("" +
                "UPDATE student SET gmt_modified = now(), user_name = ? WHERE id = ?; " +
                "UPDATE student SET gmt_modified = now(), user_name = ? WHERE id = ?"
            , StringMode.SameAsSpace);
        /** 验证SQL参数 **/
        db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2)
            .id.values(23L, 24L)
            .userName.values("user name23", "user name24")
        );
    }
}

要实现批量更新,首先得设置mysql支持批量操作,在jdbc url链接中附加&allowMultiQueries=true属性

例如:

jdbc:mysql://localhost:3306/testdb?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true

使用mysql的Case When then方式更新

UPDATE student 
SET gmt_modified = now(),
address = case id when 1 then 'address 1' when 2 then 'address 2' when 3 then 'address 3' end
WHERE id in (1, 2, 3)

上面的sql语句使用mysql的case when then语法实现的批量更新3条记录,并且根据id的值不同,设置不同的address值。

mybatis原生实现方式

如果使用mybatis的xml语法来实现,xml文件就需要表达为下面方式:

xml文件

<update id="updateBatchByIds" parameterType="list">
    update student
    <trim prefix="set" suffixOverrides=",">
        <trim prefix="address =case id" suffix="end,">
            <foreach collection="list" item="item" index="index">
                <if test="item.id!=null">
                    when #{item.id} then #{item.address}
                </if>
            </foreach>
        </trim>
    </trim>
    <trim prefix="age =case id" suffix="end,">
        <foreach collection="list" item="item" index="index">
            <if test="item.id!=null">
                when #{item.id} then #{item.age}
            </if>
        </foreach>
    </trim>
    where id in
    <foreach collection="list" item="item" index="index" separator="," open="(" close=")">
        #{item.id}
    </foreach>
</update>

定义Mapper

public interface StudentBatchMapper {
    int updateBatchByIds(List<StudentEntity> list);
}

验证

public class CaseFuncTest extends BaseTest {
    @Autowired
    private StudentBatchMapper batchMapper;

    @Test
    public void test_mybatis_batch() {
        batchMapper.updateBatchByIds(Arrays.asList(
            new StudentEntity().setId(1L).setAddress("address 1").setAge(23),
            new StudentEntity().setId(2L).setAddress("address 2").setAge(24),
            new StudentEntity().setId(3L).setAddress("address 3").setAge(25)
        ));
        /** 验证执行的SQL语句 **/
        db.sqlList().wantFirstSql().eq("" +
                "update student " +
                "set address =case id when ? then ? when ? then ? when ? then ? end, " +
                "age =case id when ? then ? when ? then ? when ? then ? end " +
                "where id in ( ? , ? , ? )"
            , StringMode.SameAsSpace);
    }
}

使用Fluent Mybatis实现方式

public class CaseFuncTest extends BaseTest {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void test_fluentMybatisBatch() throws Exception {
        final String CaseWhen = "case id " +
            "when 1 then ? " +
            "when 2 then ? " +
            "else ? end";
        StudentUpdate update = new StudentUpdate()
            .update.address().applyFunc(CaseWhen, "address 1", "address 2", "address 3")
            .set.age().applyFunc(CaseWhen, 23, 24, 25)
            .end()
            .where.id().in(new int[]{1, 2, 3}).end();
        mapper.updateBy(update);
        /** 验证执行的SQL语句 **/
        db.sqlList().wantFirstSql()
            .eq("UPDATE student " +
                    "SET gmt_modified = now(), " +
                    "address = case id when 1 then ? when 2 then ? else ? end, " +
                    "age = case id when 1 then ? when 2 then ? else ? end " +
                    "WHERE id IN (?, ?, ?)",
                StringMode.SameAsSpace);
    }
}

只需要在applyFunc中传入case when语句,和对应的参数(对应case when语句中的预编译占位符'?')

如果业务入口传入的是Entity List或者Map List,可以使用java8的stream功能处理成数组,示例如下:

public class CaseFuncTest extends BaseTest {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void test_fluentMybatisBatch2() throws Exception {
        List<StudentEntity> students = Arrays.asList(
            new StudentEntity().setId(1L).setAddress("address 1").setAge(23),
            new StudentEntity().setId(2L).setAddress("address 2").setAge(24),
            new StudentEntity().setId(3L).setAddress("address 3").setAge(25));
        final String CaseWhen = "case id " +
            "when 1 then ? " +
            "when 2 then ? " +
            "else ? end";
        StudentUpdate update = new StudentUpdate()
            .update.address().applyFunc(CaseWhen, getFields(students, StudentEntity::getAddress))
            .set.age().applyFunc(CaseWhen, getFields(students, StudentEntity::getAge))
            .end()
            .where.id().in(getFields(students, StudentEntity::getId)).end();
        mapper.updateBy(update);
        // 验证SQL语句
        db.sqlList().wantFirstSql()
            .eq("UPDATE student " +
                    "SET gmt_modified = now(), " +
                    "address = case id when 1 then ? when 2 then ? else ? end, " +
                    "age = case id when 1 then ? when 2 then ? else ? end " +
                    "WHERE id IN (?, ?, ?)",
                StringMode.SameAsSpace);
        // 验证参数
        db.sqlList().wantFirstPara()
            .eqReflect(new Object[]{"address 1", "address 2", "address 3", 23, 24, 25, 1L, 2L, 3L});
    }

    private Object[] getFields(List<StudentEntity> students, Function<StudentEntity, Object> getField) {
        return students.stream().map(getField).toArray(Object[]::new);
    }
}

使用Fluent Mybatis无需额外编写xml文件和mapper(使用框架生成的Mapper文件就够了)。在业务逻辑上不至于因为有额外的xml文件,而产生割裂感。

批量更新不同的表数据

上面的例子使用mybatis和fluent mybatis演示的如果通过不同方法批量更新同一张表的数据,在fluent mybatis的更新其实不限定于同一张表,

在#updateBy(IUpdate... updates)函数可以传入任意表更新.

public class UpdateBatchTest extends BaseTest {
    @Autowired
    private StudentMapper mapper;

    @DisplayName("批量更新不同表")
    @Test
    public void testUpdateBatch_different() {
        StudentUpdate update1 = new StudentUpdate()
            .update.userName().is("user name23").end()
            .where.id().eq(23L).end();
        HomeAddressUpdate update2 = new HomeAddressUpdate()
            .update.address().is("address 24").end()
            .where.id().eq(24L).end();
        
        /** 执行不同表的批量更新 **/
        mapper.updateBy(update1, update2);
    
        /** 验证实际执行的预编译SQL语句**/
        db.sqlList().wantFirstSql().eq("" +
                "UPDATE student SET gmt_modified = now(), user_name = ? WHERE id = ?; " +
                "UPDATE home_address SET gmt_modified = now(), address = ? WHERE id = ?", StringMode.SameAsSpace);
        db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2)
            .id.values(23L, 24L)
            .userName.values("user name23", "user")
        );
        /** 验证实际执行预编译SQL入参值 **/
        db.table(ATM.table.homeAddress).query().eqDataMap(ATM.dataMap.homeAddress.table(2)
            .id.values(23, 24)
            .address.values("address", "address 24")
        );
    }
}

示例更新了2张表: student 和 home_address

参考

Fluent MyBatis地址
Fluent MyBatis文档

到此这篇关于Fluent Mybatis 批量更新的使用的文章就介绍到这了,更多相关Fluent Mybatis 批量更新内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java自定义JDBC实现连接池

    java自定义JDBC实现连接池

    本文主要介绍了java自定义JDBC实现连接池,包含实现JDBC连接池以及SQLException 异常的处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • Spring事务@Transactional注解四种不生效案例场景分析

    Spring事务@Transactional注解四种不生效案例场景分析

    这篇文章主要为大家介绍了Spring事务@Transactional注解四种不生效的案例场景示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Java List简介_动力节点Java学院整理

    Java List简介_动力节点Java学院整理

    Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解。需要的朋友参考下吧
    2017-05-05
  • Java多线程案例之阻塞队列详解

    Java多线程案例之阻塞队列详解

    阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则.阻塞队列能是一种线程安全的数据结构。本文将通过一些示例为大家详细讲讲阻塞队列的原理与使用,感兴趣的小伙伴可以学习一下
    2022-10-10
  • java中的Serializable、transient关键字详解

    java中的Serializable、transient关键字详解

    这篇文章主要介绍了java中的Serializable、transient关键字详解,序列化只会保存属性值,不会保存方法,通过反序列化可以把序列化后的内容恢复成对象,需要的朋友可以参考下
    2023-09-09
  • 带你了解10道java入门面试题

    带你了解10道java入门面试题

    面试题相信大家都不陌生,想要一个好的工作面试题必不可少的,下面和小编一起来学习与了解Java当中有有些什么面试题吧,希望能给你带来帮助
    2021-08-08
  • Java数据结构之顺序表的实现

    Java数据结构之顺序表的实现

    线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,本文将用Java实现顺序表,感兴趣的可以了解一下
    2022-09-09
  • Spring AOP的底层实现方式-代理模式

    Spring AOP的底层实现方式-代理模式

    这篇文章主要介绍了Spring AOP的底层实现方式-代理模式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Springboot之restTemplate的配置及使用方式

    Springboot之restTemplate的配置及使用方式

    这篇文章主要介绍了Springboot之restTemplate的配置及使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java泛型初学者之上、下界通配符的深入理解

    Java泛型初学者之上、下界通配符的深入理解

    这篇文章主要给大家介绍了关于Java泛型初学者之上、下界通配符的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者实用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12

最新评论