Fluent Mybatis 批量更新的使用
批量更新同一張表的數(shù)據(jù)
更新多條數(shù)據(jù),每條數(shù)據(jù)都不一樣
背景描述
通常需要一次更新多條數(shù)據(jù)有兩個方式
在業(yè)務代碼中循環(huán)遍歷,逐條更新
一次性更新所有數(shù)據(jù), 采用批量sql方式,一次執(zhí)行。
更準確的說是一條sql語句來更新所有數(shù)據(jù),逐條更新的操作放到數(shù)據(jù)庫端,在業(yè)務代碼端展現(xiàn)的就是一次性更新所有數(shù)據(jù)。
這兩種方式各有利弊,程序中for循環(huán)實現(xiàn)就不說了,這里主要介紹第二種方式在fluent mybatis中的實現(xiàn),以及和mybatis實現(xiàn)的對比。
java中for循環(huán)實現(xiàn)方式
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);
}
}
這種方式在大批量更新時, 最大的問題就是效率,逐條更新,每次都會連接數(shù)據(jù)庫,然后更新,再釋放連接資源。
一條SQL,服務端逐條更新
mybatis實現(xiàn)方式
通過mybatis提供的循環(huán)標簽,一次構造多條update的sql,一次提交服務器進行執(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í)行測試驗證
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參數(shù) **/
db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2)
.id.values(23L, 24L)
.userName.values("user name23", "user name24")
);
}
}
使用FluentMybatis實現(xiàn)方式
使用fluent mybatis進行批量更新很簡單,只需要在#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);
/** 驗證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參數(shù) **/
db.table(ATM.table.student).query().eqDataMap(ATM.dataMap.student.table(2)
.id.values(23L, 24L)
.userName.values("user name23", "user name24")
);
}
}
要實現(xiàn)批量更新,首先得設置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語法實現(xiàn)的批量更新3條記錄,并且根據(jù)id的值不同,設置不同的address值。
mybatis原生實現(xiàn)方式
如果使用mybatis的xml語法來實現(xiàn),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)
));
/** 驗證執(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實現(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);
/** 驗證執(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語句,和對應的參數(shù)(對應case when語句中的預編譯占位符'?')
如果業(yè)務入口傳入的是Entity List或者Map 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);
// 驗證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);
// 驗證參數(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è)務邏輯上不至于因為有額外的xml文件,而產生割裂感。
批量更新不同的表數(shù)據(jù)
上面的例子使用mybatis和fluent mybatis演示的如果通過不同方法批量更新同一張表的數(shù)據(jù),在fluent mybatis的更新其實不限定于同一張表,
在#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);
/** 驗證實際執(zhí)行的預編譯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")
);
/** 驗證實際執(zhí)行預編譯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文檔
到此這篇關于Fluent Mybatis 批量更新的使用的文章就介紹到這了,更多相關Fluent Mybatis 批量更新內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)文件和base64流的相互轉換功能示例
這篇文章主要介紹了Java實現(xiàn)文件和base64流的相互轉換功能,涉及Java文件讀取及base64 轉換相關操作技巧,需要的朋友可以參考下2018-05-05

