項目地址: mybatis-dynamic-query
前言
在 2.0 完成對 tk.mapper 集成,為何 mybatis-dynamic-query 選擇 tk.mapper 集成, 再 3.0 進一步對查詢進行優化,當然這里可能會對比 mybatis-plus, 我覺得有對比大家才能選擇自己合適的。
更新內容
- 添加 DynamicQueryBuilder 步驟化生成 DynamicQuery 語句
- 優化 DynamicQuery 添加,移除篩選和排序
DynamicQueryBuilder 引入
這個在 3.0 引入,目的是為了讓大家寫查詢的時候真的像寫 sql (嚴格遵循 sql 查詢順序),最后通過 build 方法來 build 一個 DynamicQuery, 根據經驗來看 DynamicQueryBuilder 適合篩選條件已知的情況下。
public List<ProductsDO> getProductListByBuilder() {
// select product_name, list_price, category
// where (list_price > 1 and list_price < 10) and description is not null or id = 1
// order by id desc, list_price asc
DynamicQuery<ProductsDO> query = DynamicQueryBuilder.create(ProductsDO.class)
.select(ProductsDO::getProductName, ProductsDO::getListPrice, ProductsDO::getCategory)
.where(ProductsDO::getListPrice, greaterThan(BigDecimal.ONE),
and(ProductsDO::getListPrice, lessThan(BigDecimal.TEN)))
.and(ProductsDO::getDescription, notEqual(null))
.or(ProductsDO::getId, isEqual(1))
.orderBy(ProductsDO::getId, desc())
.thenBy(ProductsDO::getListPrice, asc())
.build();
return productMapper.selectByDynamicQuery(query);
}
DynamicQuery 篩選排序優化
DynamicQuery 的很多方法名被改了,和 DynamicQueryBuilder 基本保持一致,這樣大家在使用的時候比較方便,從下面的例子大家可以看到可以在任何位置添加篩選或者排序並且和 if 判斷語句結合
@Test
public void testGetProductListByQuery() {
BigDecimal startPrice = BigDecimal.valueOf(1.1);
BigDecimal endPrice = BigDecimal.valueOf(10.1);
DynamicQuery<ProductsDO> query = DynamicQuery.createQuery(ProductsDO.class)
.select(ProductsDO::getProductName, ProductsDO::getListPrice, ProductsDO::getCategory);
// 根據參數添加篩選條件,這里就是我們看看開始價,結束價有沒有,如果有才會放到一個組里面,
if (Objects.nonNull(startPrice) || Objects.nonNull(endPrice)) {
FilterGroupDescriptor<ProductsDO> priceFilterGroup = new FilterGroupDescriptor<>();
if (Objects.nonNull(startPrice)) {
priceFilterGroup.and(ProductsDO::getListPrice, greaterThan(startPrice));
}
if (Objects.nonNull(endPrice)) {
priceFilterGroup.and(ProductsDO::getListPrice, lessThan(endPrice));
}
}
query.and(ProductsDO::getDescription, notEqual(null))
.or(ProductsDO::getId, isEqual(1))
.orderBy(ProductsDO::getId, desc())
.orderBy(ProductsDO::getListPrice, asc());
List<ProductsDO> result = productMapper.selectByDynamicQuery(query);
Assert.assertFalse(result.isEmpty());
}
enable 字段
大家看到上面例子, 有 if 判斷條件會斷開一個查詢,這個在閱讀的時候非常不方便,有了 enable 可以設置這個篩選是否生效,這樣我們寫代碼的可讀性高了
@Test
public void testGetProductListByQuery2() {
BigDecimal startPrice = BigDecimal.valueOf(1.1);
BigDecimal endPrice = BigDecimal.valueOf(10.1);
// 根據參數添加篩選條件,這里就是我們看看開始價,結束價有沒有,如果有才會放到一個組里面,
DynamicQuery<ProductsDO> query = DynamicQuery.createQuery(ProductsDO.class)
.select(ProductsDO::getProductName, ProductsDO::getListPrice, ProductsDO::getCategory)
.and(group -> group
.and(ProductsDO::getListPrice, greaterThan(startPrice), Objects.nonNull(startPrice))
.and(ProductsDO::getListPrice, lessThan(endPrice), Objects.nonNull(endPrice)))
.and(ProductsDO::getDescription, notEqual(null))
.or(ProductsDO::getId, isEqual(1))
.orderBy(ProductsDO::getId, desc())
.orderBy(ProductsDO::getListPrice, asc());
List<ProductsDO> result = productMapper.selectByDynamicQuery(query);
Assert.assertFalse(result.isEmpty());
}
對比
開始我是不知道 mybatis-plus 博客園動態查詢第一帖 的不然的話,可能我就直接用了哈哈~,既然自己做了一個也和標桿對比一下吧,但還是期望大家選擇自己合適的吧,這里我只對比 mybatis-plus 查詢功能。
舉例
復雜條件查詢
基本和動態查詢在寫法上基本表現一致,不過新版的動態插敘加上了 enable 字段以后讀起來會好一些
@Test
public void testGetProductListByPlus() {
BigDecimal startPrice = BigDecimal.valueOf(1.1);
BigDecimal endPrice = BigDecimal.valueOf(10.1);
LambdaQueryWrapper<ProductsDO> queryWrapper = new QueryWrapper<ProductsDO>().lambda()
.select(ProductsDO::getListPrice, ProductsDO::getProductName, ProductsDO::getCategory);
if (Objects.nonNull(startPrice) && Objects.nonNull(endPrice)) {
// 沒有找到如何將連個price 篩選放到一個組里面
queryWrapper.and(obj -> obj.gt(ProductsDO::getListPrice, startPrice)
.lt(ProductsDO::getListPrice, endPrice));
} else if (Objects.nonNull(startPrice)) {
queryWrapper.gt(ProductsDO::getListPrice, startPrice);
} else if (Objects.nonNull(endPrice)) {
queryWrapper.lt(ProductsDO::getListPrice, startPrice);
}
queryWrapper.ne(ProductsDO::getDescription, null)
.or(obj -> obj.eq(ProductsDO::getId, 1))
.orderByDesc(ProductsDO::getId)
.orderByAsc(ProductsDO::getListPrice);
List<ProductsDO> result = productPlusMapper.selectList(queryWrapper);
Assert.assertFalse(result.isEmpty());
}
靈活性
mybatis-plus 是非常靈活的, api 特別多, 比如 queryWrapper 可以直接接上操作符比如 eq,gt, lt 也可以接 and, or, 但是動態查詢對於篩選只能接 and / or 操作符必須在里面, 我個人喜歡統一性,這其實就是仁者見仁智者見智了。
安全性
類型檢查
可以說這個就是動態查詢的優勢了,設計之初就是怎么樣讓用戶寫出不會錯的代碼,所以所有的篩選值都是強類型,比如說字段 Price 是一個 BigDecimal, 如果寫 Integer 就會報錯,但是 mybatis-plus 就不會報錯。
@Test
public void testGetProductListByQuery2() {
BigDecimal startPrice = BigDecimal.valueOf(1.1);
BigDecimal endPrice = BigDecimal.valueOf(10.1);
Integer integerStartPrice = 1;
DynamicQuery<ProductsDO> query = DynamicQuery.createQuery(ProductsDO.class)
.select(ProductsDO::getProductName, ProductsDO::getListPrice, ProductsDO::getCategory)
.and(group -> group
// 這段代碼 會包錯,因為integerStartPrice 不是BigDecimal 類型的
.and(ProductsDO::getListPrice, greaterThan(integerStartPrice), Objects.nonNull(startPrice))
.and(ProductsDO::getListPrice, lessThan(endPrice), Objects.nonNull(endPrice)))
.and(ProductsDO::getDescription, notEqual(null))
.or(ProductsDO::getId, isEqual(1))
.orderBy(ProductsDO::getId, desc())
.orderBy(ProductsDO::getListPrice, asc());
List<ProductsDO> result = productMapper.selectByDynamicQuery(query);
Assert.assertFalse(result.isEmpty());
}
鏈式調用
這個動態查詢和 mybatis-plus 都是支持的,不一樣的是,動態查詢不會隨意接后面的方法,是做過驗證的,但是 mybatis-plus 比較靈活,把這個安全性檢查交給用戶了。
比如
@Test
public void testGetProductListByPlus() {
BigDecimal startPrice = BigDecimal.valueOf(1.1);
BigDecimal endPrice = BigDecimal.valueOf(10.1);
LambdaQueryWrapper<ProductsDO> queryWrapper = new QueryWrapper<ProductsDO>().lambda()
.select(ProductsDO::getListPrice, ProductsDO::getProductName, ProductsDO::getCategory);
if (Objects.nonNull(startPrice) && Objects.nonNull(endPrice)) {
// 這里我隨意在篩選后面添加了一個排序, 編譯的時候沒有錯,執行的時候會報錯
queryWrapper.and(obj -> obj.gt(ProductsDO::getListPrice, startPrice)
.lt(ProductsDO::getListPrice, endPrice).orderByAsc(ProductsDO::getListPrice));
} else if (Objects.nonNull(startPrice)) {
queryWrapper.gt(ProductsDO::getListPrice, startPrice);
} else if (Objects.nonNull(endPrice)) {
queryWrapper.lt(ProductsDO::getListPrice, startPrice);
}
queryWrapper.ne(ProductsDO::getDescription, null)
.or(obj -> obj.eq(ProductsDO::getId, 1))
.orderByDesc(ProductsDO::getId)
.orderByAsc(ProductsDO::getListPrice);
List<ProductsDO> result = productPlusMapper.selectList(queryWrapper);
Assert.assertFalse(result.isEmpty());
}
小結
主要給大家看了一下 3.0 對查詢的改動,主要也是給大家多一個選擇, 稍微對比了一下 mybatis-plus, 自我感覺在查詢寫法上面有優勢,但是 mybatis-plus 是功能非常多,大而全的一整套解決方案,文檔非常完善,這也是動態查詢不具備的,所以大家選擇自己合適的。