Fluent Mybatis 批量更新的使用
批量更新同一張表的數(shù)據(jù)
更新多條數(shù)據(jù),每條數(shù)據(jù)都不一樣
背景描述
通常需要一次更新多條數(shù)據(jù)有兩個(gè)方式
在業(yè)務(wù)代碼中循環(huán)遍歷,逐條更新
一次性更新所有數(shù)據(jù), 采用批量sql方式,一次執(zhí)行。
更準(zhǔn)確的說是一條sql語句來更新所有數(shù)據(jù),逐條更新的操作放到數(shù)據(jù)庫端,在業(yè)務(wù)代碼端展現(xiàn)的就是一次性更新所有數(shù)據(jù)。
這兩種方式各有利弊,程序中for循環(huán)實(shí)現(xiàn)就不說了,這里主要介紹第二種方式在fluent mybatis中的實(shí)現(xiàn),以及和mybatis實(shí)現(xiàn)的對(duì)比。
java中for循環(huán)實(shí)現(xiàn)方式
public class UpdateBatchTest extends BaseTest { @Autowired private StudentMapper mapper; @Test public void testBatchJavaEach() { /** 構(gòu)造多個(gè)更新 **/ List<IUpdate> updates = this.newListUpdater(); for (IUpdate update : updates) { mapper.updateBy(update); } } /** * 構(gòu)造多個(gè)更新操作 */ 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); } }
這種方式在大批量更新時(shí), 最大的問題就是效率,逐條更新,每次都會(huì)連接數(shù)據(jù)庫,然后更新,再釋放連接資源。
一條SQL,服務(wù)端逐條更新
mybatis實(shí)現(xiàn)方式
通過mybatis提供的循環(huán)標(biāo)簽,一次構(gòu)造多條update的sql,一次提交服務(wù)器進(jìn)行執(zhí)行。
<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); }
執(zhí)行測(cè)試驗(yàn)證
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); /** 驗(yàn)證SQL參數(shù) **/ db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2) .id.values(23L, 24L) .userName.values("user name23", "user name24") ); } }
使用FluentMybatis實(shí)現(xiàn)方式
使用fluent mybatis進(jìn)行批量更新很簡單,只需要在#updateBy方法中傳入 IUpdate數(shù)組即可
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); /** 驗(yàn)證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); /** 驗(yàn)證SQL參數(shù) **/ db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2) .id.values(23L, 24L) .userName.values("user name23", "user name24") ); } }
要實(shí)現(xiàn)批量更新,首先得設(shè)置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語法實(shí)現(xiàn)的批量更新3條記錄,并且根據(jù)id的值不同,設(shè)置不同的address值。
mybatis原生實(shí)現(xiàn)方式
如果使用mybatis的xml語法來實(shí)現(xiàn),xml文件就需要表達(dá)為下面方式:
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); }
驗(yàn)證
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) )); /** 驗(yàn)證執(zhí)行的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實(shí)現(xiàn)方式
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); /** 驗(yàn)證執(zhí)行的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語句,和對(duì)應(yīng)的參數(shù)(對(duì)應(yīng)case when語句中的預(yù)編譯占位符'?')
如果業(yè)務(wù)入口傳入的是Entity List或者M(jìn)ap List,可以使用java8的stream功能處理成數(shù)組,示例如下:
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); // 驗(yàn)證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); // 驗(yàn)證參數(shù) 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文件就夠了)。在業(yè)務(wù)邏輯上不至于因?yàn)橛蓄~外的xml文件,而產(chǎn)生割裂感。
批量更新不同的表數(shù)據(jù)
上面的例子使用mybatis和fluent mybatis演示的如果通過不同方法批量更新同一張表的數(shù)據(jù),在fluent mybatis的更新其實(shí)不限定于同一張表,
在#updateBy(IUpdate... updates)函數(shù)可以傳入任意表更新.
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(); /** 執(zhí)行不同表的批量更新 **/ mapper.updateBy(update1, update2); /** 驗(yàn)證實(shí)際執(zhí)行的預(yù)編譯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") ); /** 驗(yàn)證實(shí)際執(zhí)行預(yù)編譯SQL入?yún)⒅?**/ 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文檔
到此這篇關(guān)于Fluent Mybatis 批量更新的使用的文章就介紹到這了,更多相關(guān)Fluent Mybatis 批量更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java SMM框架關(guān)聯(lián)關(guān)系映射示例講解
SSM框架是spring MVC ,spring和mybatis框架的整合,是標(biāo)準(zhǔn)的MVC模式,將整個(gè)系統(tǒng)劃分為表現(xiàn)層,controller層,service層,DAO層四層,使用spring MVC負(fù)責(zé)請(qǐng)求的轉(zhuǎn)發(fā)和視圖管理,spring實(shí)現(xiàn)業(yè)務(wù)對(duì)象管理,mybatis作為數(shù)據(jù)對(duì)象的持久化引擎2022-08-08java實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Mapper類中存在名稱相同的方法重載報(bào)錯(cuò)問題
這篇文章主要介紹了Mapper類中存在名稱相同的方法重載報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能,涉及Java文件讀取及base64 轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2018-05-05