mirror of
https://gitee.com/best_handsome/mybatis-plus-join
synced 2025-07-11 00:02:22 +08:00
优化分页
This commit is contained in:
parent
064f5ccaa9
commit
69713b1c39
@ -75,4 +75,7 @@ public interface IAdapter {
|
|||||||
}
|
}
|
||||||
return typeHandler;
|
return typeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void checkCollectionPage() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.github.yulichang.adapter.v33x;
|
|||||||
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||||
import com.github.yulichang.adapter.base.IAdapter;
|
import com.github.yulichang.adapter.base.IAdapter;
|
||||||
@ -87,4 +88,9 @@ public class Adapter33x implements IAdapter {
|
|||||||
}
|
}
|
||||||
return typeHandler;
|
return typeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCollectionPage() {
|
||||||
|
throw ExceptionUtils.mpe("page by main need MP version 3.5.6+, current version: " + MybatisPlusVersion.getVersion());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.github.yulichang.adapter.v3431;
|
|||||||
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||||
import com.github.yulichang.adapter.base.IAdapter;
|
import com.github.yulichang.adapter.base.IAdapter;
|
||||||
@ -60,4 +61,9 @@ public class Adapter3431 implements IAdapter {
|
|||||||
}
|
}
|
||||||
return typeHandler;
|
return typeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCollectionPage() {
|
||||||
|
throw ExceptionUtils.mpe("page by main need MP version 3.5.6+, current version: " + MybatisPlusVersion.getVersion());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.github.yulichang.adapter.v355;
|
package com.github.yulichang.adapter.v355;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||||
import com.github.yulichang.adapter.base.IAdapter;
|
import com.github.yulichang.adapter.base.IAdapter;
|
||||||
import com.github.yulichang.adapter.jsqlparser.v46.JSqlParserHelperV46;
|
import com.github.yulichang.adapter.jsqlparser.v46.JSqlParserHelperV46;
|
||||||
import org.apache.ibatis.session.Configuration;
|
import org.apache.ibatis.session.Configuration;
|
||||||
@ -29,4 +31,9 @@ public class Adapter355 implements IAdapter {
|
|||||||
}
|
}
|
||||||
return typeHandler;
|
return typeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCollectionPage() {
|
||||||
|
throw ExceptionUtils.mpe("page by main need MP version 3.5.6+, current version: " + MybatisPlusVersion.getVersion());
|
||||||
|
}
|
||||||
}
|
}
|
@ -34,7 +34,6 @@ public class MPJInterceptorConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void replaceInterceptorChain(List<SqlSessionFactory> sqlSessionFactoryList) {
|
private void replaceInterceptorChain(List<SqlSessionFactory> sqlSessionFactoryList) {
|
||||||
if (CollectionUtils.isEmpty(sqlSessionFactoryList)) {
|
if (CollectionUtils.isEmpty(sqlSessionFactoryList)) {
|
||||||
return;
|
return;
|
||||||
@ -46,6 +45,7 @@ public class MPJInterceptorConfig {
|
|||||||
InterceptorChain chain = (InterceptorChain) interceptorChain.get(factory.getConfiguration());
|
InterceptorChain chain = (InterceptorChain) interceptorChain.get(factory.getConfiguration());
|
||||||
Field interceptors = InterceptorChain.class.getDeclaredField("interceptors");
|
Field interceptors = InterceptorChain.class.getDeclaredField("interceptors");
|
||||||
interceptors.setAccessible(true);
|
interceptors.setAccessible(true);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
List<Interceptor> list = (List<Interceptor>) interceptors.get(chain);
|
List<Interceptor> list = (List<Interceptor>) interceptors.get(chain);
|
||||||
if (CollectionUtils.isEmpty(list)) {
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
interceptors.set(chain, new InterceptorList<>());
|
interceptors.set(chain, new InterceptorList<>());
|
||||||
|
@ -20,8 +20,10 @@ import com.github.yulichang.toolkit.TableHelper;
|
|||||||
import com.github.yulichang.toolkit.TableMap;
|
import com.github.yulichang.toolkit.TableMap;
|
||||||
import com.github.yulichang.toolkit.support.ColumnCache;
|
import com.github.yulichang.toolkit.support.ColumnCache;
|
||||||
import com.github.yulichang.wrapper.interfaces.MFunction;
|
import com.github.yulichang.wrapper.interfaces.MFunction;
|
||||||
|
import com.github.yulichang.wrapper.segments.PageInfo;
|
||||||
import com.github.yulichang.wrapper.segments.SelectCache;
|
import com.github.yulichang.wrapper.segments.SelectCache;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
@ -58,6 +60,12 @@ public abstract class AptAbstractWrapper<T, Children extends AptAbstractWrapper<
|
|||||||
protected boolean resultMap = false;
|
protected boolean resultMap = false;
|
||||||
@Getter
|
@Getter
|
||||||
protected boolean resultMapCollection = false;
|
protected boolean resultMapCollection = false;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
protected PageInfo pageInfo;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected boolean pageByMain = false;
|
||||||
/**
|
/**
|
||||||
* 表序号
|
* 表序号
|
||||||
*/
|
*/
|
||||||
@ -134,6 +142,29 @@ public abstract class AptAbstractWrapper<T, Children extends AptAbstractWrapper<
|
|||||||
initNeed();
|
initNeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain() {
|
||||||
|
this.pageByMain = true;
|
||||||
|
this.pageInfo = new PageInfo();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain(MFunction<PageInfo.PageInfoBuilder> function) {
|
||||||
|
this.pageByMain = true;
|
||||||
|
PageInfo.PageInfoBuilder apply = function.apply(PageInfo.builder());
|
||||||
|
this.pageInfo = apply.build();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfo getPageInfo() {
|
||||||
|
return pageInfo == null ? new PageInfo() : pageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置表别名
|
* 设置表别名
|
||||||
* 设置表别名注意sql注入问题
|
* 设置表别名注意sql注入问题
|
||||||
@ -405,6 +436,8 @@ public abstract class AptAbstractWrapper<T, Children extends AptAbstractWrapper<
|
|||||||
this.alias = ConfigProperties.tableAlias;
|
this.alias = ConfigProperties.tableAlias;
|
||||||
this.resultMap = false;
|
this.resultMap = false;
|
||||||
this.resultMapCollection = false;
|
this.resultMapCollection = false;
|
||||||
|
this.pageInfo = null;
|
||||||
|
this.pageByMain = false;
|
||||||
this.tableIndex = 1;
|
this.tableIndex = 1;
|
||||||
this.dynamicTableName = false;
|
this.dynamicTableName = false;
|
||||||
this.tableFunc = null;
|
this.tableFunc = null;
|
||||||
|
@ -14,9 +14,12 @@ import com.github.yulichang.extension.kt.interfaces.QueryJoin;
|
|||||||
import com.github.yulichang.toolkit.*;
|
import com.github.yulichang.toolkit.*;
|
||||||
import com.github.yulichang.toolkit.support.ColumnCache;
|
import com.github.yulichang.toolkit.support.ColumnCache;
|
||||||
import com.github.yulichang.wrapper.enums.PrefixEnum;
|
import com.github.yulichang.wrapper.enums.PrefixEnum;
|
||||||
|
import com.github.yulichang.wrapper.interfaces.MFunction;
|
||||||
|
import com.github.yulichang.wrapper.segments.PageInfo;
|
||||||
import com.github.yulichang.wrapper.segments.SelectCache;
|
import com.github.yulichang.wrapper.segments.SelectCache;
|
||||||
import kotlin.reflect.KProperty;
|
import kotlin.reflect.KProperty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
@ -57,6 +60,12 @@ public abstract class KtAbstractLambdaWrapper<T, Children extends KtAbstractLamb
|
|||||||
protected boolean resultMap = false;
|
protected boolean resultMap = false;
|
||||||
@Getter
|
@Getter
|
||||||
protected boolean resultMapCollection = false;
|
protected boolean resultMapCollection = false;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
protected PageInfo pageInfo;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected boolean pageByMain = false;
|
||||||
/**
|
/**
|
||||||
* 表序号
|
* 表序号
|
||||||
*/
|
*/
|
||||||
@ -168,6 +177,29 @@ public abstract class KtAbstractLambdaWrapper<T, Children extends KtAbstractLamb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain() {
|
||||||
|
this.pageByMain = true;
|
||||||
|
this.pageInfo = new PageInfo();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain(MFunction<PageInfo.PageInfoBuilder> function) {
|
||||||
|
this.pageByMain = true;
|
||||||
|
PageInfo.PageInfoBuilder apply = function.apply(PageInfo.builder());
|
||||||
|
this.pageInfo = apply.build();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfo getPageInfo() {
|
||||||
|
return pageInfo == null ? new PageInfo() : pageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置表别名
|
* 设置表别名
|
||||||
* 设置表别名注意sql注入问题
|
* 设置表别名注意sql注入问题
|
||||||
@ -448,6 +480,8 @@ public abstract class KtAbstractLambdaWrapper<T, Children extends KtAbstractLamb
|
|||||||
this.alias = ConfigProperties.tableAlias;
|
this.alias = ConfigProperties.tableAlias;
|
||||||
this.resultMap = false;
|
this.resultMap = false;
|
||||||
this.resultMapCollection = false;
|
this.resultMapCollection = false;
|
||||||
|
this.pageInfo = null;
|
||||||
|
this.pageByMain = false;
|
||||||
this.tableIndex = 1;
|
this.tableIndex = 1;
|
||||||
this.dynamicTableName = false;
|
this.dynamicTableName = false;
|
||||||
this.tableFunc = null;
|
this.tableFunc = null;
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package com.github.yulichang.interceptor;
|
package com.github.yulichang.interceptor;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
import com.baomidou.mybatisplus.core.toolkit.*;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
||||||
import com.github.yulichang.adapter.AdapterHelper;
|
import com.github.yulichang.adapter.AdapterHelper;
|
||||||
import com.github.yulichang.adapter.base.tookit.VersionUtils;
|
import com.github.yulichang.adapter.base.tookit.VersionUtils;
|
||||||
import com.github.yulichang.config.ConfigProperties;
|
import com.github.yulichang.config.ConfigProperties;
|
||||||
import com.github.yulichang.interfaces.MPJBaseJoin;
|
import com.github.yulichang.interfaces.MPJBaseJoin;
|
||||||
|
import com.github.yulichang.toolkit.ReflectionKit;
|
||||||
import com.github.yulichang.toolkit.*;
|
import com.github.yulichang.toolkit.*;
|
||||||
import com.github.yulichang.toolkit.support.FieldCache;
|
import com.github.yulichang.toolkit.support.FieldCache;
|
||||||
import com.github.yulichang.wrapper.interfaces.SelectWrapper;
|
import com.github.yulichang.wrapper.interfaces.SelectWrapper;
|
||||||
@ -20,6 +19,8 @@ import com.github.yulichang.wrapper.segments.SelectLabel;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.ibatis.executor.Executor;
|
import org.apache.ibatis.executor.Executor;
|
||||||
|
import org.apache.ibatis.logging.Log;
|
||||||
|
import org.apache.ibatis.logging.LogFactory;
|
||||||
import org.apache.ibatis.mapping.MappedStatement;
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
import org.apache.ibatis.mapping.ResultFlag;
|
import org.apache.ibatis.mapping.ResultFlag;
|
||||||
import org.apache.ibatis.mapping.ResultMap;
|
import org.apache.ibatis.mapping.ResultMap;
|
||||||
@ -50,6 +51,7 @@ public class MPJInterceptor implements Interceptor {
|
|||||||
private static final Map<String, Val> MS_MAPPER_CACHE = new ConcurrentHashMap<>();
|
private static final Map<String, Val> MS_MAPPER_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private static final Map<String, Val> RES_MAPPER_CACHE = new ConcurrentHashMap<>();
|
private static final Map<String, Val> RES_MAPPER_CACHE = new ConcurrentHashMap<>();
|
||||||
|
private static final Log log = LogFactory.getLog(MPJInterceptor.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object intercept(Invocation invocation) throws Throwable {
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
@ -75,7 +77,7 @@ public class MPJInterceptor implements Interceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Objects.nonNull(rt)) {
|
if (Objects.nonNull(rt)) {
|
||||||
args[0] = getMappedStatement(ms, rt, ew);
|
args[0] = getMappedStatement(ms, rt, ew, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +90,7 @@ public class MPJInterceptor implements Interceptor {
|
|||||||
/**
|
/**
|
||||||
* 获取MappedStatement
|
* 获取MappedStatement
|
||||||
*/
|
*/
|
||||||
public <E> MappedStatement getMappedStatement(MappedStatement ms, Class<?> resultType, Object ew) {
|
public <E> MappedStatement getMappedStatement(MappedStatement ms, Class<?> resultType, Object ew, Map<String, Object> map) {
|
||||||
if (ew instanceof SelectWrapper) {
|
if (ew instanceof SelectWrapper) {
|
||||||
SelectWrapper<E, ?> wrapper = (SelectWrapper<E, ?>) ew;
|
SelectWrapper<E, ?> wrapper = (SelectWrapper<E, ?>) ew;
|
||||||
if (wrapper.getEntityClass() == null) {
|
if (wrapper.getEntityClass() == null) {
|
||||||
@ -97,6 +99,19 @@ public class MPJInterceptor implements Interceptor {
|
|||||||
if (wrapper.getSelectColumns().isEmpty() && wrapper.getEntityClass() != null) {
|
if (wrapper.getSelectColumns().isEmpty() && wrapper.getEntityClass() != null) {
|
||||||
wrapper.selectAll();
|
wrapper.selectAll();
|
||||||
}
|
}
|
||||||
|
if (wrapper.isResultMapCollection()) {
|
||||||
|
if (map.values().stream().anyMatch(a -> a instanceof IPage)) {
|
||||||
|
if (wrapper.isPageByMain()) {
|
||||||
|
AdapterHelper.getAdapter().checkCollectionPage();
|
||||||
|
IPage<?> page = ParameterUtils.findPage(map).orElse(null);
|
||||||
|
wrapper.getPageInfo().setInnerPage(page);
|
||||||
|
map.entrySet().removeIf(entry -> entry.getValue() instanceof IPage);
|
||||||
|
} else {
|
||||||
|
// 一对多分页问题警告
|
||||||
|
log.warn("select one to many and page query will result in errors in the total count statistics, please use xml.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buildMappedStatement(ms, resultType, ew);
|
return buildMappedStatement(ms, resultType, ew);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.github.yulichang.interceptor.pagination;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||||
|
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
|
import net.sf.jsqlparser.schema.Column;
|
||||||
|
import net.sf.jsqlparser.schema.Table;
|
||||||
|
import net.sf.jsqlparser.statement.select.*;
|
||||||
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
|
import org.apache.ibatis.mapping.ParameterMapping;
|
||||||
|
import org.apache.ibatis.mapping.SqlSource;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yulichang
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
public class DialectWrapper {
|
||||||
|
|
||||||
|
private final IDialect dialect;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final List<ParameterMapping> fullMappings = new ArrayList<>();
|
||||||
|
@Getter
|
||||||
|
private final List<ParameterMapping> pageMappings = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private String finallySql;
|
||||||
|
|
||||||
|
public DialectWrapper(IDialect dialect) {
|
||||||
|
this.dialect = dialect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildPaginationSql(String originalSql, List<ParameterMapping> mappings, long offset, long limit,
|
||||||
|
Consumer<DialectModel> consumers, MappedStatement ms, Object parameter) {
|
||||||
|
try {
|
||||||
|
// 解析sql
|
||||||
|
Select select = (Select) JsqlParserGlobal.parse(originalSql);
|
||||||
|
if (select instanceof SetOperationList) {
|
||||||
|
throw ExceptionUtils.mpe("不支持 union 对多分页");
|
||||||
|
}
|
||||||
|
//获取 ? 出现的次数
|
||||||
|
String sourceSql = select.toString();
|
||||||
|
int count = ParseHelper.countChar(sourceSql);
|
||||||
|
String formatSql;
|
||||||
|
if (count == mappings.size()) {
|
||||||
|
formatSql = ParseHelper.decode(sourceSql);
|
||||||
|
} else {
|
||||||
|
SqlSource sqlSource = ms.getSqlSource();
|
||||||
|
if (sqlSource instanceof DynamicSqlSource) {
|
||||||
|
formatSql = ParseHelper.getOriginalSql(parameter, (DynamicSqlSource) sqlSource);
|
||||||
|
} else {
|
||||||
|
throw ExceptionUtils.mpe("unknown type: " + sqlSource.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//分页的sql
|
||||||
|
PlainSelect pageSql = (PlainSelect) JsqlParserGlobal.parse(formatSql);
|
||||||
|
|
||||||
|
List<Join> joins = pageSql.getJoins();
|
||||||
|
if (CollectionUtils.isNotEmpty(joins)) {
|
||||||
|
pageSql.setJoins(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSql.setSelectItems(Collections.singletonList(new SelectItem<>(new Column().withColumnName("*"))));
|
||||||
|
|
||||||
|
// 替换回 ? 同步mappings顺序
|
||||||
|
String repSql = pageSql.toString();
|
||||||
|
Map<Integer, ParameterMapping> sortMap = new HashMap<>();
|
||||||
|
repSql = ParseHelper.encode(mappings, count, repSql, sortMap);
|
||||||
|
this.pageMappings.addAll(sortMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
DialectModel dialectModel = dialect.buildPaginationSql(repSql, offset, limit);
|
||||||
|
consumers.accept(dialectModel);
|
||||||
|
|
||||||
|
// 带分页方言的sql
|
||||||
|
String formatDialectSql = ParseHelper.decode(dialectModel.getDialectSql(), i -> "s" + i);
|
||||||
|
|
||||||
|
//完整的sql
|
||||||
|
PlainSelect fullSql = (PlainSelect) JsqlParserGlobal.parse(formatSql);
|
||||||
|
|
||||||
|
PlainSelect finalPlainSelect = new PlainSelect();
|
||||||
|
finalPlainSelect.setSelectItems(fullSql.getSelectItems());
|
||||||
|
finalPlainSelect.setDistinct(fullSql.getDistinct());
|
||||||
|
finalPlainSelect.setFromItem(
|
||||||
|
new Table()
|
||||||
|
.withName("(" + formatDialectSql + ")")
|
||||||
|
.withAlias(new Alias(fullSql.getFromItem().getAlias().getName(), false)));
|
||||||
|
finalPlainSelect.setJoins(fullSql.getJoins());
|
||||||
|
|
||||||
|
String finalSql = finalPlainSelect.toString();
|
||||||
|
Map<Integer, ParameterMapping> finalSortMap = new HashMap<>();
|
||||||
|
//page部分
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String repStr = ParseHelper.format.apply("s" + i);
|
||||||
|
int i1 = finalSql.indexOf(repStr);
|
||||||
|
if (i1 != -1) {
|
||||||
|
finalSql = finalSql.replace(repStr, "?");
|
||||||
|
finalSortMap.put(i1, this.pageMappings.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//其他部分
|
||||||
|
finalSql = ParseHelper.encode(mappings, count, finalSql, finalSortMap);
|
||||||
|
this.fullMappings.addAll(finalSortMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList()));
|
||||||
|
this.finallySql = finalSql;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw ExceptionUtils.mpe("not support this sql, please use xml. error sql: " + originalSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,252 @@
|
|||||||
|
package com.github.yulichang.interceptor.pagination;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||||
|
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
|
||||||
|
import com.github.yulichang.wrapper.interfaces.SelectWrapper;
|
||||||
|
import net.sf.jsqlparser.JSQLParserException;
|
||||||
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import net.sf.jsqlparser.schema.Column;
|
||||||
|
import net.sf.jsqlparser.statement.select.*;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.ibatis.cache.CacheKey;
|
||||||
|
import org.apache.ibatis.executor.Executor;
|
||||||
|
import org.apache.ibatis.mapping.BoundSql;
|
||||||
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
|
import org.apache.ibatis.mapping.ParameterMapping;
|
||||||
|
import org.apache.ibatis.mapping.SqlSource;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
import org.apache.ibatis.session.ResultHandler;
|
||||||
|
import org.apache.ibatis.session.RowBounds;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yulichang
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
public class PageInnerInterceptor extends PaginationInnerInterceptor {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(PageInnerInterceptor.class);
|
||||||
|
|
||||||
|
public PageInnerInterceptor(PaginationInnerInterceptor pagination) {
|
||||||
|
super.setOptimizeJoin(true);
|
||||||
|
super.setDbType(pagination.getDbType());
|
||||||
|
super.setDialect(pagination.getDialect());
|
||||||
|
super.setOverflow(pagination.isOverflow());
|
||||||
|
super.setMaxLimit(pagination.getMaxLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行count
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
|
||||||
|
//没有wrapper 或者 不是对多查询 不做处理
|
||||||
|
SelectWrapper<?, ?> wrapper = findMPJWrapper(parameter).orElse(null);
|
||||||
|
if (wrapper == null || !wrapper.isPageByMain()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// copy super
|
||||||
|
IPage<?> page = wrapper.getPageInfo().getInnerPage();
|
||||||
|
if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundSql countSql;
|
||||||
|
MappedStatement countMs = buildCountMappedStatement(ms, page.countId());
|
||||||
|
if (countMs != null) {
|
||||||
|
countSql = countMs.getBoundSql(parameter);
|
||||||
|
} else {
|
||||||
|
countMs = buildAutoCountMappedStatement(ms);
|
||||||
|
ArrayList<ParameterMapping> mappingArrayList = new ArrayList<>(boundSql.getParameterMappings());
|
||||||
|
String countSqlStr = autoCountSql(boundSql.getSql(), mappingArrayList, ms, parameter, wrapper);
|
||||||
|
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
|
||||||
|
countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mappingArrayList, parameter);
|
||||||
|
PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
|
||||||
|
List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);
|
||||||
|
long total = 0;
|
||||||
|
if (CollectionUtils.isNotEmpty(result)) {
|
||||||
|
// 个别数据库 count 没数据不会返回 0
|
||||||
|
Object o = result.get(0);
|
||||||
|
if (o != null) {
|
||||||
|
total = Long.parseLong(o.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.setTotal(total);
|
||||||
|
return continuePage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加分页方言
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||||
|
SelectWrapper<?, ?> wrapper = findMPJWrapper(parameter).orElse(null);
|
||||||
|
if (wrapper == null || !wrapper.isPageByMain()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// copy super
|
||||||
|
IPage<?> page = wrapper.getPageInfo().getInnerPage();
|
||||||
|
if (null == page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 orderBy 拼接
|
||||||
|
boolean addOrdered = false;
|
||||||
|
String buildSql = boundSql.getSql();
|
||||||
|
List<OrderItem> orders = page.orders();
|
||||||
|
if (CollectionUtils.isNotEmpty(orders)) {
|
||||||
|
addOrdered = true;
|
||||||
|
buildSql = this.concatOrderBy(buildSql, orders);
|
||||||
|
}
|
||||||
|
|
||||||
|
// size 小于 0 且不限制返回值则不构造分页sql
|
||||||
|
Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;
|
||||||
|
if (page.getSize() < 0 && null == _limit) {
|
||||||
|
if (addOrdered) {
|
||||||
|
PluginUtils.mpBoundSql(boundSql).sql(buildSql);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerLimit(page, _limit);
|
||||||
|
DialectWrapper dialect = findMPJDialect(executor);
|
||||||
|
|
||||||
|
final Configuration configuration = ms.getConfiguration();
|
||||||
|
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
|
||||||
|
Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();
|
||||||
|
dialect.buildPaginationSql(buildSql, boundSql.getParameterMappings(), page.offset(), page.getSize(),
|
||||||
|
c -> c.consumers(dialect.getPageMappings(), configuration, additionalParameter),
|
||||||
|
ms, parameter);
|
||||||
|
mpBoundSql.sql(dialect.getFinallySql());
|
||||||
|
mpBoundSql.parameterMappings(dialect.getFullMappings());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProperties(Properties properties) {
|
||||||
|
super.setProperties(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String autoCountSql(String sql, List<ParameterMapping> mappings, MappedStatement ms, Object parameter, SelectWrapper<?, ?> wrapper) {
|
||||||
|
try {
|
||||||
|
Select select = (Select) JsqlParserGlobal.parse(sql);
|
||||||
|
if (select instanceof SetOperationList) {
|
||||||
|
throw ExceptionUtils.mpe("不支持 union 对多分页");
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取 ? 出现的次数
|
||||||
|
String sourceSql = select.toString();
|
||||||
|
int count = ParseHelper.countChar(sourceSql);
|
||||||
|
|
||||||
|
String formatSql;
|
||||||
|
if (count == mappings.size()) {
|
||||||
|
formatSql = ParseHelper.decode(sourceSql);
|
||||||
|
} else {
|
||||||
|
SqlSource sqlSource = ms.getSqlSource();
|
||||||
|
if (sqlSource instanceof DynamicSqlSource) {
|
||||||
|
formatSql = ParseHelper.getOriginalSql(parameter, (DynamicSqlSource) sqlSource);
|
||||||
|
} else {
|
||||||
|
log.error("unknown type: " + sqlSource.getClass().getName());
|
||||||
|
throw ExceptionUtils.mpe("not support this sql, please use xml. error sql: " + sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlainSelect ps = (PlainSelect) JsqlParserGlobal.parse(formatSql);
|
||||||
|
|
||||||
|
// 优化 order by 在非分组情况下
|
||||||
|
List<OrderByElement> orderBy = ps.getOrderByElements();
|
||||||
|
if (CollectionUtils.isNotEmpty(orderBy)) {
|
||||||
|
boolean canClean = true;
|
||||||
|
for (OrderByElement order : orderBy) {
|
||||||
|
// order by 里带参数,不去除order by
|
||||||
|
Expression expression = order.getExpression();
|
||||||
|
if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {
|
||||||
|
canClean = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canClean) {
|
||||||
|
ps.setOrderByElements(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Join> joins = ps.getJoins();
|
||||||
|
if (CollectionUtils.isNotEmpty(joins)) {
|
||||||
|
ps.setJoins(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String repSql;
|
||||||
|
Distinct distinct = ps.getDistinct();
|
||||||
|
GroupByElement groupBy = ps.getGroupBy();
|
||||||
|
String mainAlias = Optional.of(ps.getFromItem().getAlias().getName()).orElse("");
|
||||||
|
// 包含 distinct、groupBy 不优化
|
||||||
|
if (null != distinct || null != groupBy) {
|
||||||
|
if (wrapper.getPageInfo().getCountSelectSql() != null) {
|
||||||
|
ps.setSelectItems(Collections.singletonList(new SelectItem<>(new Column().withColumnName(wrapper.getPageInfo().getCountSelectSql()))));
|
||||||
|
} else {
|
||||||
|
ps.getSelectItems().removeIf(s -> {
|
||||||
|
if (s.getExpression() instanceof Column) {
|
||||||
|
Column column = (Column) s.getExpression();
|
||||||
|
return !mainAlias.equals(column.getTable().toString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
repSql = lowLevelCountSql(ps.toString());
|
||||||
|
} else {
|
||||||
|
// 优化 SQL
|
||||||
|
ps.setSelectItems(COUNT_SELECT_ITEM);
|
||||||
|
repSql = ps.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换回 ? 同步mappings顺序
|
||||||
|
Map<Integer, ParameterMapping> sortMap = new HashMap<>();
|
||||||
|
repSql = ParseHelper.encode(mappings, count, repSql, sortMap);
|
||||||
|
mappings.clear();
|
||||||
|
mappings.addAll(sortMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList()));
|
||||||
|
return repSql;
|
||||||
|
} catch (JSQLParserException e) {
|
||||||
|
logger.error("optimize this sql to a count sql has exception, sql:\"" + sql + "\", exception:\n" + e.getCause());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("optimize this sql to a count sql has error, sql:\"" + sql + "\", exception:\n" + e);
|
||||||
|
}
|
||||||
|
throw ExceptionUtils.mpe("not support this sql, please use xml. error sql: " + sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DialectWrapper findMPJDialect(Executor executor) {
|
||||||
|
IDialect dialect = super.findIDialect(executor);
|
||||||
|
return new DialectWrapper(dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<SelectWrapper<?, ?>> findMPJWrapper(Object parameterObject) {
|
||||||
|
if (parameterObject != null) {
|
||||||
|
if (parameterObject instanceof Map) {
|
||||||
|
Map<?, ?> parameterMap = (Map<?, ?>) parameterObject;
|
||||||
|
for (Map.Entry<?, ?> entry : parameterMap.entrySet()) {
|
||||||
|
if (entry.getValue() != null && entry.getValue() instanceof SelectWrapper) {
|
||||||
|
return Optional.of((SelectWrapper<?, ?>) entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (parameterObject instanceof SelectWrapper) {
|
||||||
|
return Optional.of((SelectWrapper<?, ?>) parameterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package com.github.yulichang.interceptor.pagination;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Assert;
|
||||||
|
import com.github.yulichang.toolkit.ReflectionKit;
|
||||||
|
import org.apache.ibatis.mapping.ParameterMapping;
|
||||||
|
import org.apache.ibatis.mapping.SqlSource;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.SqlNode;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql 分页解析工具类
|
||||||
|
*
|
||||||
|
* @author yulichang
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class ParseHelper {
|
||||||
|
|
||||||
|
private static final Map<SqlSource, SqlSourceWrapper> SQL_SOURCE_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final String prefix = "MPJ_Param_i_";
|
||||||
|
private static final String suffix = "_MPJ_Param_i";
|
||||||
|
private static final char placeholder_char = '?';
|
||||||
|
private static final String placeholder_str = String.valueOf(placeholder_char);
|
||||||
|
|
||||||
|
public static final Function<Object, String> format = index -> prefix + index + suffix;
|
||||||
|
|
||||||
|
public static int countChar(String str) {
|
||||||
|
return countChar(str, placeholder_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int countChar(String str, String tag) {
|
||||||
|
return countChar(str, tag.charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int countChar(String str, char tag) {
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
if (str.charAt(i) == tag) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(String str) {
|
||||||
|
return decode(str, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(String str, Function<Object, String> formatter) {
|
||||||
|
return decode(str, formatter, placeholder_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(String str, Function<Object, String> formatter, char placeholder) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
if (str.charAt(i) == placeholder) {
|
||||||
|
sb.append(format.apply(formatter == null ? count : formatter.apply(count)));
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
sb.append(str.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(String str, Function<Object, String> formatter, String placeholder) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String[] split = str.split(placeholder);
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
sb.append(split[i]);
|
||||||
|
if (i < split.length - 1) {
|
||||||
|
sb.append(format.apply(formatter == null ? i : formatter.apply(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(List<ParameterMapping> mappings, int count, String repSql, Map<Integer, ParameterMapping> sortMap) {
|
||||||
|
return encode(mappings, count, repSql, sortMap, placeholder_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(List<ParameterMapping> mappings, int count, String repSql, Map<Integer, ParameterMapping> sortMap, String placeholder) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String repStr = ParseHelper.format.apply(i);
|
||||||
|
int i1 = repSql.indexOf(repStr);
|
||||||
|
if (i1 != -1) {
|
||||||
|
repSql = repSql.replace(repStr, placeholder);
|
||||||
|
sortMap.put(i1, mappings.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getOriginalSql(Object parameter, DynamicSqlSource sqlSource) {
|
||||||
|
Assert.notNull(sqlSource, "sqlSource must not be null");
|
||||||
|
SqlSourceWrapper sqlSourceWrapper = SQL_SOURCE_CACHE.computeIfAbsent(sqlSource, key -> {
|
||||||
|
Configuration configuration = ReflectionKit.getFieldValue(sqlSource, "configuration");
|
||||||
|
SqlNode sqlNode = ReflectionKit.getFieldValue(sqlSource, "rootSqlNode");
|
||||||
|
return new SqlSourceWrapper(configuration, sqlNode);
|
||||||
|
});
|
||||||
|
return ParseHelper.decode(sqlSourceWrapper.getSql(parameter), null, ParseHelper.format.apply(""));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
package com.github.yulichang.interceptor.pagination;
|
||||||
|
|
||||||
|
import org.apache.ibatis.builder.BaseBuilder;
|
||||||
|
import org.apache.ibatis.builder.BuilderException;
|
||||||
|
import org.apache.ibatis.builder.ParameterExpression;
|
||||||
|
import org.apache.ibatis.builder.SqlSourceBuilder;
|
||||||
|
import org.apache.ibatis.mapping.ParameterMapping;
|
||||||
|
import org.apache.ibatis.parsing.GenericTokenParser;
|
||||||
|
import org.apache.ibatis.parsing.TokenHandler;
|
||||||
|
import org.apache.ibatis.reflection.MetaClass;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.DynamicContext;
|
||||||
|
import org.apache.ibatis.scripting.xmltags.SqlNode;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yulichang
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
public class SqlSourceWrapper {
|
||||||
|
|
||||||
|
private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
|
||||||
|
|
||||||
|
private final Configuration configuration;
|
||||||
|
private final SqlNode rootSqlNode;
|
||||||
|
|
||||||
|
public SqlSourceWrapper(Configuration configuration, SqlNode rootSqlNode) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.rootSqlNode = rootSqlNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getSql(Object parameterObject) {
|
||||||
|
DynamicContext context = new DynamicContext(configuration, parameterObject);
|
||||||
|
rootSqlNode.apply(context);
|
||||||
|
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
|
||||||
|
return parse(context.getSql(), parameterType, context.getBindings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
|
||||||
|
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType,
|
||||||
|
additionalParameters) {
|
||||||
|
@Override
|
||||||
|
public String handleToken(String content) {
|
||||||
|
super.handleToken(content);
|
||||||
|
return ParseHelper.format.apply("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
|
||||||
|
String sql;
|
||||||
|
if (configuration.isShrinkWhitespacesInSql()) {
|
||||||
|
sql = parser.parse(SqlSourceBuilder.removeExtraWhitespaces(originalSql));
|
||||||
|
} else {
|
||||||
|
sql = parser.parse(originalSql);
|
||||||
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy {@link org.apache.ibatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
|
||||||
|
|
||||||
|
private final List<ParameterMapping> parameterMappings = new ArrayList<>();
|
||||||
|
private final Class<?> parameterType;
|
||||||
|
private final MetaObject metaParameters;
|
||||||
|
|
||||||
|
public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType,
|
||||||
|
Map<String, Object> additionalParameters) {
|
||||||
|
super(configuration);
|
||||||
|
this.parameterType = parameterType;
|
||||||
|
this.metaParameters = configuration.newMetaObject(additionalParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ParameterMapping> getParameterMappings() {
|
||||||
|
return parameterMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String handleToken(String content) {
|
||||||
|
parameterMappings.add(buildParameterMapping(content));
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParameterMapping buildParameterMapping(String content) {
|
||||||
|
Map<String, String> propertiesMap = parseParameterMapping(content);
|
||||||
|
String property = propertiesMap.get("property");
|
||||||
|
Class<?> propertyType;
|
||||||
|
if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
|
||||||
|
propertyType = metaParameters.getGetterType(property);
|
||||||
|
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
|
||||||
|
propertyType = parameterType;
|
||||||
|
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
|
||||||
|
propertyType = java.sql.ResultSet.class;
|
||||||
|
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
|
||||||
|
propertyType = Object.class;
|
||||||
|
} else {
|
||||||
|
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
|
||||||
|
if (metaClass.hasGetter(property)) {
|
||||||
|
propertyType = metaClass.getGetterType(property);
|
||||||
|
} else {
|
||||||
|
propertyType = Object.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
|
||||||
|
Class<?> javaType = propertyType;
|
||||||
|
String typeHandlerAlias = null;
|
||||||
|
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
if ("javaType".equals(name)) {
|
||||||
|
javaType = resolveClass(value);
|
||||||
|
builder.javaType(javaType);
|
||||||
|
} else if ("jdbcType".equals(name)) {
|
||||||
|
builder.jdbcType(resolveJdbcType(value));
|
||||||
|
} else if ("mode".equals(name)) {
|
||||||
|
builder.mode(resolveParameterMode(value));
|
||||||
|
} else if ("numericScale".equals(name)) {
|
||||||
|
builder.numericScale(Integer.valueOf(value));
|
||||||
|
} else if ("resultMap".equals(name)) {
|
||||||
|
builder.resultMapId(value);
|
||||||
|
} else if ("typeHandler".equals(name)) {
|
||||||
|
typeHandlerAlias = value;
|
||||||
|
} else if ("jdbcTypeName".equals(name)) {
|
||||||
|
builder.jdbcTypeName(value);
|
||||||
|
} else if ("property".equals(name)) {
|
||||||
|
// Do Nothing
|
||||||
|
} else if ("expression".equals(name)) {
|
||||||
|
throw new BuilderException("Expression based parameters are not supported yet");
|
||||||
|
} else {
|
||||||
|
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content
|
||||||
|
+ "}. Valid properties are " + PARAMETER_PROPERTIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeHandlerAlias != null) {
|
||||||
|
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> parseParameterMapping(String content) {
|
||||||
|
try {
|
||||||
|
return new ParameterExpression(content);
|
||||||
|
} catch (BuilderException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new BuilderException("Parsing error was found in mapping #{" + content
|
||||||
|
+ "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,10 +1,19 @@
|
|||||||
package com.github.yulichang.toolkit;
|
package com.github.yulichang.toolkit;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import com.github.yulichang.adapter.AdapterHelper;
|
||||||
import com.github.yulichang.interceptor.MPJInterceptor;
|
import com.github.yulichang.interceptor.MPJInterceptor;
|
||||||
|
import com.github.yulichang.interceptor.pagination.PageInnerInterceptor;
|
||||||
|
import org.apache.ibatis.logging.Log;
|
||||||
|
import org.apache.ibatis.logging.LogFactory;
|
||||||
import org.apache.ibatis.plugin.Interceptor;
|
import org.apache.ibatis.plugin.Interceptor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,6 +26,8 @@ import java.util.function.Predicate;
|
|||||||
*/
|
*/
|
||||||
public class InterceptorList<E extends Interceptor> extends ArrayList<E> {
|
public class InterceptorList<E extends Interceptor> extends ArrayList<E> {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(InterceptorList.class);
|
||||||
|
|
||||||
public InterceptorList() {
|
public InterceptorList() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -29,6 +40,41 @@ public class InterceptorList<E extends Interceptor> extends ArrayList<E> {
|
|||||||
super.removeIf(predicate);
|
super.removeIf(predicate);
|
||||||
super.add(mpjInterceptor);
|
super.add(mpjInterceptor);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
AdapterHelper.getAdapter().checkCollectionPage();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.stream().anyMatch(i -> i instanceof MybatisPlusInterceptor)) {
|
||||||
|
MybatisPlusInterceptor mybatisPlusInterceptor = super.stream().filter(i -> i instanceof MybatisPlusInterceptor)
|
||||||
|
.map(i -> (MybatisPlusInterceptor) i).findFirst().orElse(null);
|
||||||
|
if (mybatisPlusInterceptor != null) {
|
||||||
|
try {
|
||||||
|
Field field = MybatisPlusInterceptor.class.getDeclaredField("interceptors");
|
||||||
|
field.setAccessible(true);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<InnerInterceptor> interceptors = (List<InnerInterceptor>) field.get(mybatisPlusInterceptor);
|
||||||
|
|
||||||
|
Integer index = null;
|
||||||
|
PaginationInnerInterceptor paginationInnerInterceptor = null;
|
||||||
|
if (interceptors.stream().noneMatch(i -> i instanceof PageInnerInterceptor)) {
|
||||||
|
for (int i = 0; i < interceptors.size(); i++) {
|
||||||
|
InnerInterceptor innerInterceptor = interceptors.get(i);
|
||||||
|
if (innerInterceptor instanceof PaginationInnerInterceptor) {
|
||||||
|
paginationInnerInterceptor = (PaginationInnerInterceptor) innerInterceptor;
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index != null) {
|
||||||
|
interceptors.add(index + 1, new PageInnerInterceptor(paginationInnerInterceptor));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,16 +71,13 @@ public final class ReflectionKit {
|
|||||||
* @param fieldName 字段名称
|
* @param fieldName 字段名称
|
||||||
* @return 属性值
|
* @return 属性值
|
||||||
*/
|
*/
|
||||||
public static Object getFieldValue(Object entity, String fieldName) {
|
public static <T> T getFieldValue(Object entity, String fieldName) {
|
||||||
Class<?> cls = entity.getClass();
|
|
||||||
Map<String, Field> fieldMaps = getFieldMap(cls);
|
|
||||||
try {
|
try {
|
||||||
Field field = fieldMaps.get(fieldName);
|
Field field = entity.getClass().getDeclaredField(fieldName);
|
||||||
Assert.notNull(field, "Error: NoSuchField in %s for %s. Cause:", cls.getSimpleName(), fieldName);
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return field.get(entity);
|
return (T) field.get(entity);
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
throw ExceptionUtils.mpe("Error: Cannot read field in %s. Cause:", e, cls.getSimpleName());
|
throw ExceptionUtils.mpe("Error: Cannot read field in %s. Cause:", e, entity.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,12 @@ import com.github.yulichang.config.enums.LogicDelTypeEnum;
|
|||||||
import com.github.yulichang.toolkit.*;
|
import com.github.yulichang.toolkit.*;
|
||||||
import com.github.yulichang.toolkit.support.ColumnCache;
|
import com.github.yulichang.toolkit.support.ColumnCache;
|
||||||
import com.github.yulichang.wrapper.enums.PrefixEnum;
|
import com.github.yulichang.wrapper.enums.PrefixEnum;
|
||||||
|
import com.github.yulichang.wrapper.interfaces.MFunction;
|
||||||
import com.github.yulichang.wrapper.interfaces.QueryJoin;
|
import com.github.yulichang.wrapper.interfaces.QueryJoin;
|
||||||
|
import com.github.yulichang.wrapper.segments.PageInfo;
|
||||||
import com.github.yulichang.wrapper.segments.SelectCache;
|
import com.github.yulichang.wrapper.segments.SelectCache;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
@ -57,6 +60,12 @@ public abstract class JoinAbstractLambdaWrapper<T, Children extends JoinAbstract
|
|||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
protected boolean resultMapCollection = false;
|
protected boolean resultMapCollection = false;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
protected PageInfo pageInfo;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected boolean pageByMain = false;
|
||||||
/**
|
/**
|
||||||
* 表序号
|
* 表序号
|
||||||
*/
|
*/
|
||||||
@ -168,6 +177,29 @@ public abstract class JoinAbstractLambdaWrapper<T, Children extends JoinAbstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain() {
|
||||||
|
this.pageByMain = true;
|
||||||
|
this.pageInfo = new PageInfo();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主表分页
|
||||||
|
*/
|
||||||
|
public Children pageByMain(MFunction<PageInfo.PageInfoBuilder> function) {
|
||||||
|
this.pageByMain = true;
|
||||||
|
PageInfo.PageInfoBuilder apply = function.apply(PageInfo.builder());
|
||||||
|
this.pageInfo = apply.build();
|
||||||
|
return typedThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfo getPageInfo() {
|
||||||
|
return pageInfo == null ? new PageInfo() : pageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置表别名
|
* 设置表别名
|
||||||
* 设置表别名注意sql注入问题
|
* 设置表别名注意sql注入问题
|
||||||
@ -475,6 +507,8 @@ public abstract class JoinAbstractLambdaWrapper<T, Children extends JoinAbstract
|
|||||||
this.alias = ConfigProperties.tableAlias;
|
this.alias = ConfigProperties.tableAlias;
|
||||||
this.resultMap = false;
|
this.resultMap = false;
|
||||||
this.resultMapCollection = false;
|
this.resultMapCollection = false;
|
||||||
|
this.pageInfo = null;
|
||||||
|
this.pageByMain = false;
|
||||||
this.tableIndex = 1;
|
this.tableIndex = 1;
|
||||||
this.dynamicTableName = false;
|
this.dynamicTableName = false;
|
||||||
this.tableFunc = null;
|
this.tableFunc = null;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.yulichang.wrapper.interfaces;
|
package com.github.yulichang.wrapper.interfaces;
|
||||||
|
|
||||||
import com.github.yulichang.wrapper.resultmap.Label;
|
import com.github.yulichang.wrapper.resultmap.Label;
|
||||||
|
import com.github.yulichang.wrapper.segments.PageInfo;
|
||||||
import com.github.yulichang.wrapper.segments.Select;
|
import com.github.yulichang.wrapper.segments.Select;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -26,4 +27,10 @@ public interface SelectWrapper<Entity, Children> {
|
|||||||
String getFrom();
|
String getFrom();
|
||||||
|
|
||||||
String getAlias();
|
String getAlias();
|
||||||
|
|
||||||
|
boolean isResultMapCollection();
|
||||||
|
|
||||||
|
PageInfo getPageInfo();
|
||||||
|
|
||||||
|
boolean isPageByMain();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.github.yulichang.wrapper.segments;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yulichang
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PageInfo implements Serializable {
|
||||||
|
|
||||||
|
private IPage<?> innerPage;
|
||||||
|
|
||||||
|
private String countSelectSql;
|
||||||
|
|
||||||
|
public static PageInfoBuilder builder() {
|
||||||
|
return new PageInfoBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class PageInfoBuilder {
|
||||||
|
private IPage<?> innerPage;
|
||||||
|
private String countSelectSql;
|
||||||
|
|
||||||
|
PageInfoBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfoBuilder innerPage(IPage<?> innerPage) {
|
||||||
|
this.innerPage = innerPage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfoBuilder countSelectSql(String countSelectSql) {
|
||||||
|
this.countSelectSql = countSelectSql;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageInfo build() {
|
||||||
|
return new PageInfo(this.innerPage, this.countSelectSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -51,8 +51,6 @@ public class MybatisPlusConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
PaginationInnerInterceptor page = new PaginationInnerInterceptor();
|
|
||||||
page.setOptimizeJoin(false);
|
|
||||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
|
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
|
||||||
@Override
|
@Override
|
||||||
public Expression getTenantId() {
|
public Expression getTenantId() {
|
||||||
@ -64,7 +62,11 @@ public class MybatisPlusConfig {
|
|||||||
return !tableName.startsWith("user_tenant");
|
return !tableName.startsWith("user_tenant");
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
PaginationInnerInterceptor page = new PaginationInnerInterceptor();
|
||||||
|
page.setOptimizeJoin(false);
|
||||||
interceptor.addInnerInterceptor(page);
|
interceptor.addInnerInterceptor(page);
|
||||||
|
|
||||||
interceptor.addInnerInterceptor(new SqlInterceptor());
|
interceptor.addInnerInterceptor(new SqlInterceptor());
|
||||||
return interceptor;
|
return interceptor;
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ mybatis-plus:
|
|||||||
logic-delete-value: true
|
logic-delete-value: true
|
||||||
logic-not-delete-value: false
|
logic-not-delete-value: false
|
||||||
banner: true
|
banner: true
|
||||||
# configuration:
|
configuration:
|
||||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
mybatis-plus-join:
|
mybatis-plus-join:
|
||||||
# 打印 mybatis plus join banner
|
# 打印 mybatis plus join banner
|
||||||
@ -26,9 +26,9 @@ mybatis-plus-join:
|
|||||||
logic-del-type: where
|
logic-del-type: where
|
||||||
|
|
||||||
|
|
||||||
logging:
|
#logging:
|
||||||
level:
|
# level:
|
||||||
com.github.yulichang: debug
|
# com.github.yulichang: debug
|
||||||
|
|
||||||
---
|
---
|
||||||
spring:
|
spring:
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.github.yulichang.test.join.unit;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.github.yulichang.test.join.dto.UserDTO;
|
||||||
|
import com.github.yulichang.test.join.entity.AddressDO;
|
||||||
|
import com.github.yulichang.test.join.entity.UserDO;
|
||||||
|
import com.github.yulichang.test.util.ThreadLocalUtils;
|
||||||
|
import com.github.yulichang.toolkit.JoinWrappers;
|
||||||
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
public class PageByMainTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageByMain() {
|
||||||
|
ThreadLocalUtils.set("SELECT t.id, t.pid, t.`name`, t.`json`, t.sex, t.head_img, t.create_time, t.address_id, " +
|
||||||
|
"t.address_id2, t.del, t.create_by, t.update_by, t1.id AS joina_id, t1.user_id, t1.area_id, t1.tel, " +
|
||||||
|
"t1.address, t1.del AS joina_del FROM (SELECT * FROM `user` t WHERE t.del = false AND (t.id <= ?) LIMIT ?) t " +
|
||||||
|
"LEFT JOIN address t1 ON (t1.user_id = t.id AND t1.user_id >= ? AND t1.del = false)");
|
||||||
|
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class)
|
||||||
|
.selectAll()
|
||||||
|
.selectCollection(AddressDO.class, UserDTO::getAddressList)
|
||||||
|
.leftJoin(AddressDO.class, on -> on
|
||||||
|
.eq(AddressDO::getUserId, UserDO::getId)
|
||||||
|
.ge(AddressDO::getUserId, 0))
|
||||||
|
.le(UserDO::getId, 15)
|
||||||
|
.logicDelToOn()
|
||||||
|
.pageByMain();
|
||||||
|
|
||||||
|
Page<UserDTO> page = wrapper.page(new Page<>(1, 8), UserDTO.class);
|
||||||
|
page.getRecords().forEach(System.out::println);
|
||||||
|
assert page.getRecords().size() == 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageByMain1() {
|
||||||
|
ThreadLocalUtils.set("SELECT t.id, t.pid, t.`name`, t.`json`, t.sex, t.head_img, t.create_time, t.address_id, t.address_id2," +
|
||||||
|
" t.del, t.create_by, t.update_by, concat('?', t.id) AS sdafsdfsdfsd, t1.id AS joina_id, t1.user_id, " +
|
||||||
|
"t1.area_id, t1.tel, t1.address, t1.del AS joina_del FROM (SELECT * FROM `user` t WHERE t.del = false " +
|
||||||
|
"AND (t.id <= ?) LIMIT ?) t LEFT JOIN address t1 ON (t1.user_id = t.id AND t1.user_id >= ? AND t1.del = false)");
|
||||||
|
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class)
|
||||||
|
.selectAll()
|
||||||
|
.selectFunc(() -> "concat('?',%s)", UserDO::getId, "sdafsdfsdfsd")
|
||||||
|
.selectCollection(AddressDO.class, UserDTO::getAddressList)
|
||||||
|
.leftJoin(AddressDO.class, on -> on
|
||||||
|
.eq(AddressDO::getUserId, UserDO::getId)
|
||||||
|
.ge(AddressDO::getUserId, 0))
|
||||||
|
.le(UserDO::getId, 15)
|
||||||
|
.logicDelToOn()
|
||||||
|
.pageByMain();
|
||||||
|
|
||||||
|
Page<UserDTO> page = wrapper.page(new Page<>(1, 8), UserDTO.class);
|
||||||
|
page.getRecords().forEach(System.out::println);
|
||||||
|
assert page.getRecords().size() == 8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test11() {
|
||||||
|
ThreadLocalUtils.set("SELECT t.id, t.pid, t.`name`, t.`json`, t.sex, t.head_img, t.create_time, t.address_id, " +
|
||||||
|
"t.address_id2, t.del, t.create_by, t.update_by, concat('?', t.id) AS sdafsdfsdfsd, t1.id AS joina_id, " +
|
||||||
|
"t1.user_id, t1.area_id, t1.tel, t1.address, t1.del AS joina_del FROM (SELECT * FROM `user` t " +
|
||||||
|
"WHERE t.del = false AND (t.id <= ?) GROUP BY t.id ORDER BY t.id DESC LIMIT ?) t LEFT JOIN address t1 ON " +
|
||||||
|
"(t1.user_id = t.id AND t1.user_id >= ? AND t1.del = false)");
|
||||||
|
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class)
|
||||||
|
.selectAll()
|
||||||
|
.selectFunc(() -> "concat('?',%s)", UserDO::getId, "sdafsdfsdfsd")
|
||||||
|
.selectCollection(AddressDO.class, UserDTO::getAddressList)
|
||||||
|
.leftJoin(AddressDO.class, on -> on
|
||||||
|
.eq(AddressDO::getUserId, UserDO::getId)
|
||||||
|
.ge(AddressDO::getUserId, 0))
|
||||||
|
.le(UserDO::getId, 15)
|
||||||
|
.orderByDesc(UserDO::getId)
|
||||||
|
.groupBy(UserDO::getId)
|
||||||
|
.logicDelToOn()
|
||||||
|
.pageByMain(f -> f.countSelectSql("1"));
|
||||||
|
|
||||||
|
Page<UserDTO> page = wrapper.page(new Page<>(1, 8), UserDTO.class);
|
||||||
|
page.getRecords().forEach(System.out::println);
|
||||||
|
assert page.getRecords().size() == 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test13() {
|
||||||
|
ThreadLocalUtils.set("SELECT t.id, t.pid, t.`name`, t.`json`, t.sex, t.head_img, t.create_time, t.address_id, " +
|
||||||
|
"t.address_id2, t.del, t.create_by, t.update_by, concat('?', t.id) AS sdafsdfsdfsd, t1.id AS joina_id, " +
|
||||||
|
"t1.user_id, t1.area_id, t1.tel, t1.address, t1.del AS joina_del FROM (SELECT * FROM `user` t " +
|
||||||
|
"WHERE t.del = false AND (t.id <= ?) GROUP BY t.id ORDER BY t.id DESC LIMIT ?) t LEFT JOIN address t1 ON " +
|
||||||
|
"(t1.user_id = t.id AND t1.user_id >= ? AND t1.del = false)");
|
||||||
|
MPJLambdaWrapper<UserDO> wrapper = JoinWrappers.lambda(UserDO.class)
|
||||||
|
.selectAll()
|
||||||
|
.selectFunc(() -> "concat('?',%s)", UserDO::getId, "sdafsdfsdfsd")
|
||||||
|
.selectCollection(AddressDO.class, UserDTO::getAddressList)
|
||||||
|
.leftJoin(AddressDO.class, on -> on
|
||||||
|
.eq(AddressDO::getUserId, UserDO::getId)
|
||||||
|
.ge(AddressDO::getUserId, 0))
|
||||||
|
.le(UserDO::getId, 15)
|
||||||
|
.orderByDesc(UserDO::getId)
|
||||||
|
.groupBy(UserDO::getId)
|
||||||
|
.logicDelToOn()
|
||||||
|
.pageByMain();
|
||||||
|
|
||||||
|
Page<UserDTO> page = wrapper.page(new Page<>(1, 8), UserDTO.class);
|
||||||
|
page.getRecords().forEach(System.out::println);
|
||||||
|
assert page.getRecords().size() == 8;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user