mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-18 17:38:48 +08:00
拓展mybatis-plus-join的一些用法
This commit is contained in:
parent
bc827570e4
commit
83b8543f18
7
pom.xml
7
pom.xml
@ -208,6 +208,13 @@
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-boot-starter</artifactId>
|
||||
<version>${mybatis-plus-join.version}</version>
|
||||
<!--排除后重写扩展类到目录com.github.yulichang.wrapper.ext-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-wrapper-ext</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- sql性能分析插件 -->
|
||||
|
||||
@ -45,6 +45,13 @@
|
||||
<dependency>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-boot-starter</artifactId>
|
||||
<!--排除后重写扩展类到目录com.github.yulichang.wrapper.ext-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-wrapper-ext</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- sql性能分析插件 -->
|
||||
|
||||
@ -0,0 +1,199 @@
|
||||
package com.github.yulichang.wrapper.ext;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import com.github.yulichang.wrapper.interfaces.IExt;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.dromara.common.mybatis.helper.DataBaseHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 扩展接口,提供基于MPJ额外的查询方法
|
||||
*
|
||||
* @author LukewarmMe
|
||||
* @since 2026/3/25
|
||||
*/
|
||||
public interface Ext<Children extends MPJLambdaWrapper<?>> extends IExt<Children> {
|
||||
|
||||
/**
|
||||
* JSON字段属性非空查询
|
||||
* 假如 SYS_USER 中有一个数据库类型为 JSON 的字段 USER_EXT_INFO
|
||||
* 实体类中为 private UserExtInfo userExtInfo,UserExtInfo对象有属性workNumber
|
||||
* 使用方式:wrapper.jsonIsNotNull(SysUser::getUserExtInfo, UserExtInfo::getWorkNumber)
|
||||
*
|
||||
* @param jsonColumn 实体中JSON类型字段
|
||||
* @param jsonProperty JSON内部对象的属性Lambda
|
||||
*/
|
||||
default <T, E> Children jsonIsNotNull(SFunction<T, ?> jsonColumn, SFunction<E, ?> jsonProperty) {
|
||||
getChildren().applyFunc(
|
||||
DataBaseHelper.buildJsonIsNotNullSql(jsonProperty),
|
||||
arg -> arg.accept(jsonColumn)
|
||||
);
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字段等值查询
|
||||
* 使用方法 wrapper.jsonEqIfExists(SysUser::getUserExtInfo, UserExtInfo::getWorkNumber, userExtInfo.getWorkNumber());
|
||||
*
|
||||
* @param jsonColumn 实体类中JSON类型的字段
|
||||
* @param jsonProperty JSON内部对象的属性Lambda
|
||||
* @param jsonBean JSON对象(传整个对象,无需get取值)
|
||||
*/
|
||||
default <T, E> Children jsonEqIfExists(SFunction<T, ?> jsonColumn, SFunction<E, ?> jsonProperty, E jsonBean) {
|
||||
// 对象为空直接跳过
|
||||
if (jsonBean == null) {
|
||||
return getChildren();
|
||||
}
|
||||
Object value = jsonProperty.apply(jsonBean);
|
||||
// 值为空跳过
|
||||
if (ObjectUtil.isEmpty(value)) {
|
||||
return getChildren();
|
||||
}
|
||||
// 拼接SQL
|
||||
getChildren().applyFunc(
|
||||
DataBaseHelper.buildJsonEqSql(jsonProperty),
|
||||
arg -> arg.accept(jsonColumn),
|
||||
value
|
||||
);
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字段模糊查询
|
||||
* 使用方法 wrapper.jsonLikeIfExists(SysUser::getUserExtInfo, UserExtInfo::getWorkNumber, userExtInfo.getWorkNumber());
|
||||
*
|
||||
* @param jsonColumn 实体类中JSON类型的字段
|
||||
* @param jsonProperty JSON内部对象的属性Lambda
|
||||
* @param jsonBean JSON对象(传整个对象,无需get取值)
|
||||
*/
|
||||
default <T, E> Children jsonLikeIfExists(SFunction<T, ?> jsonColumn, SFunction<E, ?> jsonProperty, E jsonBean) {
|
||||
// 对象为空直接跳过
|
||||
if (jsonBean == null) {
|
||||
return getChildren();
|
||||
}
|
||||
Object value = jsonProperty.apply(jsonBean);
|
||||
// 值为空跳过
|
||||
if (ObjectUtil.isEmpty(value)) {
|
||||
return getChildren();
|
||||
}
|
||||
// 拼接SQL
|
||||
getChildren().applyFunc(
|
||||
DataBaseHelper.buildJsonLikeSql(jsonProperty),
|
||||
arg -> arg.accept(jsonColumn),
|
||||
"%" + value.toString().trim() + "%"
|
||||
);
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据给定的列名和祖先值查找集合中是否存在匹配的记录
|
||||
*
|
||||
* @param column 祖级列表字段
|
||||
* @param ancestorsValue 要查询的值
|
||||
*/
|
||||
default <T> Children findInSetIfExists(SFunction<T, ?> column, Object ancestorsValue) {
|
||||
// 调用getChildren()获取Children对象,并调用其findInSet方法
|
||||
// 查询条件为:ancestorsValue不为null时才进行查询
|
||||
getChildren().findInSet(ObjectUtil.isNotNull(ancestorsValue), column, ancestorsValue);
|
||||
// 返回Children对象以支持链式调用
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件执行实现 FIND_IN_SET 语句片段
|
||||
*
|
||||
* @param condition 是否执行该查询的条件
|
||||
* @param column 祖级列表字段
|
||||
* @param ancestorsValue 要查询的值
|
||||
*/
|
||||
default <T> Children findInSet(boolean condition, SFunction<T, ?> column, Object ancestorsValue) {
|
||||
if (condition) {
|
||||
getChildren().findInSet(column, ancestorsValue);
|
||||
}
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现 FIND_IN_SET 语句片段
|
||||
* 例如:根据父部门ID查询其所有子部门的列表
|
||||
* new MPJLambdaWrapper<SysDept>()
|
||||
* .select(SysDept::getDeptId)
|
||||
* .findInSet(SysDept::getAncestors, parentId)
|
||||
*
|
||||
* @param column 祖级列表字段
|
||||
* @param ancestorsValue 要查询的值
|
||||
*/
|
||||
default <T> Children findInSet(SFunction<T, ?> column, Object ancestorsValue) {
|
||||
getChildren().applyFunc(
|
||||
DataBaseHelper.findInSet(),
|
||||
arg -> arg.accept(column),
|
||||
ancestorsValue);
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 如果值存在才进行的时间范围查询
|
||||
*
|
||||
* @param timeFunc 时间查询字段
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
*/
|
||||
default <T> Children betweenIfExists(SFunction<T, ?> timeFunc, Object start, Object end) {
|
||||
if (ObjectUtil.isNotNull(start) && ObjectUtil.isNotNull(end)) {
|
||||
getChildren().between(timeFunc, start, end);
|
||||
return getChildren();
|
||||
}
|
||||
if (ObjectUtil.isNotNull(start)) {
|
||||
getChildren().ge(timeFunc, start);
|
||||
return getChildren();
|
||||
}
|
||||
if (ObjectUtil.isNotNull(end)) {
|
||||
getChildren().le(timeFunc, end);
|
||||
return getChildren();
|
||||
}
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间范围查询
|
||||
*
|
||||
* @param column 时间查询字段
|
||||
* @param values 时间范围
|
||||
*/
|
||||
default <T> Children betweenIfExists(SFunction<T, ?> column, Object[] values) {
|
||||
Object val1 = ArrayUtils.get(values, 0);
|
||||
Object val2 = ArrayUtils.get(values, 1);
|
||||
return betweenIfExists(column, val1, val2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当传入的集合不为空时才进行in查询
|
||||
*
|
||||
* @param column 查询字段
|
||||
* @param coll 集合
|
||||
*/
|
||||
default <T> Children inIfExists(SFunction<T, ?> column, Collection<?> coll) {
|
||||
if (ObjectUtil.isNotEmpty(coll)) {
|
||||
getChildren().in(column, coll);
|
||||
}
|
||||
return getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当传入的集合不为空时才进行not in查询
|
||||
*
|
||||
* @param column 列
|
||||
* @param coll 集合
|
||||
*/
|
||||
default <T> Children notInIfExists(SFunction<T, ?> column, Collection<?> coll) {
|
||||
if (ObjectUtil.isNotEmpty(coll)) {
|
||||
getChildren().notIn(column, coll);
|
||||
}
|
||||
return getChildren();
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
@ -331,4 +332,42 @@ public interface BaseMapperPlus<T, V> extends MPJBaseMapper<T> {
|
||||
return StreamUtils.toList(this.selectObjs(wrapper), mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询单个VO对象(MPJ版本可减少非VO字段查询)
|
||||
*
|
||||
* @param wrapper MPJ查询条件Wrapper
|
||||
* @return 查询到的单个VO对象
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
default V selectVoOne(MPJLambdaWrapper<T> wrapper) {
|
||||
// 查询两个类的交集字段
|
||||
wrapper.selectAsClass(this.currentModelClass(), this.currentVoClass());
|
||||
return selectJoinOne(this.currentVoClass(), wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询VO对象列表(MPJ版本可减少非VO字段查询)
|
||||
*
|
||||
* @param wrapper MPJ查询条件Wrapper
|
||||
* @return 查询到的VO对象列表
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
default List<V> selectVoList(MPJLambdaWrapper<T> wrapper) {
|
||||
wrapper.selectAsClass(this.currentModelClass(), this.currentVoClass());
|
||||
return this.selectJoinList(this.currentVoClass(), wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件分页查询VO对象列表(MPJ版本可减少非VO字段查询)
|
||||
*
|
||||
* @param page 分页信息
|
||||
* @param wrapper MPJ查询条件Wrapper
|
||||
* @return 查询到的VO对象分页列表
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
default <P extends IPage<V>> P selectVoPage(P page, MPJLambdaWrapper<T> wrapper) {
|
||||
wrapper.selectAsClass(this.currentModelClass(), this.currentVoClass());
|
||||
return this.selectJoinPage(page, this.currentVoClass(), wrapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@ package org.dromara.common.mybatis.helper;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
@ -81,4 +84,84 @@ public class DataBaseHelper {
|
||||
public static List<String> getDataSourceNameList() {
|
||||
return new ArrayList<>(DS.getDataSources().keySet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取findInSet所需的sql片段(使用MPJ的applyFunc来实现)
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
public static String findInSet() {
|
||||
return switch (getDataBaseType()) {
|
||||
case ORACLE -> "instr(','||%s||',', ',{0},') <> 0";
|
||||
case POSTGRE_SQL -> "strpos(','||%s||',', ',{0},') <> 0";
|
||||
case SQL_SERVER -> "charindex(',{0},', ','+%s+',') <> 0";
|
||||
default -> "find_in_set({0}, %s) > 0";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建不同数据库的JSON等值查询SQL模板(使用MPJ的applyFunc来实现)
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
public static <E> String buildJsonEqSql(SFunction<E, ?> jsonProperty) {
|
||||
String jsonPath = lambdaToJsonPath(jsonProperty);
|
||||
return switch (DataBaseHelper.getDataBaseType()) {
|
||||
case ORACLE -> "json_value(%s, '$" + jsonPath + "') = {0}";
|
||||
case POSTGRE_SQL -> "%s->>'" + jsonPath.replaceFirst("\\.", "") + "' = {0}";
|
||||
case SQL_SERVER -> "JSON_VALUE(%s, '$" + jsonPath + "') = {0}";
|
||||
default -> "%s->>'$" + jsonPath + "' = {0}";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建不同数据库的JSON模糊查询SQL模板(使用MPJ的applyFunc来实现)
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
public static <E> String buildJsonLikeSql(SFunction<E, ?> jsonProperty) {
|
||||
String jsonPath = lambdaToJsonPath(jsonProperty);
|
||||
return switch (DataBaseHelper.getDataBaseType()) {
|
||||
case ORACLE -> "json_value(%s, '$" + jsonPath + "') LIKE {0}";
|
||||
case POSTGRE_SQL -> "%s->>'" + jsonPath.replaceFirst("\\.", "") + "' LIKE {0}";
|
||||
case SQL_SERVER -> "JSON_VALUE(%s, '$" + jsonPath + "') LIKE {0}";
|
||||
default -> "%s->>'$" + jsonPath + "' LIKE {0}";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建不同数据库的JSON非空判断SQL模板(使用MPJ的applyFunc来实现)
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
public static <E> String buildJsonIsNotNullSql(SFunction<E, ?> jsonProperty) {
|
||||
String jsonPath = lambdaToJsonPath(jsonProperty);
|
||||
return switch (DataBaseHelper.getDataBaseType()) {
|
||||
case ORACLE -> "json_value(%s, '$" + jsonPath + "') IS NOT NULL";
|
||||
case POSTGRE_SQL -> "%s->>'" + jsonPath.replaceFirst("\\.", "") + "' IS NOT NULL";
|
||||
case SQL_SERVER -> "JSON_VALUE(%s, '$" + jsonPath + "') IS NOT NULL";
|
||||
default -> "%s->>'$" + jsonPath + "' IS NOT NULL";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心:Lambda 解析为 JSON 路径(复用原有逻辑,增加空值校验)
|
||||
* 示例:SysUser中UserExtInfo::getWorkNumber → .workNumber
|
||||
* @author LukewarmMe
|
||||
*/
|
||||
public static <E> String lambdaToJsonPath(SFunction<E, ?> jsonProperty) {
|
||||
LambdaMeta lambdaMeta = LambdaUtils.extract(jsonProperty);
|
||||
String methodName = lambdaMeta.getImplMethodName();
|
||||
String propertyName;
|
||||
if (methodName.startsWith("get")) {
|
||||
propertyName = methodName.substring(3);
|
||||
} else if (methodName.startsWith("is")) {
|
||||
propertyName = methodName.substring(2);
|
||||
} else if (methodName.startsWith("set")) {
|
||||
propertyName = methodName.substring(3);
|
||||
} else {
|
||||
throw new ServiceException("不支持的Lambda方法类型,仅支持get/is/set开头:" + methodName);
|
||||
}
|
||||
if (!propertyName.isEmpty()) {
|
||||
propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
|
||||
}
|
||||
return "." + propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user