pom
<dependency><groupId>com.github.yulichang</groupId><artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 聯表查詢 -->
</dependency>
MPJLambdaWrapperX
/*** 拓展 MyBatis Plus Join QueryWrapper 類,主要增加如下功能:* <p>* 1. 拼接條件的方法,增加 xxxIfPresent 方法,用于判斷值不存在的時候,不要拼接到條件中。** @param <T> 數據類型*/
public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {public MPJLambdaWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {MPJWrappers.lambdaJoin().like(column, val);if (StringUtils.hasText(val)) {return (MPJLambdaWrapperX<T>) super.like(column, val);}return this;}public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {return (MPJLambdaWrapperX<T>) super.in(column, values);}return this;}public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {return (MPJLambdaWrapperX<T>) super.in(column, values);}return this;}public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (MPJLambdaWrapperX<T>) super.eq(column, val);}return this;}public MPJLambdaWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (MPJLambdaWrapperX<T>) super.ne(column, val);}return this;}public MPJLambdaWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (MPJLambdaWrapperX<T>) super.gt(column, val);}return this;}public MPJLambdaWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (MPJLambdaWrapperX<T>) super.ge(column, val);}return this;}public MPJLambdaWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (MPJLambdaWrapperX<T>) super.lt(column, val);}return this;}public MPJLambdaWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (MPJLambdaWrapperX<T>) super.le(column, val);}return this;}public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {if (val1 != null && val2 != null) {return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);}if (val1 != null) {return (MPJLambdaWrapperX<T>) ge(column, val1);}if (val2 != null) {return (MPJLambdaWrapperX<T>) le(column, val2);}return this;}public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {Object val1 = ArrayUtils.get(values, 0);Object val2 = ArrayUtils.get(values, 1);return betweenIfPresent(column, val1, val2);}// ========== 重寫父類方法,方便鏈式調用 ==========@Overridepublic <X> MPJLambdaWrapperX<T> eq(boolean condition, SFunction<X, ?> column, Object val) {super.eq(condition, column, val);return this;}@Overridepublic <X> MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) {super.eq(column, val);return this;}@Overridepublic <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) {//noinspection uncheckedsuper.orderByDesc(true, column);return this;}@Overridepublic MPJLambdaWrapperX<T> last(String lastSql) {super.last(lastSql);return this;}@Overridepublic <X> MPJLambdaWrapperX<T> in(SFunction<X, ?> column, Collection<?> coll) {super.in(column, coll);return this;}@Overridepublic MPJLambdaWrapperX<T> selectAll(Class<?> clazz) {super.selectAll(clazz);return this;}@Overridepublic MPJLambdaWrapperX<T> selectAll(Class<?> clazz, String prefix) {super.selectAll(clazz, prefix);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, String alias) {super.selectAs(column, alias);return this;}@Overridepublic <E> MPJLambdaWrapperX<T> selectAs(String column, SFunction<E, ?> alias) {super.selectAs(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectAs(column, alias);return this;}@Overridepublic <E, X> MPJLambdaWrapperX<T> selectAs(String index, SFunction<E, ?> column, SFunction<X, ?> alias) {super.selectAs(index, column, alias);return this;}@Overridepublic <E> MPJLambdaWrapperX<T> selectAsClass(Class<E> source, Class<?> tag) {super.selectAsClass(source, tag);return this;}@Overridepublic <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {super.selectSub(clazz, consumer, alias);return this;}@Overridepublic <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, String st, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {super.selectSub(clazz, st, consumer, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column) {super.selectCount(column);return this;}@Overridepublic MPJLambdaWrapperX<T> selectCount(Object column, String alias) {super.selectCount(column, alias);return this;}@Overridepublic <X> MPJLambdaWrapperX<T> selectCount(Object column, SFunction<X, ?> alias) {super.selectCount(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) {super.selectCount(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectCount(column, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column) {super.selectSum(column);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) {super.selectSum(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectSum(column, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column) {super.selectMax(column);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) {super.selectMax(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectMax(column, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column) {super.selectMin(column);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) {super.selectMin(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectMin(column, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column) {super.selectAvg(column);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) {super.selectAvg(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectAvg(column, alias);return this;}@Overridepublic <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column) {super.selectLen(column);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) {super.selectLen(column, alias);return this;}@Overridepublic <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, SFunction<X, ?> alias) {super.selectLen(column, alias);return this;}}
LambdaQueryWrapperX
/*** 拓展 MyBatis Plus QueryWrapper 類,主要增加如下功能:* <p>* 1. 拼接條件的方法,增加 xxxIfPresent 方法,用于判斷值不存在的時候,不要拼接到條件中。** @param <T> 數據類型*/
public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {if (StringUtils.hasText(val)) {return (LambdaQueryWrapperX<T>) super.like(column, val);}return this;}public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {return (LambdaQueryWrapperX<T>) super.in(column, values);}return this;}public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {return (LambdaQueryWrapperX<T>) super.in(column, values);}return this;}public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (LambdaQueryWrapperX<T>) super.eq(column, val);}return this;}public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (LambdaQueryWrapperX<T>) super.ne(column, val);}return this;}public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (LambdaQueryWrapperX<T>) super.gt(column, val);}return this;}public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (LambdaQueryWrapperX<T>) super.ge(column, val);}return this;}public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (LambdaQueryWrapperX<T>) super.lt(column, val);}return this;}public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {if (val != null) {return (LambdaQueryWrapperX<T>) super.le(column, val);}return this;}public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {if (val1 != null && val2 != null) {return (LambdaQueryWrapperX<T>) super.between(column, val1, val2);}if (val1 != null) {return (LambdaQueryWrapperX<T>) ge(column, val1);}if (val2 != null) {return (LambdaQueryWrapperX<T>) le(column, val2);}return this;}public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {Object val1 = ArrayUtils.get(values, 0);Object val2 = ArrayUtils.get(values, 1);return betweenIfPresent(column, val1, val2);}// ========== 重寫父類方法,方便鏈式調用 ==========@Overridepublic LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {super.eq(condition, column, val);return this;}@Overridepublic LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {super.eq(column, val);return this;}@Overridepublic LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) {super.orderByDesc(true, column);return this;}@Overridepublic LambdaQueryWrapperX<T> last(String lastSql) {super.last(lastSql);return this;}@Overridepublic LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {super.in(column, coll);return this;}
}
QueryWrapperX
/*** 拓展 MyBatis Plus QueryWrapper 類,主要增加如下功能:** 1. 拼接條件的方法,增加 xxxIfPresent 方法,用于判斷值不存在的時候,不要拼接到條件中。** @param <T> 數據類型*/
public class QueryWrapperX<T> extends QueryWrapper<T> {public QueryWrapperX<T> likeIfPresent(String column, String val) {if (StringUtils.hasText(val)) {return (QueryWrapperX<T>) super.like(column, val);}return this;}public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {if (!CollectionUtils.isEmpty(values)) {return (QueryWrapperX<T>) super.in(column, values);}return this;}public QueryWrapperX<T> inIfPresent(String column, Object... values) {if (!ArrayUtils.isEmpty(values)) {return (QueryWrapperX<T>) super.in(column, values);}return this;}public QueryWrapperX<T> eqIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.eq(column, val);}return this;}public QueryWrapperX<T> neIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.ne(column, val);}return this;}public QueryWrapperX<T> gtIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.gt(column, val);}return this;}public QueryWrapperX<T> geIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.ge(column, val);}return this;}public QueryWrapperX<T> ltIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.lt(column, val);}return this;}public QueryWrapperX<T> leIfPresent(String column, Object val) {if (val != null) {return (QueryWrapperX<T>) super.le(column, val);}return this;}public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {if (val1 != null && val2 != null) {return (QueryWrapperX<T>) super.between(column, val1, val2);}if (val1 != null) {return (QueryWrapperX<T>) ge(column, val1);}if (val2 != null) {return (QueryWrapperX<T>) le(column, val2);}return this;}public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {return (QueryWrapperX<T>) super.between(column, values[0], values[1]);}if (values!= null && values.length != 0 && values[0] != null) {return (QueryWrapperX<T>) ge(column, values[0]);}if (values!= null && values.length != 0 && values[1] != null) {return (QueryWrapperX<T>) le(column, values[1]);}return this;}// ========== 重寫父類方法,方便鏈式調用 ==========@Overridepublic QueryWrapperX<T> eq(boolean condition, String column, Object val) {super.eq(condition, column, val);return this;}@Overridepublic QueryWrapperX<T> eq(String column, Object val) {super.eq(column, val);return this;}@Overridepublic QueryWrapperX<T> orderByDesc(String column) {super.orderByDesc(true, column);return this;}@Overridepublic QueryWrapperX<T> last(String lastSql) {super.last(lastSql);return this;}@Overridepublic QueryWrapperX<T> in(String column, Collection<?> coll) {super.in(column, coll);return this;}
}
一、核心作用
1. 聯表查詢支持
- 避免空值條件拼接:自動跳過值為空的查詢條件,防止生成 WHERE column = null 的低效 SQL
- MyBatis-Plus Join 擴展:提供基于 Lambda 的聯表查詢能力,彌補 MyBatis Plus 原生不支持聯表的缺陷。
- 類型安全:通過 SFunction<T, ?> 泛型 Lambda 表達式引用實體字段,避免硬編碼字段名。
2. 鏈式 API 設計
// 示例:聯表查詢用戶和部門信息
new MPJLambdaWrapper<UserDO>().selectAll(UserDO.class).select(DepartmentDO::getName, "deptName").leftJoin(DepartmentDO.class, DepartmentDO::getId, UserDO::getDeptId).eq(UserDO::getStatus, 0);
二、擴展點
1. 條件空值安全(核心增強)
通過 xxxIfPresent 方法實現 空值自動跳過,避免無效條件拼接。
方法 | 作用 | 示例代碼 |
---|---|---|
eqIfPresent | 值非空時添加 eq 條件 | wrapper.eqIfPresent(UserDO::getName, name) |
inIfPresent | 集合非空時添加 in 條件 | wrapper.inIfPresent(UserDO::getRoleIds, roleIds) |
betweenIfPresent | 區間值有效時添加 between 條件 | wrapper.betweenIfPresent(UserDO::getCreateTime, start, end) |
2. 鏈式調用增強
重寫父類方法(如 eq, select)返回子類類型,保持鏈式調用連貫性:
public MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) {super.eq(column, val);return this; // 返回 MPJLambdaWrapperX 類型而非父類
}
@Override
public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {super.eq(column, val);return this;
}
3. 智能區間處理
betweenIfPresent 方法支持 單邊區間 自動轉換:
// 當 val2 為空時,轉換為 ge 條件
wrapper.betweenIfPresent(UserDO::getCreateTime, startTime, null);
// 生成 SQL:WHERE create_time >= startTime
三、設計模式分析
1. 裝飾器模式(Decorator Pattern)
- 表現:MPJLambdaWrapperX 在 MPJLambdaWrapper 基礎上增強功能(如空值安全),而非直接修改父類。
- 優勢:符合開閉原則,MyBatis-Plus Join 升級時只需調整父類引用。
2. 模板方法模式(Template Method Pattern)
- 表現:xxxIfPresent 方法封裝了空值判斷邏輯,子類只需調用父類條件方法。
public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (MPJLambdaWrapperX<T>) super.eq(column, val); // 調用父類方法}return this;
}public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {if (ObjectUtil.isNotEmpty(val)) {return (LambdaQueryWrapperX<T>) super.eq(column, val); // 調用父類方法}return this;
}
四、典型使用場景
1. 動態查詢條件構建
public List<UserDO> getUsers(String name, Integer status, LocalDateTime startTime) {return joinMapper.selectJoinList(new MPJLambdaWrapperX<UserDO>().eqIfPresent(UserDO::getName, name) // name 為空時不拼接.eqIfPresent(UserDO::getStatus, status) // status 為空時不拼接.geIfPresent(UserDO::getCreateTime, startTime).leftJoin(DepartmentDO.class, ...));
}
2. 復雜聯表查詢
wrapper.selectAll(UserDO.class).selectAs(DepartmentDO::getName, UserRespVO::getDeptName).leftJoin(DepartmentDO.class, DepartmentDO::getId, UserDO::getDeptId).inIfPresent(UserDO::getRoleIds, roleIds);
3. 區間查詢優化
// 處理可能不完整的區間參數
wrapper.betweenIfPresent(UserDO::getCreateTime, params.getStartTime(), params.getEndTime());
五、與原生 MyBatis Plus 對比
特性 | MyBatis Plus QueryWrapper | QueryWrapperX | MPJLambdaWrapperX | MyBatis Plus LambdaQueryWrapper | LambdaQueryWrapperX |
---|---|---|---|---|---|
聯表支持 | 不支持 | 不支持 | 支持 LEFT/INNER JOIN | 不支持 | 不支持 |
字段引用 | 字符串字段名(如 “name”) | 字符串字段名(如 “name”) | Lambda 表達式(如 UserDO::getName) | Lambda 表達式(如 UserDO::getName) | Lambda 表達式(如 UserDO::getName) |
空值處理 | 需手動判空 | 內置 xxxIfPresent 方法 | 內置 xxxIfPresent 方法自動處理 | 內置 xxxIfPresent 方法自動處理 | 內置 xxxIfPresent 方法自動處理 |
類型安全 | 容易拼寫錯誤 | 容易拼寫錯誤 | 編譯期檢查字段是否存在 | 編譯期檢查字段是否存在 | 編譯期檢查字段是否存在 |