This commit is contained in:
admin 2021-02-04 18:51:40 +08:00
parent e26aad80b2
commit 567cae431a
10 changed files with 186 additions and 249 deletions

View File

@ -8,31 +8,38 @@
### 方法一
1. 将代码down到本地使用maven install
2. 在自己的项目中添加依赖
1. 在项目中添加依赖,依赖已经包含了mybatis-plus3.4.2,依赖后无需再次引入mybatis-plus
```xml
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.0.5</version>
</dependency>
```
### 方法二
2. 配置插件,添加MyJoinInterceptor
1. 将mybatisplus目录复制到你的工程中的springboot扫描路径下
```java
2. 添加cglib依赖
@Configuration
public class MybatisPlusConfig {
```xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
```
/**
* 启用连表拦截器
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//连表插件
mybatisPlusInterceptor.addInnerInterceptor(new MyJoinInterceptor());
//可以添加多租户或其他插件
return mybatisPlusInterceptor;
}
}
```
### 使用
@ -40,12 +47,12 @@
* service继承MyBaseService (可选)
* serviceImpl继承MyBaseServiceImpl (可选)
1. MyBaseMapper继承BaseMapper,在原有的方法基础上又添加了以下方法:
* selectJoinOne 连表查询一条记录对象
* selectJoinList 连表查询返回命中记录对象集合
* selectJoinPage 连表分页查询对象集合
* selectJoinMap 连表查询一条记录返回Map
* selectJoinMaps 连表查询返回命中记录Map集合
1. MyBaseMapper继承BaseMapper,在原有的方法基础上又添加了以下方法:
* selectJoinOne 连表查询一条记录对象
* selectJoinList 连表查询返回命中记录对象集合
* selectJoinPage 连表分页查询对象集合
* selectJoinMap 连表查询一条记录返回Map
* selectJoinMaps 连表查询返回命中记录Map集合
* selectJoinMapsPage 连表分页查询返回Map集合
2. MyBaseService 继承了IService,同样添加以上方法
@ -189,34 +196,33 @@ class test {
对应sql
```mysql
SELECT
t.id,
t.name,
t.sex,
t.head_img,
addr.tel,
addr.address,
CASE t.sex WHEN '男' THEN '1' ELSE '0' END AS sex,
sum(a.province) as province
FROM
user t
LEFT JOIN (select * from user_address) addr on t.id = addr.user_id
RIGHT JOIN area a on addr.area_id = a.id
WHERE
t.id = ?
AND addr.tel LIKE ?
AND a.province <= ?)
ORDER BY
addr.id DESC
SELECT t.id,
t.name,
t.sex,
t.head_img,
addr.tel,
addr.address,
CASE t.sex WHEN '男' THEN '1' ELSE '0' END AS sex,
sum(a.province) as province
FROM user t
LEFT JOIN (select * from user_address) addr on t.id = addr.user_id
RIGHT JOIN area a on addr.area_id = a.id
WHERE t.id = ?
AND addr.tel LIKE ?
AND a.province <= ?)
ORDER BY
addr.id
DESC
```
## MyJoinLambdaQueryWrapper用法
MyJoinLambdaQueryWrapper与MyLambdaQueryWrapper不同,是一套新的支持多表的wrapper
MyJoinLambdaQueryWrapper与上面连个Wrapper不同,是一套新的支持多表的wrapper
MyQueryWrapper是基于QueryWrapper扩展的
MyLambdaQueryWrapper是基于LambdaQueryWrapper扩展的
而LambdaQueryWrapper由于泛型约束,不支持扩展成多表的lambdaWrapper
#### MyJoinLambdaQueryWrapper更符合面向对象(OOP),没有魔术值,全部基于lambda,但灵活性不如上面的
#### MyJoinLambdaQueryWrapper示例
#### 简单的3表查询

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.0.2</version>
<version>1.0.5</version>
<name>mp</name>
<description>An enhanced toolkit of Mybatis-Plus to simplify development.</description>
<url>https://github.com/yulichang/mybatis-plus-join</url>

View File

@ -1,7 +1,6 @@
package com.github.yulichang;
import com.github.yulichang.injector.MySqlInjector;
import com.github.yulichang.interceptor.MyResultInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -18,9 +17,9 @@ public class MybatisPlusConfiguration {
return new MySqlInjector();
}
@Bean
@ConditionalOnMissingBean(MyResultInterceptor.class)
public MyResultInterceptor myResultInterceptor() {
return new MyResultInterceptor();
}
// @Bean
// @ConditionalOnMissingBean(MyResultInterceptor.class)
// public MyResultInterceptor myResultInterceptor() {
// return new MyResultInterceptor();
// }
}

