1. 程式人生 > >mybatis-dynamic-query 3.0 更新

mybatis-dynamic-query 3.0 更新

專案地址: mybatis-dynamic-query

前言

在 2.0 完成對 tk.mapper 整合,為何 mybatis-dynamic-query 選擇 tk.mapper 整合, 再 3.0 進一步對查詢進行優化,當然這裡可能會對比 mybatis-plus, 我覺得有對比大家才能選擇自己合適的。

更新內容

  1. 新增 DynamicQueryBuilder 步驟化生成 DynamicQuery 語句
  2. 優化 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 是功能非常多,大而全的一整套解決方案,文件非常完善,這也是動態查詢不具備的,所以大家選擇自己合適的