Fluent Mybatis 批量更新的使用
背景描述
通常需要一次更新多條數(shù)據(jù)有兩個(gè)方式
在業(yè)務(wù)代碼中循環(huán)遍歷,逐條更新一次性更新所有數(shù)據(jù), 采用批量sql方式,一次執(zhí)行。
更準(zhǔn)確的說是一條sql語句來更新所有數(shù)據(jù),逐條更新的操作放到數(shù)據(jù)庫(kù)端,在業(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ù)庫(kù),然后更新,再釋放連接資源。
一條SQL,服務(wù)端逐條更新mybatis實(shí)現(xiàn)方式通過mybatis提供的循環(huán)標(biāo)簽,一次構(gòu)造多條update的sql,一次提交服務(wù)器進(jìn)行執(zhí)行。
<update parameterType='java.util.List'> <update 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)行批量更新很簡(jiǎ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’ endWHERE 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 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)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. MySQL基礎(chǔ)教程9 —— 函數(shù)之日期和時(shí)間函數(shù)2. DB2 XML 全文搜索之為文本搜索做準(zhǔn)備3. Microsoft Office Access修改代碼字體大小的方法4. mssql鎖基礎(chǔ)教程5. MySQL 千萬級(jí)數(shù)據(jù)量如何快速分頁6. centos 7安裝mysql5.5和安裝 mariadb使用的命令7. Mybatis查詢方法如何實(shí)現(xiàn)沒有返回值8. 快速解決mysql導(dǎo)出scv文件亂碼、躥行的問題9. 數(shù)據(jù)庫(kù)人員手冊(cè)之ORACLE應(yīng)用源碼10. debian10 mariadb安裝過程詳解