View File

@ -0,0 +1,55 @@
package com.github.yulichang.interceptor;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.github.yulichang.toolkit.Constant;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* 结果封装拦截器
*
* @author yulichang
*/
public class MyJoinInterceptor implements InnerInterceptor {
private static Field type = null;
static {
try {
type = ResultMap.class.getDeclaredField("type");
type.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
@SuppressWarnings("unchecked")
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (parameter instanceof Map) {
Map<String, ?> map = (Map<String, ?>) parameter;
if (CollectionUtils.isNotEmpty(map)) {
try {
Class<?> clazz = (Class<?>) map.get(Constant.CLAZZ);
List<ResultMap> list = ms.getResultMaps();
if (CollectionUtils.isNotEmpty(list)) {
ResultMap resultMap = list.get(0);
type.set(resultMap, clazz);
}
} catch (Exception ignored) {
//通常是MapperMethod内部类HashMap的子类ParamMap重写了了get方法抛出的BindingException
}
}
}
}
}

View File

@ -1,73 +0,0 @@
package com.github.yulichang.interceptor;
import com.baomidou.mybatisplus.core.MybatisParameterHandler;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.yulichang.toolkit.Constant;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 结果封装
*
* @author yulichang
*/
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
})
//@SuppressWarnings("all")
public class MyResultInterceptor implements Interceptor {
/**
* 缓存初始化反射字段
* todo
* 应该可以想mybatis-plus那样用自定义的MybatisParameterHandler直接获取mappedStatement和resultMap,而不是反射获取
*/
private static Field mappedStatement = null;
private static Field type = null;
static {
try {
mappedStatement = MybatisParameterHandler.class.getDeclaredField("mappedStatement");
mappedStatement.setAccessible(true);
type = ResultMap.class.getDeclaredField("type");
type.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (Objects.nonNull(invocation.getTarget()) && invocation.getTarget() instanceof MybatisParameterHandler) {
MybatisParameterHandler mybatisParameterHandler = (MybatisParameterHandler) invocation.getTarget();
Map<String, ?> map = (Map<String, ?>) mybatisParameterHandler.getParameterObject();
if (CollectionUtils.isNotEmpty(map)) {
try {
Class<?> resClazz = (Class<?>) map.get(Constant.CLAZZ);
MappedStatement statement = (MappedStatement) mappedStatement.get(mybatisParameterHandler);
List<ResultMap> list = statement.getResultMaps();
if (CollectionUtils.isNotEmpty(list)) {
ResultMap resultMap = list.get(0);
type.set(resultMap, resClazz);
}
} catch (Exception e) {
return invocation.proceed();
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}

View File

@ -153,7 +153,9 @@ public class MyLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyLambda
public final MyLambdaQueryWrapper<T> selectAll(Class<T> clazz) {
TableInfo info = TableInfoHelper.getTableInfo(clazz);
List<String> list = new ArrayList<>();
list.add(Constant.TABLE_ALIAS + StringPool.DOT + info.getKeyColumn());
if (info.havePK()) {
list.add(Constant.TABLE_ALIAS + StringPool.DOT + info.getKeyColumn());
}
list.addAll(info.getFieldList().stream().map(i -> Constant.TABLE_ALIAS + StringPool.DOT + i.getColumn()).collect(Collectors.toList()));
String join = String.join(StringPool.COMMA, list);
if (StringUtils.isBlank(sqlSelect.getStringValue())) {

View File

@ -97,7 +97,9 @@ public class MyQueryWrapper<T> extends AbstractWrapper<T, String, MyQueryWrapper
public final MyQueryWrapper<T> selectAll(Class<T> clazz) {
TableInfo info = TableInfoHelper.getTableInfo(clazz);
List<String> list = new ArrayList<>();
list.add(Constant.TABLE_ALIAS + StringPool.DOT + info.getKeyColumn());
if (info.havePK()) {
list.add(Constant.TABLE_ALIAS + StringPool.DOT + info.getKeyColumn());
}
list.addAll(info.getFieldList().stream().map(i -> Constant.TABLE_ALIAS + StringPool.DOT + i.getColumn()).collect(Collectors.toList()));
String join = String.join(StringPool.COMMA, list);
if (StringUtils.isBlank(sqlSelect.getStringValue())) {

View File

@ -1,29 +1,51 @@
package com.github.yulichang.toolkit;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.lang.reflect.Field;
import java.util.Objects;
/**
* @author yulichang
* @see LambdaUtils
* @see PropertyNamer
*/
@SuppressWarnings("all")
public final class MyLambdaUtils {
/**
* 获取lambda属性名 UserDO::getId -> id
*/
public static <T> String getName(SFunction<T, ?> fn) {
return PropertyNamer.methodToProperty(LambdaUtils.resolve(fn).getImplMethodName());
}
/**
* 获取列明
* 优先获取tableField中的值
*/
public static <T> String getColumn(SFunction<T, ?> fn) {
return StringUtils.camelToUnderline(getName(fn));
SerializedLambda lambda = LambdaUtils.resolve(fn);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
try {
Field field = lambda.getImplClass().getDeclaredField(fieldName);
TableField annotation = field.getAnnotation(TableField.class);
if (Objects.nonNull(annotation)) {
return annotation.value();
}
} catch (NoSuchFieldException ignored) {
}
return StringUtils.camelToUnderline(fieldName);
}
@SuppressWarnings("unchecked")
public static <T> Class<T> getEntityClass(SFunction<T, ?> fn) {
return (Class<T>) LambdaUtils.resolve(fn).getInstantiatedType();
}
}

View File

@ -1,15 +1,10 @@
package com.github.yulichang.wrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.github.yulichang.toolkit.Constant;
import com.github.yulichang.toolkit.MyLambdaUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.util.Arrays;
import java.util.HashMap;
@ -29,9 +24,9 @@ public abstract class MyAbstractLambdaWrapper<T, Children extends MyAbstractLamb
private boolean initColumnMap = false;
/**
* 参与连接的表<class,别名>
* 关联的表
*/
protected Map<Class<?>, SubClass<?, ?>> subTable = new HashMap<>();
protected final Map<Class<?>, Integer> subTable = new HashMap<>();
@SuppressWarnings("unchecked")
protected <X> String columnsToString(SFunction<X, ?>... columns) {
@ -54,89 +49,15 @@ public abstract class MyAbstractLambdaWrapper<T, Children extends MyAbstractLamb
}
protected String columnToString(SFunction<?, ?> column, boolean onlyColumn) {
TableInfo info = TableInfoHelper.getTableInfo(MyLambdaUtils.getEntityClass(column));
Assert.notNull(info, "can not find table for lambda");
return info.getTableName() + StringPool.DOT + getColumn(LambdaUtils.resolve(column), onlyColumn);
return Constant.TABLE_ALIAS + getDefault(subTable.get(MyLambdaUtils.getEntityClass(column))) + StringPool.DOT +
MyLambdaUtils.getColumn(column);
}
/**
* 获取 SerializedLambda 对应的列信息 lambda 表达式中推测实体类
* <p>
* 如果获取不到列信息那么本次条件组装将会失败
*
* @param lambda lambda 表达式
* @param onlyColumn 如果是结果: "name", 如果否 "name" as "name"
* @return
* @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常
* @see SerializedLambda#getImplClass()
* @see SerializedLambda#getImplMethodName()
*/
private String getColumn(SerializedLambda lambda, boolean onlyColumn) {
Class<?> aClass = lambda.getInstantiatedType();
tryInitCache(aClass);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
ColumnCache columnCache = getColumnCache(fieldName, aClass);
return onlyColumn ? columnCache.getColumn() : columnCache.getColumnSelect();
protected String getDefault(Integer i) {
if (Objects.nonNull(i)) {
return i.toString();
}
return StringPool.SPACE;
}
private void tryInitCache(Class<?> lambdaClass) {
if (!initColumnMap) {
final Class<T> entityClass = getEntityClass();
if (entityClass != null) {
lambdaClass = entityClass;
}
columnMap = LambdaUtils.getColumnMap(lambdaClass);
initColumnMap = true;
}
Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
}
private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
if (Objects.isNull(columnCache)) {
columnCache = new ColumnCache(fieldName, null);
}
return columnCache;
}
public static class SubClass<L, R> {
private String tableAlias;
private SFunction<L, ?> left;
private SFunction<R, ?> right;
public SubClass(String tableAlias, SFunction<L, ?> left, SFunction<R, ?> right) {
this.tableAlias = tableAlias;
this.left = left;
this.right = right;
}
public String getTableAlias() {
return tableAlias;
}
public void setTableAlias(String tableAlias) {
this.tableAlias = tableAlias;
}
public SFunction<L, ?> getLeft() {
return left;
}
public void setLeft(SFunction<L, ?> left) {
this.left = left;
}
public SFunction<R, ?> getRight() {
return right;
}
public void setRight(SFunction<R, ?> right) {
this.right = right;
}
}
}

View File

@ -15,9 +15,7 @@ import com.github.yulichang.toolkit.MyLambdaUtils;
import com.github.yulichang.wrapper.interfaces.MyLambdaJoin;
import com.github.yulichang.wrapper.interfaces.MySFunctionQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -42,13 +40,17 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
/**
* 主表别名
*/
private final SharedString alias = new SharedString();
private final SharedString alias = new SharedString(Constant.TABLE_ALIAS);
/**
* 查询的字段
*/
private final List<SelectColumn> selectColumns = new ArrayList<>();
/**
* 表序号
*/
private int tableIndex = 1;
/**
* 不建议直接 new 该实例使用 Wrappers.lambdaQuery(entity)
@ -99,9 +101,7 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
public final <S> MyJoinLambdaQueryWrapper<T> select(SFunction<S, ?>... columns) {
if (ArrayUtils.isNotEmpty(columns)) {
for (SFunction<S, ?> s : columns) {
Class<S> clazz = MyLambdaUtils.getEntityClass(s);
TableInfo info = TableInfoHelper.getTableInfo(clazz);
selectColumns.add(new SelectColumn(clazz, info.getTableName(), MyLambdaUtils.getColumn(s), null));
selectColumns.add(new SelectColumn(MyLambdaUtils.getEntityClass(s), MyLambdaUtils.getColumn(s), null));
}
}
return typedThis;
@ -112,25 +112,24 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
TableInfo info = TableInfoHelper.getTableInfo(entityClass);
Assert.notNull(info, "table can not be find");
info.getFieldList().stream().filter(predicate).collect(Collectors.toList()).forEach(
i -> selectColumns.add(new SelectColumn(entityClass, info.getTableName(), i.getColumn(), null)));
i -> selectColumns.add(new SelectColumn(entityClass, i.getColumn(), null)));
return typedThis;
}
public final <S, X> MyJoinLambdaQueryWrapper<T> selectAs(SFunction<S, ?> columns, SFunction<X, ?> alias) {
Class<S> clazz = MyLambdaUtils.getEntityClass(columns);
TableInfo info = TableInfoHelper.getTableInfo(clazz);
Assert.notNull(info, "table can not be find for lambda");
selectColumns.add(new SelectColumn(clazz, info.getTableName(), MyLambdaUtils.getColumn(columns), MyLambdaUtils.getName(alias)));
selectColumns.add(new SelectColumn(MyLambdaUtils.getEntityClass(columns), MyLambdaUtils.getColumn(columns), MyLambdaUtils.getName(alias)));
return typedThis;
}
public final MyJoinLambdaQueryWrapper<T> selectAll(Class<?> clazz) {
TableInfo info = TableInfoHelper.getTableInfo(clazz);
Assert.notNull(info, "table can not be find -> %s", clazz);
selectColumns.add(new SelectColumn(clazz, info.getTableName(), info.getKeyColumn(), null));
if (info.havePK()) {
selectColumns.add(new SelectColumn(clazz, info.getKeyColumn(), null));
}
info.getFieldList().forEach(c ->
selectColumns.add(new SelectColumn(clazz, info.getTableName(), c.getColumn(), null)));
selectColumns.add(new SelectColumn(clazz, c.getColumn(), null)));
return typedThis;
}
@ -138,7 +137,7 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
public String getSqlSelect() {
if (StringUtils.isBlank(sqlSelect.getStringValue())) {
String s = selectColumns.stream().map(i ->
i.getTableName() + StringPool.DOT + i.getColumnName() +
Constant.TABLE_ALIAS + getDefault(subTable.get(i.getClazz())) + StringPool.DOT + i.getColumnName() +
(StringUtils.isBlank(i.getAlias()) ? StringPool.EMPTY : (Constant.AS + i.getAlias())))
.collect(Collectors.joining(StringPool.COMMA));
sqlSelect.setStringValue(s);
@ -174,22 +173,37 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
@Override
public <L, X> MyJoinLambdaQueryWrapper<T> join(String keyWord, boolean condition, Class<L> clazz, SFunction<L, ?> left, SFunction<X, ?> right) {
if (condition) {
subTable.put(clazz, tableIndex);
TableInfo leftInfo = TableInfoHelper.getTableInfo(clazz);
TableInfo rightInfo = TableInfoHelper.getTableInfo(MyLambdaUtils.getEntityClass(right));
String s = keyWord + leftInfo.getTableName() + Constant.ON + leftInfo.getTableName() + StringPool.DOT
+ MyLambdaUtils.getColumn(left) + Constant.EQUALS + rightInfo.getTableName() + StringPool.DOT
+ MyLambdaUtils.getColumn(right);
StringBuilder sb = new StringBuilder(keyWord)
.append(leftInfo.getTableName())
.append(StringPool.SPACE)
.append(Constant.TABLE_ALIAS)
.append(tableIndex)
.append(StringPool.SPACE)
.append(Constant.ON)
.append(Constant.TABLE_ALIAS)
.append(tableIndex)
.append(StringPool.DOT)
.append(MyLambdaUtils.getColumn(left))
.append(Constant.EQUALS)
.append(Constant.TABLE_ALIAS)
.append(getDefault(subTable.get(MyLambdaUtils.getEntityClass(right))))
.append(StringPool.DOT)
.append(MyLambdaUtils.getColumn(right));
tableIndex++;
if (StringUtils.isBlank(from.getStringValue())) {
from.setStringValue(s);
from.setStringValue(sb.toString());
} else {
from.setStringValue(from.getStringValue() + s);
from.setStringValue(from.getStringValue() + sb.toString());
}
}
return typedThis;
}
/**
* select字段
*/
@ -197,15 +211,12 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
private Class<?> clazz;
private String tableName;
private String columnName;
private String alias;
public SelectColumn(Class<?> clazz, String tableName, String columnName, String alias) {
public SelectColumn(Class<?> clazz, String columnName, String alias) {
this.clazz = clazz;
this.tableName = tableName;
this.columnName = columnName;
this.alias = alias;
}
@ -218,14 +229,6 @@ public class MyJoinLambdaQueryWrapper<T> extends MyAbstractLambdaWrapper<T, MyJo
this.clazz = clazz;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getColumnName() {
return columnName;
}