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 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);}
執行測試驗證
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’ endWHERE id in (1, 2, 3)
上面的sql語句使用mysql的case when then語法實現的批量更新3條記錄,并且根據id的值不同,設置不同的address值。
mybatis原生實現方式如果使用mybatis的xml語法來實現,xml文件就需要表達為下面方式:
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);}
驗證
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 批量更新內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章:
