From a466cfc06669bf62e055ca0663d1a36b81e69b3d Mon Sep 17 00:00:00 2001
From: admin <570810310@qq.com>
Date: Fri, 21 May 2021 11:59:46 +0800
Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5mybatis-plus=203.4.3=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81,=E5=B9=B6=E5=8D=87=E7=BA=A7mybatis-plus=E7=89=88?=
=?UTF-8?q?=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../yulichang/metadata/TableFieldInfo.java | 70 ++++-
.../github/yulichang/metadata/TableInfo.java | 14 +
.../yulichang/metadata/TableInfoHelper.java | 56 ++--
.../github/yulichang/toolkit/LambdaUtils.java | 113 +++++++-
.../yulichang/wrapper/MPJAbstractWrapper.java | 261 ++++++++++++------
.../yulichang/wrapper/MPJLambdaWrapper.java | 8 +-
.../yulichang/wrapper/interfaces/Func.java | 65 ++---
.../yulichang/wrapper/interfaces/Join.java | 136 +++++++++
8 files changed, 549 insertions(+), 174 deletions(-)
create mode 100644 src/main/java/com/github/yulichang/wrapper/interfaces/Join.java
diff --git a/src/main/java/com/github/yulichang/metadata/TableFieldInfo.java b/src/main/java/com/github/yulichang/metadata/TableFieldInfo.java
index 414925f..a6d26f5 100644
--- a/src/main/java/com/github/yulichang/metadata/TableFieldInfo.java
+++ b/src/main/java/com/github/yulichang/metadata/TableFieldInfo.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.*;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.reflection.Reflector;
@@ -42,6 +43,10 @@ public class TableFieldInfo implements Constants {
* 属性表达式#{property}, 可以指定jdbcType, typeHandler等
*/
private final String el;
+ /**
+ * jdbcType, typeHandler等部分
+ */
+ private final String mapping;
/**
* 属性类型
*/
@@ -140,6 +145,32 @@ public class TableFieldInfo implements Constants {
*/
private Class extends TypeHandler>> typeHandler;
+ /**
+ * 是否存在OrderBy注解
+ */
+ private boolean isOrderBy;
+ /**
+ * 排序类型
+ */
+ private String orderByType;
+ /**
+ * 排序顺序
+ */
+ private short orderBySort;
+
+ /**
+ * 全新的 存在 TableField 注解时使用的构造函数
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public TableFieldInfo(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, Field field, TableField tableField,
+ Reflector reflector, boolean existTableLogic,boolean isOrderBy) {
+ this(dbConfig,tableInfo,field,tableField,reflector,existTableLogic);
+ this.isOrderBy = isOrderBy;
+ if(isOrderBy){
+ initOrderBy(field);
+ }
+ }
+
/**
* 全新的 存在 TableField 注解时使用的构造函数
*/
@@ -163,7 +194,7 @@ public class TableFieldInfo implements Constants {
String el = this.property;
if (JdbcType.UNDEFINED != jdbcType) {
this.jdbcType = jdbcType;
- el += (COMMA + "jdbcType=" + jdbcType.name());
+ el += (COMMA + SqlScriptUtils.mappingJdbcType(jdbcType));
}
if (UnknownTypeHandler.class != typeHandler) {
this.typeHandler = (Class extends TypeHandler>>) typeHandler;
@@ -183,12 +214,14 @@ public class TableFieldInfo implements Constants {
}
el += (COMMA + "javaType=" + javaType);
}
- el += (COMMA + "typeHandler=" + typeHandler.getName());
+ el += (COMMA + SqlScriptUtils.mappingTypeHandler(this.typeHandler));
}
if (StringUtils.isNotBlank(numericScale)) {
- el += (COMMA + "numericScale=" + numericScale);
+ el += (COMMA + SqlScriptUtils.mappingNumericScale(Integer.valueOf(numericScale)));
}
this.el = el;
+ int index = el.indexOf(COMMA);
+ this.mapping = index > 0 ? el.substring(++index) : null;
this.initLogicDelete(dbConfig, field, existTableLogic);
String column = tableField.value();
@@ -211,7 +244,7 @@ public class TableFieldInfo implements Constants {
this.column = column;
this.sqlSelect = column;
if (tableInfo.getResultMap() == null && !tableInfo.isAutoInitResultMap() &&
- TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {
+ TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {
/* 未设置 resultMap 也未开启自动构建 resultMap, 字段规则又不符合 mybatis 的自动封装规则 */
String propertyFormat = dbConfig.getPropertyFormat();
String asProperty = this.property;
@@ -241,6 +274,17 @@ public class TableFieldInfo implements Constants {
return fromAnnotation == FieldStrategy.DEFAULT ? fromDbConfig : fromAnnotation;
}
+ /**
+ * 不存在 TableField 注解时, 使用的构造函数
+ */
+ public TableFieldInfo(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, Field field, Reflector reflector,
+ boolean existTableLogic,boolean isOrderBy) {
+ this(dbConfig,tableInfo,field,reflector,existTableLogic);
+ this.isOrderBy = isOrderBy;
+ if(isOrderBy){
+ initOrderBy(field);
+ }
+ }
/**
* 不存在 TableField 注解时, 使用的构造函数
*/
@@ -254,6 +298,7 @@ public class TableFieldInfo implements Constants {
this.isPrimitive = this.propertyType.isPrimitive();
this.isCharSequence = StringUtils.isCharSequence(this.propertyType);
this.el = this.property;
+ this.mapping = null;
this.insertStrategy = dbConfig.getInsertStrategy();
this.updateStrategy = dbConfig.getUpdateStrategy();
this.whereStrategy = dbConfig.getSelectStrategy();
@@ -277,7 +322,7 @@ public class TableFieldInfo implements Constants {
this.column = column;
this.sqlSelect = column;
if (tableInfo.getResultMap() == null && !tableInfo.isAutoInitResultMap() &&
- TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {
+ TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {
/* 未设置 resultMap 也未开启自动构建 resultMap, 字段规则又不符合 mybatis 的自动封装规则 */
String propertyFormat = dbConfig.getPropertyFormat();
String asProperty = this.property;
@@ -288,6 +333,21 @@ public class TableFieldInfo implements Constants {
}
}
+ /**
+ * 排序初始化
+ * @param field 字段
+ */
+ private void initOrderBy(Field field){
+ OrderBy orderBy = field.getAnnotation(OrderBy.class);
+ if (null != orderBy) {
+ this.isOrderBy = true;
+ this.orderBySort = orderBy.sort();
+ this.orderByType = orderBy.isDesc()?"desc":"asc";
+ }else{
+ this.isOrderBy = false;
+ }
+ }
+
/**
* 逻辑删除初始化
*
diff --git a/src/main/java/com/github/yulichang/metadata/TableInfo.java b/src/main/java/com/github/yulichang/metadata/TableInfo.java
index 1a8ecdf..271cc3e 100644
--- a/src/main/java/com/github/yulichang/metadata/TableInfo.java
+++ b/src/main/java/com/github/yulichang/metadata/TableInfo.java
@@ -13,6 +13,7 @@ import lombok.experimental.Accessors;
import org.apache.ibatis.session.Configuration;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -148,6 +149,13 @@ public class TableInfo implements Constants {
@Setter(AccessLevel.NONE)
private TableFieldInfo versionFieldInfo;
+ /**
+ * 排序列表
+ */
+ @Getter
+ @Setter
+ public List orderByFields;
+
public TableInfo(Class> entityType) {
this.entityType = entityType;
}
@@ -198,6 +206,12 @@ public class TableInfo implements Constants {
if (i.isWithUpdateFill()) {
this.withUpdateFill = true;
}
+ if (i.isOrderBy()) {
+ if (null == this.orderByFields) {
+ this.orderByFields = new LinkedList<>();
+ }
+ this.orderByFields.add(i);
+ }
if (i.isVersion()) {
this.withVersion = true;
this.versionFieldInfo = i;
diff --git a/src/main/java/com/github/yulichang/metadata/TableInfoHelper.java b/src/main/java/com/github/yulichang/metadata/TableInfoHelper.java
index 1f27e73..b3fbe24 100644
--- a/src/main/java/com/github/yulichang/metadata/TableInfoHelper.java
+++ b/src/main/java/com/github/yulichang/metadata/TableInfoHelper.java
@@ -96,7 +96,7 @@ public class TableInfoHelper {
tableInfo.setTableName(targetTableName);
/* 开启了自定义 KEY 生成器 */
- if (null != dbConfig.getKeyGenerator()) {
+ if (CollectionUtils.isNotEmpty(dbConfig.getKeyGenerators())) {
tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
}
return excludeProperty;
@@ -146,6 +146,8 @@ public class TableInfoHelper {
boolean existTableId = isExistTableId(list);
// 是否存在 @TableLogic 注解
boolean existTableLogic = isExistTableLogic(list);
+ // 是否存在 @OrderBy 注解
+ boolean existOrderBy = isExistOrderBy(list);
List fieldList = new ArrayList<>(list.size());
for (Field field : list) {
@@ -175,12 +177,12 @@ public class TableInfoHelper {
/* 有 @TableField 注解的字段初始化 */
if (tableField != null) {
- fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic));
+ fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic, existOrderBy));
continue;
}
- /* 无 @TableField 注解的字段初始化 */
- fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic));
+ /* 无 @TableField 注解的字段初始化 */
+ fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, existOrderBy));
}
/* 字段列表 */
@@ -216,6 +218,18 @@ public class TableInfoHelper {
return list.stream().anyMatch(field -> field.isAnnotationPresent(TableLogic.class));
}
+ /**
+ *
+ * 判断排序注解是否存在
+ *
+ *
+ * @param list 字段列表
+ * @return true 为存在 @TableId 注解;
+ */
+ public static boolean isExistOrderBy(List list) {
+ return list.stream().anyMatch(field -> field.isAnnotationPresent(OrderBy.class));
+ }
+
/**
*
* 主键属性初始化
@@ -233,7 +247,7 @@ public class TableInfoHelper {
final String property = field.getName();
if (field.getAnnotation(TableField.class) != null) {
logger.warn(String.format("This \"%s\" is the table primary key by @TableId annotation in Class: \"%s\",So @TableField annotation will not work!",
- property, tableInfo.getEntityType().getName()));
+ property, tableInfo.getEntityType().getName()));
}
/* 主键策略( 注解 > 全局 ) */
// 设置 Sequence 其他策略无效
@@ -260,12 +274,12 @@ public class TableInfoHelper {
final Class> keyType = reflector.getGetterType(property);
if (keyType.isPrimitive()) {
logger.warn(String.format("This primary key of \"%s\" is primitive !不建议如此请使用包装类 in Class: \"%s\"",
- property, tableInfo.getEntityType().getName()));
+ property, tableInfo.getEntityType().getName()));
}
tableInfo.setKeyRelated(checkRelated(underCamel, property, column))
- .setKeyColumn(column)
- .setKeyProperty(property)
- .setKeyType(keyType);
+ .setKeyColumn(column)
+ .setKeyProperty(property)
+ .setKeyType(keyType);
}
/**
@@ -284,7 +298,7 @@ public class TableInfoHelper {
if (DEFAULT_ID_NAME.equalsIgnoreCase(property)) {
if (field.getAnnotation(TableField.class) != null) {
logger.warn(String.format("This \"%s\" is the table primary key by default name for `id` in Class: \"%s\",So @TableField will not work!",
- property, tableInfo.getEntityType().getName()));
+ property, tableInfo.getEntityType().getName()));
}
String column = property;
if (dbConfig.isCapitalMode()) {
@@ -293,13 +307,13 @@ public class TableInfoHelper {
final Class> keyType = reflector.getGetterType(property);
if (keyType.isPrimitive()) {
logger.warn(String.format("This primary key of \"%s\" is primitive !不建议如此请使用包装类 in Class: \"%s\"",
- property, tableInfo.getEntityType().getName()));
+ property, tableInfo.getEntityType().getName()));
}
tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column))
- .setIdType(dbConfig.getIdType())
- .setKeyColumn(column)
- .setKeyProperty(property)
- .setKeyType(keyType);
+ .setIdType(dbConfig.getIdType())
+ .setKeyColumn(column)
+ .setKeyProperty(property)
+ .setKeyType(keyType);
return true;
}
return false;
@@ -322,7 +336,7 @@ public class TableInfoHelper {
if (underCamel) {
// 开启了驼峰并且 column 包含下划线
return !(propertyUpper.equals(columnUpper) ||
- propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));
+ propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));
} else {
// 未开启驼峰,直接判断 property 是否与 column 相同(全大写)
return !propertyUpper.equals(columnUpper);
@@ -340,10 +354,10 @@ public class TableInfoHelper {
public static List getAllFields(Class> clazz) {
List fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
return fieldList.stream()
- .filter(field -> {
- /* 过滤注解非表字段属性 */
- TableField tableField = field.getAnnotation(TableField.class);
- return (tableField == null || tableField.exist());
- }).collect(toList());
+ .filter(field -> {
+ /* 过滤注解非表字段属性 */
+ TableField tableField = field.getAnnotation(TableField.class);
+ return (tableField == null || tableField.exist());
+ }).collect(toList());
}
}
diff --git a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java
index 2039e71..e8e098c 100644
--- a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java
+++ b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java
@@ -1,29 +1,120 @@
package com.github.yulichang.toolkit;
-import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
-import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.github.yulichang.exception.MPJException;
+import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
+import com.baomidou.mybatisplus.core.toolkit.support.*;
import org.apache.ibatis.reflection.property.PropertyNamer;
+import java.lang.invoke.SerializedLambda;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static java.util.Locale.ENGLISH;
/**
- * @author yulichang
- * @see com.baomidou.mybatisplus.core.toolkit.LambdaUtils
- * @see org.apache.ibatis.reflection.property.PropertyNamer
+ * copy {@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils}
*/
public final class LambdaUtils {
- /**
- * 获取属性名
- */
+ /* ******* 自定义方法 *********** */
public static String getName(SFunction fn) {
- return PropertyNamer.methodToProperty(com.baomidou.mybatisplus.core.toolkit.LambdaUtils.resolve(fn).getImplMethodName());
+ return PropertyNamer.methodToProperty(extract(fn).getImplMethodName());
}
@SuppressWarnings("unchecked")
public static Class getEntityClass(SFunction fn) {
- return (Class) com.baomidou.mybatisplus.core.toolkit.LambdaUtils.resolve(fn).getInstantiatedType();
+ return (Class) extract(fn).getInstantiatedClass();
}
+ /* ******* 自定义方法 结束 以下代码均为拷贝 *********** */
+
+ /**
+ * 字段映射
+ */
+ private static final Map> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();
+
+ /**
+ * 该缓存可能会在任意不定的时间被清除
+ *
+ * @param func 需要解析的 lambda 对象
+ * @param 类型,被调用的 Function 对象的目标类型
+ * @return 返回解析后的结果
+ */
+ public static LambdaMeta extract(SFunction func) {
+ try {
+ Method method = func.getClass().getDeclaredMethod("writeReplace");
+ return new SerializedLambdaMeta((SerializedLambda) ReflectionKit.setAccessible(method).invoke(func));
+ } catch (NoSuchMethodException e) {
+ if (func instanceof Proxy) return new ProxyLambdaMeta((Proxy) func);
+ String message = "Cannot find method writeReplace, please make sure that the lambda composite class is currently passed in";
+ throw new MybatisPlusException(message);
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ throw new MybatisPlusException(e);
+ }
+ }
+
+ /**
+ * 格式化 key 将传入的 key 变更为大写格式
+ *
+ *
+ * Assert.assertEquals("USERID", formatKey("userId"))
+ *
+ *
+ * @param key key
+ * @return 大写的 key
+ */
+ public static String formatKey(String key) {
+ return key.toUpperCase(ENGLISH);
+ }
+
+ /**
+ * 将传入的表信息加入缓存
+ *
+ * @param tableInfo 表信息
+ */
+ public static void installCache(TableInfo tableInfo) {
+ COLUMN_CACHE_MAP.put(tableInfo.getEntityType().getName(), createColumnCacheMap(tableInfo));
+ }
+
+ /**
+ * 缓存实体字段 MAP 信息
+ *
+ * @param info 表信息
+ * @return 缓存 map
+ */
+ private static Map createColumnCacheMap(TableInfo info) {
+ Map map;
+
+ if (info.havePK()) {
+ map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size() + 1);
+ map.put(formatKey(info.getKeyProperty()), new ColumnCache(info.getKeyColumn(), info.getKeySqlSelect()));
+ } else {
+ map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size());
+ }
+
+ info.getFieldList().forEach(i ->
+ map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect(), i.getMapping()))
+ );
+ return map;
+ }
+
+ /**
+ * 获取实体对应字段 MAP
+ *
+ * @param clazz 实体类
+ * @return 缓存 map
+ */
+ public static Map getColumnMap(Class> clazz) {
+ return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz.getName(), key -> {
+ TableInfo info = TableInfoHelper.getTableInfo(clazz);
+ return info == null ? null : createColumnCacheMap(info);
+ });
+ }
+
}
diff --git a/src/main/java/com/github/yulichang/wrapper/MPJAbstractWrapper.java b/src/main/java/com/github/yulichang/wrapper/MPJAbstractWrapper.java
index b716e42..3fc1fe6 100644
--- a/src/main/java/com/github/yulichang/wrapper/MPJAbstractWrapper.java
+++ b/src/main/java/com/github/yulichang/wrapper/MPJAbstractWrapper.java
@@ -3,17 +3,18 @@ package com.github.yulichang.wrapper;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.enums.SqlLike;
import com.baomidou.mybatisplus.core.toolkit.*;
+import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.wrapper.interfaces.Compare;
import com.github.yulichang.wrapper.interfaces.Func;
+import com.github.yulichang.wrapper.interfaces.Join;
import java.util.Arrays;
import java.util.Collection;
@@ -46,6 +47,11 @@ public abstract class MPJAbstractWrapper paramNameValuePairs;
+ /**
+ * 其他
+ */
+ /* mybatis plus 3.4.3新增 这个时wrapper的别名 不是MPJ的别名 */
+ protected SharedString paramAlias;
protected SharedString lastSql;
/**
* SQL注释
@@ -176,14 +182,14 @@ public abstract class MPJAbstractWrapper Children between(boolean condition, SFunction column, Object val1, Object val2) {
- return doIt(condition, () -> columnToString(column), BETWEEN, () -> formatSql("{0}", val1), AND,
- () -> formatSql("{0}", val2));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN,
+ () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));
}
@Override
public Children notBetween(boolean condition, SFunction column, Object val1, Object val2) {
- return doIt(condition, () -> columnToString(column), NOT_BETWEEN, () -> formatSql("{0}", val1), AND,
- () -> formatSql("{0}", val2));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_BETWEEN,
+ () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));
}
@Override
@@ -208,12 +214,13 @@ public abstract class MPJAbstractWrapper appendSqlSegments(OR));
}
@Override
- public Children apply(boolean condition, String applySql, Object... value) {
- return doIt(condition, APPLY, () -> formatSql(applySql, value));
+ public Children apply(boolean condition, String applySql, Object... values) {
+ return maybeDo(condition, () -> appendSqlSegments(APPLY,
+ () -> formatSqlMaybeWithParam(applySql, null, values)));
}
@Override
@@ -241,77 +248,101 @@ public abstract class MPJAbstractWrapper String.format("(%s)", existsSql));
+ public Children exists(boolean condition, String existsSql, Object... values) {
+ return maybeDo(condition, () -> appendSqlSegments(EXISTS,
+ () -> String.format("(%s)", formatSqlMaybeWithParam(existsSql, null, values))));
}
@Override
- public Children notExists(boolean condition, String existsSql) {
- return not(condition).exists(condition, existsSql);
+ public Children notExists(boolean condition, String existsSql, Object... values) {
+ return not(condition).exists(condition, existsSql, values);
}
@Override
public Children isNull(boolean condition, SFunction column) {
- return doIt(condition, () -> columnToString(column), IS_NULL);
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NULL));
}
@Override
public Children isNotNull(boolean condition, SFunction column) {
- return doIt(condition, () -> columnToString(column), IS_NOT_NULL);
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL));
}
@Override
public Children in(boolean condition, SFunction column, Collection> coll) {
- return doIt(condition, () -> columnToString(column), IN, inExpression(coll));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll)));
+ }
+
+ @Override
+ public Children in(boolean condition, SFunction column, Object... values) {
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values)));
}
@Override
public Children notIn(boolean condition, SFunction column, Collection> coll) {
- return doIt(condition, () -> columnToString(column), NOT_IN, inExpression(coll));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll)));
+ }
+
+ @Override
+ public Children notIn(boolean condition, SFunction column, Object... values) {
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(values)));
}
@Override
public Children inSql(boolean condition, SFunction column, String inValue) {
- return doIt(condition, () -> columnToString(column), IN, () -> String.format("(%s)", inValue));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN,
+ () -> String.format("(%s)", inValue)));
}
@Override
public Children notInSql(boolean condition, SFunction column, String inValue) {
- return doIt(condition, () -> columnToString(column), NOT_IN, () -> String.format("(%s)", inValue));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN,
+ () -> String.format("(%s)", inValue)));
}
@Override
- public Children groupBy(boolean condition, SFunction... columns) {
- if (ArrayUtils.isEmpty(columns)) {
- return typedThis;
- }
- return doIt(condition, GROUP_BY,
- () -> columns.length == 1 ? columnToString(columns[0]) : columnsToString(columns));
+ public Children groupBy(boolean condition, SFunction column, SFunction... columns) {
+ return maybeDo(condition, () -> {
+ String one = columnToString(column);
+ if (ArrayUtils.isNotEmpty(columns)) {
+ one += (StringPool.COMMA + columnsToString(columns));
+ }
+ final String finalOne = one;
+ appendSqlSegments(GROUP_BY, () -> finalOne);
+ });
}
@Override
- public Children orderBy(boolean condition, boolean isAsc, SFunction... columns) {
- if (ArrayUtils.isEmpty(columns)) {
- return typedThis;
- }
- SqlKeyword mode = isAsc ? ASC : DESC;
- for (SFunction column : columns) {
- doIt(condition, ORDER_BY, () -> columnToString(column), mode);
- }
- return typedThis;
+ public Children orderBy(boolean condition, boolean isAsc, SFunction column, SFunction... columns) {
+ return maybeDo(condition, () -> {
+ final SqlKeyword mode = isAsc ? ASC : DESC;
+ appendSqlSegments(ORDER_BY, columnToSqlSegment(column), mode);
+ if (ArrayUtils.isNotEmpty(columns)) {
+ Arrays.stream(columns).forEach(c -> appendSqlSegments(ORDER_BY,
+ columnToSqlSegment(columnSqlInjectFilter(c)), mode));
+ }
+ });
+ }
+
+ /**
+ * 字段 SQL 注入过滤处理,子类重写实现过滤逻辑
+ *
+ * @param column 字段内容
+ * @return
+ */
+ protected SFunction columnSqlInjectFilter(SFunction column) {
+ return column;
}
@Override
public Children having(boolean condition, String sqlHaving, Object... params) {
- return doIt(condition, HAVING, () -> formatSqlIfNeed(condition, sqlHaving, params));
+ return maybeDo(condition, () -> appendSqlSegments(HAVING,
+ () -> formatSqlMaybeWithParam(sqlHaving, null, params)));
}
@Override
public Children func(boolean condition, Consumer consumer) {
- if (condition) {
- consumer.accept(typedThis);
- }
- return typedThis;
+ return maybeDo(condition, () -> consumer.accept(typedThis));
}
/**
@@ -319,7 +350,7 @@ public abstract class MPJAbstractWrapperNOT 关键词
*/
protected Children not(boolean condition) {
- return doIt(condition, NOT);
+ return maybeDo(condition, () -> appendSqlSegments(NOT));
}
/**
@@ -327,7 +358,7 @@ public abstract class MPJAbstractWrapper拼接 AND
*/
protected Children and(boolean condition) {
- return doIt(condition, AND);
+ return maybeDo(condition, () -> appendSqlSegments(AND));
}
/**
@@ -335,7 +366,8 @@ public abstract class MPJAbstractWrapper拼接 LIKE 以及 值
*/
protected Children likeValue(boolean condition, SqlKeyword keyword, SFunction column, Object val, SqlLike sqlLike) {
- return doIt(condition, () -> columnToString(column), keyword, () -> formatSql("{0}", SqlUtils.concatLike(val, sqlLike)));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword,
+ () -> formatParam(null, SqlUtils.concatLike(val, sqlLike))));
}
/**
@@ -347,7 +379,8 @@ public abstract class MPJAbstractWrapper Children addCondition(boolean condition, SFunction column, SqlKeyword sqlKeyword, Object val) {
- return doIt(condition, () -> columnToString(column), sqlKeyword, () -> formatSql("{0}", val));
+ return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword,
+ () -> formatParam(null, val)));
}
/**
@@ -356,12 +389,11 @@ public abstract class MPJAbstractWrapper consumer) {
- if (condition) {
+ return maybeDo(condition, () -> {
final Children instance = instance();
consumer.accept(instance);
- return doIt(true, APPLY, instance);
- }
- return typedThis;
+ appendSqlSegments(APPLY, instance);
+ });
}
/**
@@ -370,56 +402,80 @@ public abstract class MPJAbstractWrapper
- * 根据需要格式化SQL
- *
- * Format SQL for methods: EntityQ.where/and/or...("name={0}", value);
- * ALL the {i} will be replaced with #{MPGENVALi}
- *
- * ew.where("sample_name={0}", "haha").and("sample_age >{0}
- * and sample_age<{1}", 18, 30) TO
- * sample_name=#{MPGENVAL1} and sample_age>#{MPGENVAL2} and
- * sample_age<#{MPGENVAL3}
- *
+ * 支持 "{0}" 这种,或者 "sql {0} sql" 这种
*
- * @param need 是否需要格式化
- * @param sqlStr SQL语句部分
- * @param params 参数集
- * @return sql
+ * @param sqlStr 可能是sql片段
+ * @param mapping 例如: "javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler" 这种
+ * @param params 参数
+ * @return sql片段
*/
- protected final String formatSqlIfNeed(boolean need, String sqlStr, Object... params) {
- if (!need || StringUtils.isBlank(sqlStr)) {
+ protected final String formatSqlMaybeWithParam(String sqlStr, String mapping, Object... params) {
+ if (StringUtils.isBlank(sqlStr)) {
+ // todo 何时会这样?
return null;
}
if (ArrayUtils.isNotEmpty(params)) {
for (int i = 0; i < params.length; ++i) {
- String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();
- sqlStr = sqlStr.replace(String.format("{%s}", i),
- String.format(Constants.WRAPPER_PARAM_FORMAT, Constants.WRAPPER, genParamName));
- paramNameValuePairs.put(genParamName, params[i]);
+ final String target = Constants.LEFT_BRACE + i + Constants.RIGHT_BRACE;
+ sqlStr = sqlStr.replace(target, formatParam(mapping, params[i]));
}
}
return sqlStr;
}
+ /**
+ * 处理入参
+ *
+ * @param mapping 例如: "javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler" 这种
+ * @param param 参数
+ * @return value
+ */
+ protected final String formatParam(String mapping, Object param) {
+ final String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();
+ final String paramStr = getParamAlias() + Constants.WRAPPER_PARAM_MIDDLE + genParamName;
+ paramNameValuePairs.put(genParamName, param);
+ return SqlScriptUtils.safeParam(paramStr, mapping);
+ }
+
+ /**
+ * 函数化的做事
+ *
+ * @param condition 做不做
+ * @param something 做什么
+ * @return Children
+ */
+ protected final Children maybeDo(boolean condition, DoSomething something) {
+ if (condition) {
+ something.doIt();
+ }
+ return typedThis;
+ }
+
/**
* 获取in表达式 包含括号
*
* @param value 集合
*/
- private ISqlSegment inExpression(Collection> value) {
- return () -> value.stream().map(i -> formatSql("{0}", i))
+ protected ISqlSegment inExpression(Collection> value) {
+ if (CollectionUtils.isEmpty(value)) {
+ return () -> "()";
+ }
+ return () -> value.stream().map(i -> formatParam(null, i))
+ .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
+ }
+
+ /**
+ * 获取in表达式 包含括号
+ *
+ * @param values 数组
+ */
+ protected ISqlSegment inExpression(Object[] values) {
+ if (ArrayUtils.isEmpty(values)) {
+ return () -> "()";
+ }
+ return () -> Arrays.stream(values).map(i -> formatParam(null, i))
.collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
}
@@ -447,17 +503,12 @@ public abstract class MPJAbstractWrapper ISqlSegment columnToSqlSegment(SFunction column) {
+ return () -> columnToString(column);
+ }
+
/**
* 获取 columnName
*/
@@ -511,4 +587,13 @@ public abstract class MPJAbstractWrapper extends MPJAbstractLambdaWrapper MPJLambdaWrapper select(SFunction... columns) {
if (ArrayUtils.isNotEmpty(columns)) {
for (SFunction s : columns) {
- selectColumns.add(new SelectColumn(LambdaUtils.getEntityClass(s), getCache(s).getColumn(), null));
+ selectColumns.add(new SelectColumn(com.github.yulichang.toolkit.LambdaUtils.getEntityClass(s), getCache(s).getColumn(), null));
}
}
return typedThis;
@@ -113,14 +113,14 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper MPJLambdaWrapper selectAs(SFunction columns, SFunction alias) {
- return selectAs(columns, LambdaUtils.getName(alias));
+ return selectAs(columns, com.github.yulichang.toolkit.LambdaUtils.getName(alias));
}
/**
* @since 1.1.3
*/
public final MPJLambdaWrapper selectAs(SFunction columns, String alias) {
- selectColumns.add(new SelectColumn(LambdaUtils.getEntityClass(columns), getCache(columns).getColumn(), alias));
+ selectColumns.add(new SelectColumn(com.github.yulichang.toolkit.LambdaUtils.getEntityClass(columns), getCache(columns).getColumn(), alias));
return typedThis;
}
@@ -150,7 +150,7 @@ public class MPJLambdaWrapper extends MPJAbstractLambdaWrapper MPJLambdaWrapper selectIgnore(SFunction... columns) {
if (ArrayUtils.isNotEmpty(columns)) {
for (SFunction s : columns) {
- ignoreColumns.add(new SelectColumn(LambdaUtils.getEntityClass(s), getCache(s).getColumn(), null));
+ ignoreColumns.add(new SelectColumn(com.github.yulichang.toolkit.LambdaUtils.getEntityClass(s), getCache(s).getColumn(), null));
}
}
return typedThis;
diff --git a/src/main/java/com/github/yulichang/wrapper/interfaces/Func.java b/src/main/java/com/github/yulichang/wrapper/interfaces/Func.java
index 5f1c84b..a3a29b1 100644
--- a/src/main/java/com/github/yulichang/wrapper/interfaces/Func.java
+++ b/src/main/java/com/github/yulichang/wrapper/interfaces/Func.java
@@ -3,13 +3,9 @@ package com.github.yulichang.wrapper.interfaces;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import java.io.Serializable;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Optional;
import java.util.function.Consumer;
-import static java.util.stream.Collectors.toList;
-
/**
* 将原来的泛型R改成SFunction
*
@@ -63,6 +59,7 @@ public interface Func extends Serializable {
* 字段 IN (value.get(0), value.get(1), ...)
* 例: in("id", Arrays.asList(1, 2, 3, 4, 5))
*
+ * 注意!集合为空若存在逻辑错误,请在 condition 条件中判断
* 如果集合为 empty 则不会进行 sql 拼接
*
* @param condition 执行条件
@@ -83,6 +80,7 @@ public interface Func extends Serializable {
* 字段 IN (v0, v1, ...)
* 例: in("id", 1, 2, 3, 4, 5)
*
+ * 注意!数组为空若存在逻辑错误,请在 condition 条件中判断
* 如果动态数组为 empty 则不会进行 sql 拼接
*
* @param condition 执行条件
@@ -90,10 +88,7 @@ public interface Func extends Serializable {
* @param values 数据数组
* @return children
*/
- default Children in(boolean condition, SFunction column, Object... values) {
- return in(condition, column, Arrays.stream(Optional.ofNullable(values).orElseGet(() -> new Object[]{}))
- .collect(toList()));
- }
+ Children in(boolean condition, SFunction column, Object... values);
/**
* ignore
@@ -129,10 +124,7 @@ public interface Func extends Serializable {
* @param values 数据数组
* @return children
*/
- default Children notIn(boolean condition, SFunction column, Object... values) {
- return notIn(condition, column, Arrays.stream(Optional.ofNullable(values).orElseGet(() -> new Object[]{}))
- .collect(toList()));
- }
+ Children notIn(boolean condition, SFunction column, Object... values);
/**
* ignore
@@ -177,15 +169,8 @@ public interface Func extends Serializable {
/**
* ignore
*/
- default Children groupBy(SFunction column) {
- return groupBy(true, column);
- }
-
- /**
- * ignore
- */
- default Children groupBy(SFunction... columns) {
- return groupBy(true, columns);
+ default Children groupBy(SFunction column, SFunction... columns) {
+ return groupBy(true, column, columns);
}
/**
@@ -193,23 +178,17 @@ public interface Func extends Serializable {
* 例: groupBy("id", "name")
*
* @param condition 执行条件
+ * @param column 单个字段
* @param columns 字段数组
* @return children
*/
- Children groupBy(boolean condition, SFunction... columns);
+ Children groupBy(boolean condition, SFunction column, SFunction... columns);
/**
* ignore
*/
- default Children orderByAsc(SFunction column) {
- return orderByAsc(true, column);
- }
-
- /**
- * ignore
- */
- default Children orderByAsc(SFunction... columns) {
- return orderByAsc(true, columns);
+ default Children orderByAsc(SFunction column, SFunction... columns) {
+ return orderByAsc(true, column, columns);
}
/**
@@ -217,25 +196,19 @@ public interface Func extends Serializable {
* 例: orderByAsc("id", "name")
*
* @param condition 执行条件
+ * @param column 单个字段
* @param columns 字段数组
* @return children
*/
- default Children orderByAsc(boolean condition, SFunction... columns) {
- return orderBy(condition, true, columns);
+ default Children orderByAsc(boolean condition, SFunction column, SFunction... columns) {
+ return orderBy(condition, true, column, columns);
}
/**
* ignore
*/
- default Children orderByDesc(SFunction column) {
- return orderByDesc(true, column);
- }
-
- /**
- * ignore
- */
- default Children orderByDesc(SFunction... columns) {
- return orderByDesc(true, columns);
+ default Children orderByDesc(SFunction column, SFunction... columns) {
+ return orderByDesc(true, column, columns);
}
/**
@@ -243,11 +216,12 @@ public interface Func extends Serializable {
* 例: orderByDesc("id", "name")
*
* @param condition 执行条件
+ * @param column 单个字段
* @param columns 字段数组
* @return children
*/
- default Children orderByDesc(boolean condition, SFunction... columns) {
- return orderBy(condition, false, columns);
+ default Children orderByDesc(boolean condition, SFunction column, SFunction... columns) {
+ return orderBy(condition, false, column, columns);
}
/**
@@ -256,10 +230,11 @@ public interface Func extends Serializable {
*
* @param condition 执行条件
* @param isAsc 是否是 ASC 排序
+ * @param column 单个字段
* @param columns 字段数组
* @return children
*/
- Children orderBy(boolean condition, boolean isAsc, SFunction... columns);
+ Children orderBy(boolean condition, boolean isAsc, SFunction column, SFunction... columns);
/**
* ignore
diff --git a/src/main/java/com/github/yulichang/wrapper/interfaces/Join.java b/src/main/java/com/github/yulichang/wrapper/interfaces/Join.java
new file mode 100644
index 0000000..b45fee0
--- /dev/null
+++ b/src/main/java/com/github/yulichang/wrapper/interfaces/Join.java
@@ -0,0 +1,136 @@
+package com.github.yulichang.wrapper.interfaces;
+
+import java.io.Serializable;
+
+/**
+ * copy {@link com.baomidou.mybatisplus.core.conditions.interfaces.Join}
+ *
+ * 无改动 在mybatis 3.4.2 升级 3.4.3 后有改动 exists 和 not exists
+ * 为了保证 mybatis plus 3.4.3之前的也能正常使用
+ */
+public interface Join extends Serializable {
+
+ /**
+ * ignore
+ */
+ default Children or() {
+ return or(true);
+ }
+
+ /**
+ * 拼接 OR
+ *
+ * @param condition 执行条件
+ * @return children
+ */
+ Children or(boolean condition);
+
+ /**
+ * ignore
+ */
+ default Children apply(String applySql, Object... values) {
+ return apply(true, applySql, values);
+ }
+
+ /**
+ * 拼接 sql
+ * !! 会有 sql 注入风险 !!
+ * 例1: apply("id = 1")
+ * 例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
+ * 例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())
+ *
+ * @param condition 执行条件
+ * @param values 数据数组
+ * @return children
+ */
+ Children apply(boolean condition, String applySql, Object... values);
+
+ /**
+ * ignore
+ */
+ default Children last(String lastSql) {
+ return last(true, lastSql);
+ }
+
+ /**
+ * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)
+ * 例: last("limit 1")
+ * 注意只能调用一次,多次调用以最后一次为准
+ *
+ * @param condition 执行条件
+ * @param lastSql sql语句
+ * @return children
+ */
+ Children last(boolean condition, String lastSql);
+
+ /**
+ * ignore
+ */
+ default Children comment(String comment) {
+ return comment(true, comment);
+ }
+
+ /**
+ * sql 注释(会拼接在 sql 的最后面)
+ *
+ * @param condition 执行条件
+ * @param comment sql注释
+ * @return children
+ */
+ Children comment(boolean condition, String comment);
+
+ /**
+ * ignore
+ */
+ default Children first(String firstSql) {
+ return first(true, firstSql);
+ }
+
+ /**
+ * sql 起始句(会拼接在SQL语句的起始处)
+ *
+ * @param condition 执行条件
+ * @param firstSql 起始语句
+ * @return children
+ * @since 3.3.1
+ */
+ Children first(boolean condition, String firstSql);
+
+ /**
+ * ignore
+ */
+ default Children exists(String existsSql, Object... values) {
+ return exists(true, existsSql, values);
+ }
+
+ /**
+ * 拼接 EXISTS ( sql语句 )
+ * !! sql 注入方法 !!
+ * 例: exists("select id from table where age = 1")
+ *
+ * @param condition 执行条件
+ * @param existsSql sql语句
+ * @param values 数据数组
+ * @return children
+ */
+ Children exists(boolean condition, String existsSql, Object... values);
+
+ /**
+ * ignore
+ */
+ default Children notExists(String existsSql, Object... values) {
+ return notExists(true, existsSql, values);
+ }
+
+ /**
+ * 拼接 NOT EXISTS ( sql语句 )
+ * !! sql 注入方法 !!
+ * 例: notExists("select id from table where age = 1")
+ *
+ * @param condition 执行条件
+ * @param existsSql sql语句
+ * @param values 数据数组
+ * @return children
+ */
+ Children notExists(boolean condition, String existsSql, Object... values);
+}