diff --git a/mybatis-plus-join-annotation/src/main/java/com/github/yulichang/annotation/Table.java b/mybatis-plus-join-annotation/src/main/java/com/github/yulichang/annotation/Table.java new file mode 100644 index 0000000..117be10 --- /dev/null +++ b/mybatis-plus-join-annotation/src/main/java/com/github/yulichang/annotation/Table.java @@ -0,0 +1,37 @@ +package com.github.yulichang.annotation; + +import java.lang.annotation.*; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Table { + + /** + * 生成类的类名前缀 + */ + String prefix() default ""; + + /** + * 生成类的类名后缀 + */ + String suffix() default ""; + + /** + * format 优先级低,如果配置了prefix或suffix则不会生效 + */ + String format() default "%sCol"; + + /** + * 生成类的包名 + */ + String packageName() default "apt"; + + /** + * Tables中的字段名 默认大写的类名 + */ + String tablesName() default "%S"; + + +} + diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/BaseColumn.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/BaseColumn.java new file mode 100644 index 0000000..ba08689 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/BaseColumn.java @@ -0,0 +1,8 @@ +package com.github.yulichang.apt; + +public interface BaseColumn { + + Class getColumnClass(); + + String getAlias(); +} \ No newline at end of file diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/Column.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/Column.java new file mode 100644 index 0000000..7a154a7 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/apt/Column.java @@ -0,0 +1,19 @@ +package com.github.yulichang.apt; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.function.Supplier; + +@Data +@AllArgsConstructor +public class Column { + + private BaseColumn root; + + private Class clazz; + + private String property; + + private Supplier alias; +} \ No newline at end of file diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java index e6ddd07..059e4aa 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java @@ -95,7 +95,7 @@ public class MPJInterceptor implements Interceptor { wrapper.setEntityClass((Class) MPJTableMapperHelper.getEntity(getMapper(ms.getId(), ms.getResource()))); } if (wrapper.getSelectColumns().isEmpty() && wrapper.getEntityClass() != null) { - wrapper.selectAll(wrapper.getEntityClass()); + wrapper.selectAll(); } } return buildMappedStatement(ms, resultType, ew); @@ -181,9 +181,8 @@ public class MPJInterceptor implements Interceptor { } } if (wrapper.isResultMap()) { - for (Object o : wrapper.getResultMapMybatisLabel()) { - Label label = (Label) o; - resultMappings.add(buildResult(ms, label, columnSet, columnList)); + for (Label o : wrapper.getResultMapMybatisLabel()) { + resultMappings.add(buildResult(ms, o, columnSet, columnList)); } } result.add(new ResultMap.Builder(ms.getConfiguration(), id, resultType, resultMappings).build()); @@ -228,10 +227,10 @@ public class MPJInterceptor implements Interceptor { String index = r.getIndex(); if (columnSet.contains(columnName)) { columnName = getColumn(columnSet, columnName, 0); - label = new SelectLabel(r.getSelectNormal(), null, mybatisLabel.getOfType(), columnName, StringUtils.isNotBlank(index), index); + label = new SelectLabel(r.getSelectNormal(), null, mybatisLabel.getOfType(), columnName, StringUtils.isNotBlank(index), index, r.getBaseColumn()); } else { columnSet.add(columnName); - label = new SelectLabel(r.getSelectNormal(), null, mybatisLabel.getOfType(), StringUtils.isNotBlank(index), index); + label = new SelectLabel(r.getSelectNormal(), null, mybatisLabel.getOfType(), StringUtils.isNotBlank(index), index, r.getBaseColumn()); } columnList.add(label); ResultMapping.Builder builder = new ResultMapping.Builder(ms.getConfiguration(), r.getProperty(), columnName, r.getJavaType()); diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/KtLambdaWrapper.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/KtLambdaWrapper.java index 7c65b76..9936c12 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/KtLambdaWrapper.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/KtLambdaWrapper.java @@ -188,8 +188,8 @@ public class KtLambdaWrapper extends KtAbstractLambdaWrapper selectAll(Class clazz) { - return Query.super.selectAll(clazz); + public KtLambdaWrapper selectAll() { + return Query.super.selectAll(getEntityClass()); } /** diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/segments/FuncArgs.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/segments/FuncArgs.java index 17f2610..646eec8 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/segments/FuncArgs.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/kt/segments/FuncArgs.java @@ -16,7 +16,9 @@ import java.util.Arrays; public class FuncArgs { public SelectFunc.Arg[] accept(KProperty... kProperty) { - return Arrays.stream(kProperty).map(i -> new SelectFunc.Arg(KtUtils.ref(i), i.getName(), false, null, i)).toArray(SelectFunc.Arg[]::new); + return Arrays.stream(kProperty).map(i -> + new SelectFunc.Arg(KtUtils.ref(i), i.getName(), false, null, i)) + .toArray(SelectFunc.Arg[]::new); } } diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/AptWrapperUtils.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/AptWrapperUtils.java new file mode 100644 index 0000000..c35df92 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/AptWrapperUtils.java @@ -0,0 +1,149 @@ +package com.github.yulichang.toolkit; + +import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +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.toolkit.sql.SqlScriptUtils; +import com.github.yulichang.wrapper.apt.AptQueryWrapper; + +import java.util.Objects; +import java.util.Optional; + +/** + * @author yulichang + * @since 1.4.5 + */ +@SuppressWarnings("DuplicatedCode") +public class AptWrapperUtils { + + public static String buildSubSqlByWrapper(Class clazz, AptQueryWrapper wrapper, String alias) { + TableInfo tableInfo = TableHelper.getAssert(clazz); + String first = Optional.ofNullable(wrapper.getSqlFirst()).orElse(StringPool.EMPTY); + boolean hasWhere = false; + String entityWhere = getEntitySql(tableInfo, wrapper); + if (StringUtils.isNotBlank(entityWhere)) { + hasWhere = true; + } + String mainLogic = mainLogic(hasWhere, clazz, wrapper); + if (StringUtils.isNotBlank(mainLogic)) { + hasWhere = true; + } + String subLogic = subLogic(hasWhere, wrapper); + if (StringUtils.isNotBlank(subLogic)) { + hasWhere = true; + } + String sqlSegment = (wrapper.getSqlSegment() != null && StringUtils.isNotBlank(wrapper.getSqlSegment())) ? + ((wrapper.isEmptyOfNormal() ? StringPool.EMPTY : (hasWhere ? " AND " : " WHERE ")) + wrapper.getSqlSegment()) : StringPool.EMPTY; + + String sqlComment = Optional.ofNullable(wrapper.getSqlComment()).orElse(StringPool.EMPTY); + return String.format(" (%s SELECT %s FROM %s %s %s %s %s %s %s) AS %s ", + first, + wrapper.getSqlSelect(), + wrapper.getTableName(tableInfo.getTableName()), + wrapper.getAlias(), + wrapper.getFrom(), + mainLogic, + subLogic, + sqlSegment, + sqlComment, + alias); + } + + public static String buildUnionSqlByWrapper(Class clazz, AptQueryWrapper wrapper) { + TableInfo tableInfo = TableHelper.getAssert(clazz); + String first = Optional.ofNullable(wrapper.getSqlFirst()).orElse(StringPool.EMPTY); + boolean hasWhere = false; + String entityWhere = getEntitySql(tableInfo, wrapper); + if (StringUtils.isNotBlank(entityWhere)) { + hasWhere = true; + } + String mainLogic = mainLogic(hasWhere, clazz, wrapper); + if (StringUtils.isNotBlank(mainLogic)) { + hasWhere = true; + } + String subLogic = subLogic(hasWhere, wrapper); + if (StringUtils.isNotBlank(subLogic)) { + hasWhere = true; + } + String sqlSegment = (wrapper.getSqlSegment() != null && StringUtils.isNotBlank(wrapper.getSqlSegment())) ? + ((wrapper.isEmptyOfNormal() ? StringPool.EMPTY : (hasWhere ? " AND " : " WHERE ")) + wrapper.getSqlSegment()) : StringPool.EMPTY; + + String sqlComment = Optional.ofNullable(wrapper.getSqlComment()).orElse(StringPool.EMPTY); + return String.format(" %s SELECT %s FROM %s %s %s %s %s %s %s ", + first, + wrapper.getSqlSelect(), + wrapper.getTableName(tableInfo.getTableName()), + wrapper.getAlias(), + wrapper.getFrom(), + mainLogic, + subLogic, + sqlSegment, + sqlComment); + } + + private static String formatParam(AptQueryWrapper wrapper, Object param) { + final String genParamName = Constants.WRAPPER_PARAM + wrapper.getParamNameSeq().incrementAndGet(); + final String paramStr = wrapper.getParamAlias() + ".paramNameValuePairs." + genParamName; + wrapper.getParamNameValuePairs().put(genParamName, param); + return SqlScriptUtils.safeParam(paramStr, null); + } + + private static String getEntitySql(TableInfo tableInfo, AptQueryWrapper wrapper) { + Object obj = wrapper.getEntity(); + if (Objects.isNull(obj)) { + return StringPool.EMPTY; + } + StringBuilder sb = new StringBuilder(StringPool.EMPTY); + for (TableFieldInfo fieldInfo : tableInfo.getFieldList()) { + if (AdapterHelper.getAdapter().mpjHasLogic(tableInfo) && fieldInfo.isLogicDelete()) { + continue; + } + Object val; + try { + val = fieldInfo.getField().get(obj); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + if (Objects.isNull(val)) { + continue; + } + String s = wrapper.getAptIndex().get(wrapper.getBaseColumn()); + sb.append(" AND ").append(s == null ? wrapper.getAlias() : s).append(Constants.DOT) + .append(fieldInfo.getColumn()).append(Constants.EQUALS).append(formatParam(wrapper, val)); + } + //条件不为空 加上 where + if (sb.length() > 0) { + sb.delete(0, 4); + sb.insert(0, " WHERE "); + } + return sb.toString(); + } + + private static String mainLogic(boolean hasWhere, Class clazz, AptQueryWrapper wrapper) { + if (!wrapper.getLogicSql()) { + return StringPool.EMPTY; + } + String info = LogicInfoUtils.getLogicInfo(null, clazz, true, wrapper.getAlias()); + if (StringUtils.isNotBlank(info)) { + if (hasWhere) { + return " AND " + info; + } + return " WHERE " + info.substring(4); + } + return StringPool.EMPTY; + } + + private static String subLogic(boolean hasWhere, AptQueryWrapper wrapper) { + String sql = wrapper.getSubLogicSql(); + if (StringUtils.isNotBlank(sql)) { + if (hasWhere) { + return sql; + } + return " WHERE " + sql.substring(4); + } + return StringPool.EMPTY; + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/JoinWrappers.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/JoinWrappers.java index 35895ae..a1dd705 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/JoinWrappers.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/JoinWrappers.java @@ -1,9 +1,11 @@ package com.github.yulichang.toolkit; +import com.github.yulichang.apt.BaseColumn; import com.github.yulichang.query.MPJQueryWrapper; import com.github.yulichang.wrapper.DeleteJoinWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.github.yulichang.wrapper.UpdateJoinWrapper; +import com.github.yulichang.wrapper.apt.AptQueryWrapper; /** * @author yulichang @@ -102,4 +104,18 @@ public class JoinWrappers { public static UpdateJoinWrapper update(String alias, Class clazz) { return new UpdateJoinWrapper<>(clazz, alias); } + + /** + * JoinWrappers.apt(User.class) + */ + public static AptQueryWrapper apt(BaseColumn baseColumn) { + return new AptQueryWrapper<>(baseColumn); + } + + /** + * JoinWrappers.apt("t", User.class) + */ + public static AptQueryWrapper apt(BaseColumn baseColumn, T entity) { + return new AptQueryWrapper<>(baseColumn, entity); + } } diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/LogicInfoUtils.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/LogicInfoUtils.java index d0fc818..7c70fef 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/LogicInfoUtils.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/LogicInfoUtils.java @@ -29,6 +29,11 @@ public class LogicInfoUtils implements Constants { return absent.computeIfAbsent(hasAlias ? alias : (alias + tableIndex), key -> getLogicStr(key, clazz, true, false)); } + public static String getLogicInfoApt(Class clazz, String alias) { + Map absent = LOGIC_CACHE.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>()); + return absent.computeIfAbsent(alias, key -> getLogicStr(key, clazz, true, false)); + } + public static String getLogicInfoNoAnd(Integer tableIndex, Class clazz, boolean hasAlias, String alias) { Map absent = LOGIC_CACHE_NO_AND.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>()); return absent.computeIfAbsent(hasAlias ? alias : (alias + tableIndex), key -> getLogicStr(key, clazz, false, false)); diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/TableMap.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/TableMap.java new file mode 100644 index 0000000..8c8f78e --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/toolkit/TableMap.java @@ -0,0 +1,51 @@ +package com.github.yulichang.toolkit; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TableMap { + + @Getter + private final K root; + @Getter + private final String rootAlias; + + private final Map map = new HashMap<>(); + + private final List list = new ArrayList<>(); + + public TableMap(K root, String rootAlias) { + this.root = root; + this.rootAlias = rootAlias; + } + + public V put(K key, V value) { + V v = this.map.put(key, value); + if (null == v) { + this.list.add(key); + } + return v; + } + + public V get(K key) { + return this.map.get(key); + } + + public List keyList() { + return this.list; + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public void clear() { + this.map.clear(); + this.list.clear(); + } + +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java index 81337ca..ca1c278 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/MPJLambdaWrapper.java @@ -261,33 +261,6 @@ public class MPJLambdaWrapper extends JoinAbstractLambdaWrapper - * 推荐使用 union(Class<U> clazz, ConsumerConsumer<MPJLambdaWrapper<U>> consumer) - *

- * 例: wrapper.union(UserDO.class, union -> union.selectAll(UserDO.class)) - * - * @see #union(Class, Consumer) - * @deprecated union 不支持子查询 - */ - @Deprecated - @SuppressWarnings({"UnusedReturnValue", "DeprecatedIsStillUsed"}) - public final MPJLambdaWrapper union(MPJLambdaWrapper... wrappers) { - StringBuilder sb = new StringBuilder(); - for (MPJLambdaWrapper wrapper : wrappers) { - addCustomWrapper(wrapper); - Class entityClass = wrapper.getEntityClass(); - Assert.notNull(entityClass, "请使用 new MPJLambdaWrapper(主表.class) 或 JoinWrappers.lambda(主表.class) 构造方法"); - sb.append(" UNION ").append(WrapperUtils.buildUnionSqlByWrapper(entityClass, wrapper)); - } - if (Objects.isNull(unionSql)) { - unionSql = SharedString.emptyString(); - } - unionSql.setStringValue(unionSql.getStringValue() + sb); - return typedThis; - } - /** * union *

@@ -310,32 +283,6 @@ public class MPJLambdaWrapper extends JoinAbstractLambdaWrapper - * 推荐使用 unionAll(Class<U> clazz, Consumer<MPJLambdaWrapper<U>> consumer) - *

- * 例: wrapper.unionAll(UserDO.class, union -> union.selectAll(UserDO.class)) - * - * @see #unionAll(Class, Consumer) - * @deprecated union 不支持子查询 - */ - @Deprecated - @SuppressWarnings("DeprecatedIsStillUsed") - public MPJLambdaWrapper unionAll(MPJLambdaWrapper... wrappers) { - StringBuilder sb = new StringBuilder(); - for (MPJLambdaWrapper wrapper : wrappers) { - addCustomWrapper(wrapper); - Class entityClass = wrapper.getEntityClass(); - Assert.notNull(entityClass, "请使用 new MPJLambdaWrapper(主表.class) 或 JoinWrappers.lambda(主表.class) 构造方法"); - sb.append(" UNION ALL ").append(WrapperUtils.buildUnionSqlByWrapper(entityClass, wrapper)); - } - if (Objects.isNull(unionSql)) { - unionSql = SharedString.emptyString(); - } - unionSql.setStringValue(unionSql.getStringValue() + sb); - return typedThis; - } /** * union @@ -386,15 +333,7 @@ public class MPJLambdaWrapper extends JoinAbstractLambdaWrapper> + extends JoinAbstractWrapper implements QueryJoin { + + /** + * 主表别名 + */ + @Getter + protected String alias = ConfigProperties.tableAlias; + /** + * 副表别名 + */ + protected String subTableAlias = ConfigProperties.tableAlias; + /** + * 是否构建是否存在一对多 + */ + @Getter + protected boolean resultMap = false; + /** + * 表序号 + */ + protected int tableIndex = 1; + + protected boolean isMain = true; + + /** + * 主表 表名处理方法 + */ + protected boolean dynamicTableName = false; + + /** + * 主表 表名处理方法 + */ + protected Function tableFunc; + + /** + * 逻辑删除位置 + */ + protected LogicDelTypeEnum logicDelType = ConfigProperties.logicDelType; + + /** + * 查询表 + */ + protected final SharedString from = new SharedString(); + /** + * 是否有表别名 + */ + @Getter + protected boolean hasAlias; + /** + * 连表关键字 on 条件 func 使用 + */ + @Getter + protected String keyWord; + /** + * 副表逻辑删除开关 + */ + protected boolean subLogicSql = ConfigProperties.subTableLogic; + /** + * 主表逻辑删除开关 + */ + protected boolean logicSql = true; + + @Getter + protected BaseColumn baseColumn; + + /** + * apt别名对应 + */ + @Getter + protected TableMap, String> aptIndex; + + /** + * 构造方法 + */ + protected AptAbstractWrapper(BaseColumn baseColumn) { + this.alias = StringUtils.isNotBlank(baseColumn.getAlias()) ? baseColumn.getAlias() : this.alias; + setEntityClass(baseColumn.getColumnClass()); + this.baseColumn = baseColumn; + initNeed(); + } + + /** + * 构造方法 + * + * @param entity 主表实体类 + */ + protected AptAbstractWrapper(BaseColumn baseColumn, T entity) { + this.alias = StringUtils.isNotBlank(baseColumn.getAlias()) ? baseColumn.getAlias() : this.alias; + setEntity(entity); + this.baseColumn = baseColumn; + initNeed(); + } + + /** + * 设置表别名 + * 设置表别名注意sql注入问题 + * + * @return 自定义表别名 + */ + public Children setTableName(Function tableFunc) { + if (isMain) { + if (tableFunc != null) { + this.dynamicTableName = true; + this.tableFunc = tableFunc; + } + } else { + this.tableName = tableFunc.apply(this.tableName); + } + return typedThis; + } + + public String getTableName(String tableName) { + if (isMain) { + if (dynamicTableName) { + return tableFunc.apply(tableName); + } + return tableName; + } + return super.getTableName(); + } + + + public String getTableNameEnc(String tableName) { + Class entityClass = getEntityClass(); + if (entityClass != null) { + TableInfo tableInfo = TableHelper.get(entityClass); + if (tableInfo != null) { + if (dynamicTableName) { + return tableFunc.apply(tableInfo.getTableName()); + } + return tableInfo.getTableName(); + } + } + String decode; + try { + decode = URLDecoder.decode(tableName, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + if (dynamicTableName) { + return tableFunc.apply(decode); + } + return decode; + } + + + protected String getPrefix(BaseColumn baseColumn) { + if (Objects.nonNull(baseColumn.getAlias())) { + return baseColumn.getAlias(); + } + if (aptIndex.getRoot() == baseColumn) { + return aptIndex.getRootAlias(); + } + String pf = aptIndex.get(baseColumn); + Assert.notEmpty(pf, "table not find %s", baseColumn.getColumnClass().getName()); + return pf; + } + + @Override + protected String columnsToString(Column... columns) { + return Arrays.stream(columns).map(this::columnToString) + .collect(joining(StringPool.COMMA)); + } + + @Override + protected String columnToString(Column column) { + String pf = column.getAlias().get(); + if (Objects.nonNull(pf)) { + return pf + StringPool.DOT + getCache(column).getColumn(); + } + return getPrefix(column.getRoot()) + StringPool.DOT + getCache(column).getColumn(); + } + + protected SelectCache getCache(Column fn) { + Map cacheMap = ColumnCache.getMapField(fn.getClazz()); + return cacheMap.get(fn.getProperty()); + } + + + /** + * 关闭副表逻辑删除 + *

+ * 副表逻辑删除默认在where语句中 + * 但有时候需要让它出现在on语句中, 这两种写法区别还是很大的 + * 所以可以关闭副表逻辑删除, 通过on语句多条件, 自己实现on语句的逻辑删除 + */ + public Children disableSubLogicDel() { + this.subLogicSql = false; + return typedThis; + } + + public Children enableSubLogicDel() { + this.subLogicSql = true; + return typedThis; + } + + /** + * 关闭主表逻辑删除 + */ + public Children disableLogicDel() { + this.logicSql = false; + return typedThis; + } + + public Children enableLogicDel() { + this.logicSql = true; + return typedThis; + } + + /** + * 副表部分逻辑删除支持 + */ + public String getSubLogicSql() { + if (subLogicSql && logicDelType == LogicDelTypeEnum.WHERE) { + if (aptIndex.isEmpty()) { + return StringPool.EMPTY; + } + return aptIndex.keyList().stream().map(key -> LogicInfoUtils.getLogicInfoApt(key.getColumnClass(), getPrefix(key))) + .collect(Collectors.joining(StringPool.SPACE)); + } + return StringPool.EMPTY; + } + + /** + * 主表部分逻辑删除支持 + */ + public boolean getLogicSql() { + return this.logicSql; + } + + /** + * 调整逻辑删除位置为ON语句 + */ + public Children logicDelToOn() { + this.logicDelType = LogicDelTypeEnum.ON; + return typedThis; + } + + /** + * 调整逻辑删除位置为WHERE语句 + */ + public Children logicDelToWhere() { + this.logicDelType = LogicDelTypeEnum.WHERE; + return typedThis; + } + + /** + * 获取连表部分语句 + */ + public String getFrom() { + if (StringUtils.isBlank(from.getStringValue())) { + StringBuilder value = new StringBuilder(); + for (Children wrapper : onWrappers) { + if (StringUtils.isBlank(wrapper.from.getStringValue())) { + if (this.subLogicSql && this.logicDelType == LogicDelTypeEnum.ON) { + TableInfo tableInfo = TableHelper.getAssert(wrapper.getJoinClass()); + if (AdapterHelper.getAdapter().mpjHasLogic(tableInfo)) { + wrapper.appendSqlSegments(APPLY, () -> LogicInfoUtils.getLogicInfoNoAnd( + wrapper.getIndex(), wrapper.getJoinClass(), wrapper.isHasAlias(), wrapper.getAlias() + )); + } + } + value.append(StringPool.SPACE) + .append(wrapper.getKeyWord()) + .append(StringPool.SPACE) + .append(wrapper.getTableName()) + .append(StringPool.SPACE) + .append(wrapper.hasAlias ? wrapper.alias : (wrapper.alias + wrapper.getIndex())) + .append(Constant.ON) + .append(wrapper.getExpression().getNormal().getSqlSegment()); + } else { + value.append(StringPool.SPACE) + .append(wrapper.getKeyWord()) + .append(StringPool.SPACE) + .append(wrapper.from.getStringValue()) + .append(StringPool.SPACE); + } + } + from.setStringValue(value.toString()); + } + return from.getStringValue(); + } + + /** + * 检验表是否已连接 + */ + public boolean checkJoinTable(Class clazz) { + return onWrappers.stream().anyMatch(wrapper -> { + if (Objects.equals(wrapper.getJoinClass(), clazz)) { + return true; + } else { + TableInfo info = TableHelper.getAssert(clazz); + String tableName = info.getTableName(); + return Optional.ofNullable(wrapper.from.getStringValue()) + .map(w -> w.contains(Constant.JOIN + StringPool.SPACE + tableName + StringPool.SPACE)) + .orElse(false); + } + }); + } + + /** + * 内部调用, 不建议使用 + */ + @Override + public Children join(String keyWord, BaseColumn baseColumn, MFunction> function) { + Integer oldIndex = this.getIndex(); + int newIndex = tableIndex; + TableInfo info = TableHelper.getAssert(baseColumn.getColumnClass()); + Children instance = instance(newIndex, keyWord, baseColumn.getColumnClass(), info.getTableName()); + instance.isMain = false; + onWrappers.add(instance); + if (StringUtils.isBlank(baseColumn.getAlias())) { + aptIndex.put(baseColumn, subTableAlias + newIndex); + instance.alias = subTableAlias; + instance.hasAlias = false; + } else { + aptIndex.put(baseColumn, baseColumn.getAlias()); + instance.alias = baseColumn.getAlias(); + instance.hasAlias = true; + } + tableIndex++; + function.apply(instance); + return typedThis; + } + + /** + * 自定义关键词连接 + * + * @param keyWord 连表关键词 + * @param condition 条件 + * @param joinSql sql + */ + @Override + public Children join(String keyWord, boolean condition, String joinSql) { + if (condition) { + Children wrapper = instanceEmpty(); + wrapper.from.setStringValue(joinSql); + wrapper.keyWord = keyWord; + onWrappers.add(wrapper); + } + return typedThis; + } + + /** + * 是否使用默认注解 {@link OrderBy} 排序 + * + * @return true 使用 false 不使用 + */ + public boolean isUseAnnotationOrderBy() { + final String _sqlSegment = this.getSqlSegment(); + if (StringUtils.isBlank(_sqlSegment)) { + return true; + } + final String _sqlSegmentToUpperCase = _sqlSegment.toUpperCase(); + return !(_sqlSegmentToUpperCase.contains(Constants.ORDER_BY) + || _sqlSegmentToUpperCase.contains(Constants.LIMIT)); + } + + /** + * 必要的初始化 + */ + protected void initNeed() { + paramNameSeq = new AtomicInteger(0); + paramNameValuePairs = new HashMap<>(16); + expression = new MergeSegments(); + lastSql = SharedString.emptyString(); + sqlComment = SharedString.emptyString(); + sqlFirst = SharedString.emptyString(); + aptIndex = new TableMap<>(this.baseColumn, this.alias); + } + + @Override + public void clear() { + super.clear(); + this.alias = ConfigProperties.tableAlias; + this.resultMap = false; + this.tableIndex = 1; + this.dynamicTableName = false; + this.tableFunc = null; + this.logicDelType = ConfigProperties.logicDelType; + this.from.toEmpty(); + this.hasAlias = false; + this.keyWord = null; + this.logicSql = true; + this.isMain = true; + this.checkSqlInjection = false; + this.onWrappers.clear(); + this.aptIndex.clear(); + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/AptQueryWrapper.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/AptQueryWrapper.java new file mode 100644 index 0000000..777dd22 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/AptQueryWrapper.java @@ -0,0 +1,341 @@ +package com.github.yulichang.wrapper.apt; + +import com.baomidou.mybatisplus.core.conditions.SharedString; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; +import com.baomidou.mybatisplus.core.toolkit.*; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.github.yulichang.apt.BaseColumn; +import com.github.yulichang.apt.Column; +import com.github.yulichang.config.ConfigProperties; +import com.github.yulichang.toolkit.LambdaUtils; +import com.github.yulichang.toolkit.*; +import com.github.yulichang.toolkit.support.ColumnCache; +import com.github.yulichang.wrapper.apt.interfaces.Query; +import com.github.yulichang.wrapper.apt.interfaces.QueryLabel; +import com.github.yulichang.wrapper.enums.IfExistsSqlKeyWordEnum; +import com.github.yulichang.wrapper.interfaces.Chain; +import com.github.yulichang.wrapper.interfaces.SelectWrapper; +import com.github.yulichang.wrapper.resultmap.Label; +import com.github.yulichang.wrapper.segments.Select; +import com.github.yulichang.wrapper.segments.SelectApt; +import com.github.yulichang.wrapper.segments.SelectCache; +import com.github.yulichang.wrapper.segments.SelectSub; +import lombok.Getter; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * Lambda 语法使用 Wrapper + * + * @author yulichang + * @since 1.5.0 + */ +@SuppressWarnings("unused") +public class AptQueryWrapper extends AptAbstractWrapper> implements + Query>, QueryLabel>, Chain, SelectWrapper> { + + /** + * 查询字段 sql + */ + private SharedString sqlSelect = new SharedString(); + /** + * 是否 select distinct + */ + private boolean selectDistinct = false; + /** + * 查询的字段 + */ + @Getter + private final List getSelectColum() { + return this.selectColumns; + } + + @Override + public void addLabel(Label label) { + this.resultMap = true; + this.resultMapMybatisLabel.add(label); + } + + @Override + public AptQueryWrapper getChildren() { + return typedThis; + } + + + /** + * 设置查询字段 + * + * @param columns 字段数组 + * @return children + */ + public final AptQueryWrapper select(Column... columns) { + if (ArrayUtils.isNotEmpty(columns)) { + for (Column s : columns) { + Map cacheMap = ColumnCache.getMapField(s.getClazz()); + SelectCache cache = cacheMap.get(s.getProperty()); + getSelectColum().add(new SelectApt(cache, s)); + } + } + return typedThis; + } + + + /** + * 查询实体类全部字段 + */ + @Override + public final AptQueryWrapper selectAll(BaseColumn baseColumn, Column... exclude) { + return Query.super.selectAll(baseColumn, exclude); + } + + + /** + * 查询主表全部字段 + *

+ * 需要使用 使用 JoinWrappers.lambda(clazz) 或者 new MPJLambdaQueryWrapper<<(clazz) 构造 + * + * @return children + */ + @Override + public AptQueryWrapper selectAll() { + Assert.notNull(getEntityClass(), "使用 JoinWrappers.apt(clazz) 或者 new JoinAptQueryWrapper<>(BaseColum)"); + return selectAll(getBaseColumn()); + } + + /** + * 子查询 + */ + public AptQueryWrapper selectSub(BaseColumn baseColumn, Consumer> consumer, SFunction alias) { + return selectSub(baseColumn, ConfigProperties.subQueryAlias, consumer, alias); + } + + /** + * 子查询 + */ + public AptQueryWrapper selectSub(BaseColumn baseColumn, String st, Consumer> consumer, SFunction alias) { + AptQueryWrapper wrapper = new AptQueryWrapper(null, baseColumn, SharedString.emptyString(), + paramNameSeq, paramNameValuePairs, new MergeSegments(), new SharedString(this.paramAlias + .getStringValue()), SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString(), + this.aptIndex, null, null, null, null, ifExists) { + }; + wrapper.alias = st; + wrapper.subTableAlias = st; + consumer.accept(wrapper); + addCustomWrapper(wrapper); + String name = LambdaUtils.getName(alias); + this.selectColumns.add(new SelectSub(() -> AptWrapperUtils.buildSubSqlByWrapper(baseColumn.getColumnClass(), wrapper, name), hasAlias, this.alias, name)); + return typedThis; + } + + + /** + * union + *

+ * 例: wrapper.union(UserDO.class, union -> union.selectAll(UserDO.class)) + * + * @param baseColumn union语句的主表类型 + * @since 1.4.8 + */ + public AptQueryWrapper union(BaseColumn baseColumn, Consumer> consumer) { + AptQueryWrapper unionWrapper = JoinWrappers.apt(baseColumn); + addCustomWrapper(unionWrapper); + consumer.accept(unionWrapper); + + String sb = " UNION " + AptWrapperUtils.buildUnionSqlByWrapper(baseColumn.getColumnClass(), unionWrapper); + + if (Objects.isNull(unionSql)) { + unionSql = SharedString.emptyString(); + } + unionSql.setStringValue(unionSql.getStringValue() + sb); + return typedThis; + } + + /** + * union + *

+ * 例: wrapper.unionAll(UserDO.class, union -> union.selectAll(UserDO.class)) + * + * @param baseColumn union语句的主表类型 + * @since 1.4.8 + */ + public AptQueryWrapper unionAll(BaseColumn baseColumn, Consumer> consumer) { + AptQueryWrapper unionWrapper = JoinWrappers.apt(baseColumn); + addCustomWrapper(unionWrapper); + consumer.accept(unionWrapper); + + String sb = " UNION ALL " + AptWrapperUtils.buildUnionSqlByWrapper(baseColumn.getColumnClass(), unionWrapper); + + if (Objects.isNull(unionSql)) { + unionSql = SharedString.emptyString(); + } + unionSql.setStringValue(unionSql.getStringValue() + sb); + return typedThis; + } + + private void addCustomWrapper(AptQueryWrapper wrapper) { + if (Objects.isNull(wrapperIndex)) { + wrapperIndex = new AtomicInteger(0); + } + int index = wrapperIndex.incrementAndGet(); + if (Objects.isNull(wrapperMap)) { + wrapperMap = new HashMap<>(); + } + String key = "ew" + index; + wrapper.setParamAlias(wrapper.getParamAlias() + ".wrapperMap." + key); + wrapperMap.put(key, wrapper); + } + + /** + * 查询条件 SQL 片段 + */ + @Override + public String getSqlSelect() { + if (StringUtils.isBlank(sqlSelect.getStringValue()) && CollectionUtils.isNotEmpty(selectColumns)) { + String s = selectColumns.stream().map(i -> { + if (i.isStr()) { + return i.getColumn(); + } + if (i.isFunc()) { + return String.format(i.getFunc().getSql(), Arrays.stream(i.getColumns()).map(c -> + getPrefix(c.getRoot()) + StringPool.DOT + i.getColumn()).toArray()) + Constant.AS + i.getAlias(); + } else { + String prefix; + if (null == i.getTableAlias() && null != i.getBaseColumn()) { + prefix = getPrefix(i.getBaseColumn()); + } else { + prefix = i.getTableAlias(); + } + String col = prefix + StringPool.DOT + i.getColumn(); + return i.isHasAlias() ? col + Constants.AS + i.getAlias() : col; + } + }).collect(Collectors.joining(StringPool.COMMA)); + sqlSelect.setStringValue(s); + } + return sqlSelect.getStringValue(); + } + + @Override + public String getUnionSql() { + return Optional.ofNullable(unionSql).map(SharedString::getStringValue).orElse(StringPool.EMPTY); + } + + public boolean getSelectDistinct() { + return selectDistinct; + } + + /** + * 用于生成嵌套 sql + *

故 sqlSelect 不向下传递

+ */ + @Override + protected AptQueryWrapper instance() { + return instance(index, null, null, null); + } + + @Override + protected AptQueryWrapper instanceEmpty() { + return new AptQueryWrapper<>(getBaseColumn()); + } + + @Override + protected AptQueryWrapper instance(Integer index, String keyWord, Class joinClass, String tableName) { + return new AptQueryWrapper<>(getEntity(), baseColumn, null, paramNameSeq, paramNameValuePairs, + new MergeSegments(), this.paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString(), + this.aptIndex, index, keyWord, joinClass, tableName, ifExists); + } + + @Override + public void clear() { + super.clear(); + selectDistinct = false; + sqlSelect.toNull(); + selectColumns.clear(); + wrapperIndex = new AtomicInteger(0); + if (Objects.nonNull(wrapperMap)) wrapperMap.clear(); + if (Objects.nonNull(unionSql)) unionSql.toEmpty(); + resultMapMybatisLabel.clear(); + ifExists = ConfigProperties.ifExists; + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/JoinAbstractWrapper.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/JoinAbstractWrapper.java new file mode 100644 index 0000000..d7012a2 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/JoinAbstractWrapper.java @@ -0,0 +1,1062 @@ +package com.github.yulichang.wrapper.apt; + +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.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.exceptions.MybatisPlusException; +import com.baomidou.mybatisplus.core.toolkit.*; +import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils; +import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape; +import com.github.yulichang.apt.Column; +import com.github.yulichang.config.ConfigProperties; +import com.github.yulichang.toolkit.MPJSqlInjectionUtils; +import com.github.yulichang.toolkit.Ref; +import com.github.yulichang.toolkit.sql.SqlScriptUtils; +import com.github.yulichang.wrapper.apt.interfaces.CompareIfExists; +import com.github.yulichang.wrapper.apt.interfaces.Func; +import com.github.yulichang.wrapper.apt.interfaces.OnCompare; +import com.github.yulichang.wrapper.enums.IfExistsSqlKeyWordEnum; +import com.github.yulichang.wrapper.interfaces.CompareStrIfExists; +import com.github.yulichang.wrapper.interfaces.DoSomething; +import com.github.yulichang.wrapper.interfaces.FuncStr; +import com.github.yulichang.wrapper.interfaces.Join; +import com.github.yulichang.wrapper.segments.AptConsumer; +import lombok.Getter; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.*; + +import static com.baomidou.mybatisplus.core.enums.SqlKeyword.*; +import static com.baomidou.mybatisplus.core.enums.WrapperKeyword.APPLY; +import static java.util.stream.Collectors.joining; + +/** + * 查询条件封装 + * copy {@link com.baomidou.mybatisplus.core.conditions.AbstractWrapper} + * + * @author yulichang + */ +@SuppressWarnings({"unchecked", "unused", "DuplicatedCode"}) +public abstract class JoinAbstractWrapper> extends Wrapper + implements CompareIfExists, Nested, Join, Func, OnCompare, + CompareStrIfExists, FuncStr { + + /** + * 占位符 + */ + protected final Children typedThis = (Children) this; + /** + * 表别名 + */ + @Getter + protected Integer index; + /** + * 必要度量 + */ + @Getter + protected AtomicInteger paramNameSeq; + @Getter + protected Map paramNameValuePairs; + /** + * 其他 + */ + /* mybatis plus 3.4.3新增 这个时wrapper的别名 不是MPJ的别名 */ + protected SharedString paramAlias = new SharedString(null); + protected SharedString lastSql; + /** + * SQL注释 + */ + protected SharedString sqlComment; + /** + * SQL起始语句 + */ + protected SharedString sqlFirst; + /** + * ON sql wrapper集合 + */ + protected final List onWrappers = new ArrayList<>(); + /** + * 数据库表映射实体类 + */ + private T entity; + protected MergeSegments expression; + /** + * 实体类型(主要用于确定泛型以及取TableInfo缓存) + */ + private Class entityClass; + /** + * 连表实体类 on 条件 func 使用 + */ + @Getter + protected Class joinClass; + + /** + * 连表表名 + */ + @Getter + protected String tableName; + + /** + * 检查 SQL 注入过滤 + */ + protected boolean checkSqlInjection = false; + + /** + * IfExists 策略 + */ + @Getter + protected BiPredicate ifExists = ConfigProperties.ifExists; + + @Override + public T getEntity() { + return entity; + } + + public Children setEntity(T entity) { + this.entity = entity; + return typedThis; + } + + public Class getEntityClass() { + if (entityClass == null && entity != null) { + entityClass = (Class) entity.getClass(); + } + return entityClass; + } + + public Children setEntityClass(Class entityClass) { + if (entityClass != null) { + onWrappers.forEach(i -> i.setEntityClass(entityClass)); + this.entityClass = entityClass; + } + return typedThis; + } + + /** + * 转为子类,方便自定义继承扩展 + */ + public C toChildren(Ref children) { + return (C) this; + } + + /** + * 转为子类,方便自定义继承扩展 + * 需要子类自定义字段 + */ + public C toChildren(Supplier s) { + return (C) this; + } + + /** + * 开启检查 SQL 注入 + */ + public Children checkSqlInjection() { + this.checkSqlInjection = true; + return typedThis; + } + + public Children setIfExists(BiPredicate IfExists) { + this.ifExists = IfExists; + return typedThis; + } + + /** + * 设置 IfExists + * .IfExists(val -> val != null && StringUtils.isNotBlank(val)) + * + * @param IfExists 判断 + * @return Children + */ + public Children setIfExists(Predicate IfExists) { + this.ifExists = (obj, key) -> IfExists.test(obj); + return typedThis; + } + + @Override + public Children eq(boolean condition, Column column, Object val) { + return addCondition(condition, column, EQ, val); + } + + @Override + public Children ne(boolean condition, Column column, Object val) { + return addCondition(condition, column, NE, val); + } + + @Override + public Children gt(boolean condition, Column column, Object val) { + return addCondition(condition, column, GT, val); + } + + @Override + public Children ge(boolean condition, Column column, Object val) { + return addCondition(condition, column, GE, val); + } + + @Override + public Children lt(boolean condition, Column column, Object val) { + return addCondition(condition, column, LT, val); + } + + @Override + public Children le(boolean condition, Column column, Object val) { + return addCondition(condition, column, LE, val); + } + + @Override + public Children like(boolean condition, Column column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children notLike(boolean condition, Column column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children likeLeft(boolean condition, Column column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.LEFT); + } + + @Override + public Children notLikeLeft(boolean condition, Column column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.LEFT); + } + + @Override + public Children likeRight(boolean condition, Column column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.RIGHT); + } + + @Override + public Children notLikeRight(boolean condition, Column column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.RIGHT); + } + + @Override + public Children between(boolean condition, Column column, Object val1, Object val2) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN, + () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); + } + + @Override + public Children notBetween(boolean condition, Column column, Object val1, Object val2) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_BETWEEN, + () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); + } + + @Override + public Children and(boolean condition, Consumer consumer) { + return and(condition).addNestedCondition(condition, consumer); + } + + @Override + public Children or(boolean condition, Consumer consumer) { + return or(condition).addNestedCondition(condition, consumer); + } + + @Override + public Children nested(boolean condition, Consumer consumer) { + return addNestedCondition(condition, consumer); + } + + @Override + public Children not(boolean condition, Consumer consumer) { + return not(condition).addNestedCondition(condition, consumer); + } + + @Override + public Children or(boolean condition) { + return maybeDo(condition, () -> appendSqlSegments(OR)); + } + + @Override + public Children apply(boolean condition, String applySql, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(APPLY, + () -> formatSqlMaybeWithParam(applySql, null, values))); + } + + public Children applyFunc(String applySql, Function consumerFunction, Object... values) { + return applyFunc(true, applySql, consumerFunction, values); + } + + public Children applyFunc(boolean condition, String applySql, + Function consumerFunction, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(APPLY, + () -> formatSqlMaybeWithParam(String.format(applySql, + Arrays.stream(consumerFunction.apply(AptConsumer.func)).map(this::columnToString).toArray()), null, values))); + } + + @Override + public Children last(boolean condition, String lastSql) { + if (condition) { + this.lastSql.setStringValue(this.lastSql.getStringValue() + StringPool.SPACE + lastSql); + } + return typedThis; + } + + @Override + public Children comment(boolean condition, String comment) { + if (condition) { + this.sqlComment.setStringValue(comment); + } + return typedThis; + } + + @Override + public Children first(boolean condition, String firstSql) { + if (condition) { + this.sqlFirst.setStringValue(firstSql + StringPool.SPACE + this.sqlFirst.getStringValue()); + } + return typedThis; + } + + @Override + public Children around(boolean condition, String firstSql, String lastSql) { + this.first(condition, firstSql); + this.last(condition, lastSql); + return typedThis; + } + + @Override + 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, Object... values) { + return not(condition).exists(condition, existsSql, values); + } + + @Override + public Children isNull(boolean condition, Column column) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NULL)); + } + + @Override + public Children isNotNull(boolean condition, Column column) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL)); + } + + @Override + public Children in(boolean condition, Column column, Collection coll) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll))); + } + + @Override + public Children in(boolean condition, Column column, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values))); + } + + @Override + public Children notIn(boolean condition, Column column, Collection coll) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll))); + } + + @Override + public Children notIn(boolean condition, Column column, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(values))); + } + + @Override + public Children inSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children notInSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, + () -> String.format("(%s)", inValue))); + } + + + @Override + public Children gtSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GT, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children geSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GE, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children ltSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LT, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children leSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LE, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children eqSql(boolean condition, Column column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), EQ, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children groupBy(boolean condition, List columns) { + return maybeDo(condition, () -> { + if (CollectionUtils.isNotEmpty(columns)) { + final String finalOne = columnsToString(columns, false); + appendSqlSegments(GROUP_BY, () -> finalOne); + } + }); + } + + @Override + public Children groupBy(Column column, Column... columns) { + return groupBy(true, column, columns); + } + + + @Override + public Children groupBy(boolean condition, Column column, Column... 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 orderByAsc(boolean condition, List columns) { + return maybeDo(condition, () -> { + if (CollectionUtils.isNotEmpty(columns)) { + columns.forEach(c -> appendSqlSegments(ORDER_BY, + columnToSqlSegment(columnSqlInjectFilter(c)), ASC)); + } + }); + } + + @Override + public Children orderByAsc(Column column, Column... columns) { + return orderByAsc(true, column, columns); + } + + /** + * 排序:ORDER BY 字段, ... ASC + *

例: orderByAsc("id", "name")

+ * + * @param condition 执行条件 + * @param column 单个字段 + * @param columns 字段数组 + * @return children + */ + @Override + public Children orderByAsc(boolean condition, Column column, Column... columns) { + return orderBy(condition, true, column, columns); + } + + @Override + public Children orderByDesc(boolean condition, List columns) { + return maybeDo(condition, () -> { + if (CollectionUtils.isNotEmpty(columns)) { + columns.forEach(c -> appendSqlSegments(ORDER_BY, + columnToSqlSegment(columnSqlInjectFilter(c)), DESC)); + } + }); + } + + @Override + public Children orderByDesc(Column column, Column... columns) { + return orderByDesc(true, column, columns); + } + + + /** + * 排序:ORDER BY 字段, ... DESC + *

例: orderByDesc("id", "name")

+ * + * @param condition 执行条件 + * @param column 单个字段 + * @param columns 字段数组 + * @return children + */ + @Override + public Children orderByDesc(boolean condition, Column column, Column... columns) { + return orderBy(condition, false, column, columns); + } + + @Override + public Children orderBy(boolean condition, boolean isAsc, Column column, Column... 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 字段内容 + */ + protected Column columnSqlInjectFilter(Column column) { + return column; + } + + @Override + public Children having(boolean condition, String sqlHaving, Object... params) { + return maybeDo(condition, () -> appendSqlSegments(HAVING, + () -> formatSqlMaybeWithParam(sqlHaving, null, params))); + } + + @Override + public Children func(boolean condition, Consumer consumer) { + return maybeDo(condition, () -> consumer.accept(typedThis)); + } + + /** + * 内部自用 + *

NOT 关键词

+ */ + protected Children not(boolean condition) { + return maybeDo(condition, () -> appendSqlSegments(NOT)); + } + + /** + * 内部自用 + *

拼接 AND

+ */ + protected Children and(boolean condition) { + return maybeDo(condition, () -> appendSqlSegments(AND)); + } + + /** + * 内部自用 + *

拼接 LIKE 以及 值

+ */ + protected Children likeValue(boolean condition, SqlKeyword keyword, Column column, Object val, SqlLike sqlLike) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword, + () -> formatParam(null, SqlUtils.concatLike(val, sqlLike)))); + } + + protected Children likeValue(boolean condition, SqlKeyword keyword, String column, Object val, SqlLike sqlLike) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword, + () -> formatParam(null, SqlUtils.concatLike(val, sqlLike)))); + } + + /** + * 普通查询条件 + * + * @param condition 是否执行 + * @param column 属性 + * @param sqlKeyword SQL 关键词 + * @param val 条件值 + */ + protected Children addCondition(boolean condition, Column column, SqlKeyword sqlKeyword, Object val) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword, + () -> formatParam(null, val))); + } + + protected Children addCondition(boolean condition, Column column, SqlKeyword sqlKeyword, Column val) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword, columnToSqlSegment(val) + )); + } + + protected Children addCondition(boolean condition, String column, SqlKeyword sqlKeyword, Object val) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword, + () -> formatParam(null, val))); + } + + /** + * 多重嵌套查询条件 + * + * @param condition 查询条件值 + */ + protected Children addNestedCondition(boolean condition, Consumer consumer) { + return maybeDo(condition, () -> { + final Children instance = instance(); + consumer.accept(instance); + appendSqlSegments(APPLY, instance); + }); + } + + /** + * 子类返回一个自己的新对象 + */ + protected abstract Children instance(); + + protected abstract Children instanceEmpty(); + + protected abstract Children instance(Integer index, String keyWord, Class joinClass, String tableName); + + /** + * 格式化 sql + *

+ * 支持 "{0}" 这种,或者 "sql {0} sql" 这种 + * + * @param sqlStr 可能是sql片段 + * @param mapping 例如: "javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler" 这种 + * @param params 参数 + * @return sql片段 + */ + @SuppressWarnings("SameParameterValue") + 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) { + 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() + ".paramNameValuePairs." + 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 集合 + */ + 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)); + } + + @Override + public void clear() { + entity = null; + paramAlias.toNull(); + paramNameSeq.set(0); + paramNameValuePairs.clear(); + expression.clear(); + if (Objects.nonNull(lastSql)) lastSql.toEmpty(); + if (Objects.nonNull(sqlComment)) sqlComment.toEmpty(); + if (Objects.nonNull(sqlFirst)) sqlFirst.toEmpty(); + entityClass = null; + onWrappers.clear(); + index = null; + ifExists = ConfigProperties.ifExists; + } + + /** + * 添加 where 片段 + * + * @param sqlSegments ISqlSegment 数组 + */ + protected void appendSqlSegments(ISqlSegment... sqlSegments) { + expression.add(sqlSegments); + } + + @Override + public String getSqlSegment() { + return expression.getSqlSegment() + lastSql.getStringValue(); + } + + @Override + public String getSqlComment() { + if (StringUtils.isNotBlank(sqlComment.getStringValue())) { + return "/*" + StringEscape.escapeRawString(sqlComment.getStringValue()) + "*/"; + } + return null; + } + + @Override + public String getSqlFirst() { + if (StringUtils.isNotBlank(sqlFirst.getStringValue())) { + return StringEscape.escapeRawString(sqlFirst.getStringValue()); + } + return null; + } + + @Override + public MergeSegments getExpression() { + return expression; + } + + public String getParamAlias() { + return paramAlias.getStringValue() == null ? Constants.WRAPPER : paramAlias.getStringValue(); + } + + /** + * 参数别名设置,初始化时优先设置该值、重复设置异常 + * + * @param paramAlias 参数别名 + * @return Children + */ + @SuppressWarnings("UnusedReturnValue") + public Children setParamAlias(String paramAlias) { + Assert.notEmpty(paramAlias, "paramAlias can not be empty!"); + this.paramAlias.setStringValue(paramAlias); + return typedThis; + } + + /** + * 获取 columnName + */ + protected final ISqlSegment columnToSqlSegment(Column column) { + return () -> columnToString(column); + } + + protected final ISqlSegment columnToSqlSegment(String column) { + return () -> columnsToString(column); + } + + /** + * 获取 columnName + */ + abstract protected String columnToString(Column column); + + protected String columnToString(String column) { + if (checkSqlInjection && MPJSqlInjectionUtils.check(column)) { + throw new MybatisPlusException("Discovering SQL injection column: " + column); + } + return column; + } + + protected String columnsToString(List columns) { + return columns.stream().map(this::columnToString).collect(joining(StringPool.COMMA)); + } + + protected String columnsToString(String... columns) { + return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA)); + } + + + /** + * 多字段转换为逗号 "," 分割字符串 + * + * @param columns 多字段 + */ + abstract String columnsToString(Column... columns); + + public String columnsToString(List columns, boolean flag) { + return columns.stream().map(this::columnToString).collect(joining(StringPool.COMMA)); + } + + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + public Children clone() { + return SerializationUtils.clone(typedThis); + } + + /* ************************* on语句重载 *************************** */ + + @Override + public Children eq(boolean condition, Column column, Column val) { + return addCondition(condition, column, EQ, val); + } + + @Override + public Children ne(boolean condition, Column column, Column val) { + return addCondition(condition, column, NE, val); + } + + @Override + public Children gt(boolean condition, Column column, Column val) { + return addCondition(condition, column, GT, val); + } + + @Override + public Children ge(boolean condition, Column column, Column val) { + return addCondition(condition, column, GE, val); + } + + @Override + public Children lt(boolean condition, Column column, Column val) { + return addCondition(condition, column, LT, val); + } + + @Override + public Children le(boolean condition, Column column, Column val) { + return addCondition(condition, column, LE, val); + } + + /* ****************************************** **/ + + + @Override + public Children allEqStr(boolean condition, Map params, boolean null2IsNull) { + if (condition && CollectionUtils.isNotEmpty(params)) { + params.forEach((k, v) -> { + if (StringUtils.checkValNotNull(v)) { + eq(k, v); + } else { + if (null2IsNull) { + isNull(k); + } + } + }); + } + return typedThis; + } + + @Override + public Children allEqStr(boolean condition, BiPredicate filter, Map params, boolean null2IsNull) { + if (condition && CollectionUtils.isNotEmpty(params)) { + params.forEach((k, v) -> { + if (filter.test(k, v)) { + if (StringUtils.checkValNotNull(v)) { + eq(k, v); + } else { + if (null2IsNull) { + isNull(k); + } + } + } + }); + } + return typedThis; + } + + @Override + public Children eq(boolean condition, String column, Object val) { + return addCondition(condition, column, EQ, val); + } + + @Override + public Children ne(boolean condition, String column, Object val) { + return addCondition(condition, column, NE, val); + } + + @Override + public Children gt(boolean condition, String column, Object val) { + return addCondition(condition, column, GT, val); + } + + @Override + public Children ge(boolean condition, String column, Object val) { + return addCondition(condition, column, GE, val); + } + + @Override + public Children lt(boolean condition, String column, Object val) { + return addCondition(condition, column, LT, val); + } + + @Override + public Children le(boolean condition, String column, Object val) { + return addCondition(condition, column, LE, val); + } + + @Override + public Children like(boolean condition, String column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children notLike(boolean condition, String column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children likeLeft(boolean condition, String column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.LEFT); + } + + @Override + public Children notLikeLeft(boolean condition, String column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.LEFT); + } + + @Override + public Children likeRight(boolean condition, String column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.RIGHT); + } + + @Override + public Children notLikeRight(boolean condition, String column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.RIGHT); + } + + @Override + public Children between(boolean condition, String column, Object val1, Object val2) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN, + () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); + } + + @Override + public Children notBetween(boolean condition, String column, Object val1, Object val2) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_BETWEEN, + () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); + } + + + /* ****************************************** **/ + + + @Override + public Children isNull(boolean condition, String column) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NULL)); + } + + @Override + public Children isNotNull(boolean condition, String column) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL)); + } + + @Override + public Children in(boolean condition, String column, Collection coll) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll))); + } + + @Override + public Children in(boolean condition, String column, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values))); + } + + @Override + public Children notIn(boolean condition, String column, Collection coll) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll))); + } + + @Override + public Children notIn(boolean condition, String column, Object... values) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(values))); + } + + @Override + public Children inSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children gtSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GT, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children geSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GE, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children ltSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LT, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children leSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LE, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children eqSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), EQ, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children notInSql(boolean condition, String column, String inValue) { + return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, + () -> String.format("(%s)", inValue))); + } + + @Override + public Children groupBy(boolean condition, String column, String... 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 final Children orderBy(boolean condition, boolean isAsc, String column, String... 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(c), mode)); + } + }); + } + + @Override + public Children groupBy(boolean condition, String column) { + return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column))); + } + + @Override + public Children groupByStr(boolean condition, List columns) { + return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnsToString(columns))); + } + + @Override + public Children orderBy(boolean condition, boolean isAsc, String column) { + return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(column), + isAsc ? ASC : DESC)); + } + + @Override + public Children orderByStr(boolean condition, boolean isAsc, List columns) { + return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY, + columnToSqlSegment(c), isAsc ? ASC : DESC))); + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Compare.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Compare.java new file mode 100644 index 0000000..5512ff6 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Compare.java @@ -0,0 +1,98 @@ +package com.github.yulichang.wrapper.apt.interfaces; + +import com.github.yulichang.apt.Column; + +import java.io.Serializable; + +/** + * 将原来的泛型R改成Column + *

+ * {@link com.baomidou.mybatisplus.core.conditions.interfaces.Compare} + */ +@SuppressWarnings("unused") +public interface Compare extends Serializable { + + default Children eq(Column column, Object val) { + return eq(true, column, val); + } + + Children eq(boolean condition, Column column, Object val); + + default Children ne(Column column, Object val) { + return ne(true, column, val); + } + + Children ne(boolean condition, Column column, Object val); + + default Children gt(Column column, Object val) { + return gt(true, column, val); + } + + Children gt(boolean condition, Column column, Object val); + + default Children ge(Column column, Object val) { + return ge(true, column, val); + } + + Children ge(boolean condition, Column column, Object val); + + default Children lt(Column column, Object val) { + return lt(true, column, val); + } + + Children lt(boolean condition, Column column, Object val); + + default Children le(Column column, Object val) { + return le(true, column, val); + } + + Children le(boolean condition, Column column, Object val); + + default Children between(Column column, Object val1, Object val2) { + return between(true, column, val1, val2); + } + + Children between(boolean condition, Column column, Object val1, Object val2); + + default Children notBetween(Column column, Object val1, Object val2) { + return notBetween(true, column, val1, val2); + } + + Children notBetween(boolean condition, Column column, Object val1, Object val2); + + default Children like(Column column, Object val) { + return like(true, column, val); + } + + Children like(boolean condition, Column column, Object val); + + default Children notLike(Column column, Object val) { + return notLike(true, column, val); + } + + Children notLike(boolean condition, Column column, Object val); + + default Children likeLeft(Column column, Object val) { + return likeLeft(true, column, val); + } + + Children likeLeft(boolean condition, Column column, Object val); + + default Children notLikeLeft(Column column, Object val) { + return notLikeLeft(true, column, val); + } + + Children notLikeLeft(boolean condition, Column column, Object val); + + default Children likeRight(Column column, Object val) { + return likeRight(true, column, val); + } + + Children likeRight(boolean condition, Column column, Object val); + + default Children notLikeRight(Column column, Object val) { + return notLikeRight(true, column, val); + } + + Children notLikeRight(boolean condition, Column column, Object val); +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/CompareIfExists.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/CompareIfExists.java new file mode 100644 index 0000000..6dffb8e --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/CompareIfExists.java @@ -0,0 +1,66 @@ +package com.github.yulichang.wrapper.apt.interfaces; + +import com.github.yulichang.apt.Column; +import com.github.yulichang.wrapper.enums.IfExistsSqlKeyWordEnum; + +import java.util.function.BiPredicate; + +/** + * {@link com.baomidou.mybatisplus.core.conditions.interfaces.Compare} + * + * @author yulichang + * @since 1.4.9 + */ +@SuppressWarnings("unused") +public interface CompareIfExists extends Compare { + + BiPredicate getIfExists(); + + default Children eqIfExists(Column column, Object val) { + return eq(getIfExists().test(val, IfExistsSqlKeyWordEnum.EQ), column, val); + } + + default Children neIfExists(Column column, Object val) { + return ne(getIfExists().test(val, IfExistsSqlKeyWordEnum.NE), column, val); + } + + default Children gtIfExists(Column column, Object val) { + return gt(getIfExists().test(val, IfExistsSqlKeyWordEnum.GT), column, val); + } + + default Children geIfExists(Column column, Object val) { + return ge(getIfExists().test(val, IfExistsSqlKeyWordEnum.GE), column, val); + } + + default Children ltIfExists(Column column, Object val) { + return lt(getIfExists().test(val, IfExistsSqlKeyWordEnum.LT), column, val); + } + + default Children leIfExists(Column column, Object val) { + return le(getIfExists().test(val, IfExistsSqlKeyWordEnum.LE), column, val); + } + + default Children likeIfExists(Column column, Object val) { + return like(getIfExists().test(val, IfExistsSqlKeyWordEnum.LIKE), column, val); + } + + default Children notLikeIfExists(Column column, Object val) { + return notLike(getIfExists().test(val, IfExistsSqlKeyWordEnum.NOT_LIKE), column, val); + } + + default Children likeLeftIfExists(Column column, Object val) { + return likeLeft(getIfExists().test(val, IfExistsSqlKeyWordEnum.LIKE_LEFT), column, val); + } + + default Children notLikeLeftIfExists(Column column, Object val) { + return notLikeLeft(getIfExists().test(val, IfExistsSqlKeyWordEnum.NOT_LIKE_LEFT), column, val); + } + + default Children likeRightIfExists(Column column, Object val) { + return likeRight(getIfExists().test(val, IfExistsSqlKeyWordEnum.LIKE_RIGHT), column, val); + } + + default Children notLikeRightIfExists(Column column, Object val) { + return notLikeRight(getIfExists().test(val, IfExistsSqlKeyWordEnum.NOT_LIKE_RIGHT), column, val); + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Func.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Func.java new file mode 100644 index 0000000..8c46770 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Func.java @@ -0,0 +1,190 @@ +package com.github.yulichang.wrapper.apt.interfaces; + +import com.github.yulichang.apt.Column; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +/** + * 将原来的泛型R改成Column + *

+ * copy {@link com.baomidou.mybatisplus.core.conditions.interfaces.Func} + */ +@SuppressWarnings("unused") +public interface Func extends Serializable { + + + default Children isNull(Column column) { + return isNull(true, column); + } + + Children isNull(boolean condition, Column column); + + + default Children isNotNull(Column column) { + return isNotNull(true, column); + } + + Children isNotNull(boolean condition, Column column); + + + default Children in(Column column, Collection coll) { + return in(true, column, coll); + } + + Children in(boolean condition, Column column, Collection coll); + + + default Children in(Column column, Object... values) { + return in(true, column, values); + } + + Children in(boolean condition, Column column, Object... values); + + + default Children notIn(Column column, Collection coll) { + return notIn(true, column, coll); + } + + Children notIn(boolean condition, Column column, Collection coll); + + + default Children notIn(Column column, Object... values) { + return notIn(true, column, values); + } + + Children notIn(boolean condition, Column column, Object... values); + + + default Children inSql(Column column, String inValue) { + return inSql(true, column, inValue); + } + + Children inSql(boolean condition, Column column, String inValue); + + + default Children notInSql(Column column, String inValue) { + return notInSql(true, column, inValue); + } + + Children notInSql(boolean condition, Column column, String inValue); + + default Children gtSql(Column column, String inValue) { + return gtSql(true, column, inValue); + } + + Children gtSql(boolean condition, Column column, String inValue); + + default Children geSql(Column column, String inValue) { + return geSql(true, column, inValue); + } + + Children geSql(boolean condition, Column column, String inValue); + + default Children ltSql(Column column, String inValue) { + return ltSql(true, column, inValue); + } + + Children ltSql(boolean condition, Column column, String inValue); + + default Children leSql(Column column, String inValue) { + return leSql(true, column, inValue); + } + + Children leSql(boolean condition, Column column, String inValue); + + default Children eqSql(Column column, String inValue) { + return eqSql(true, column, inValue); + } + + Children eqSql(boolean condition, Column column, String inValue); + + default Children groupBy(Column column) { + return groupBy(true, column); + } + + default Children groupBy(List column) { + return groupBy(true, column); + } + + Children groupBy(boolean condition, List columns); + + default Children groupBy(Column column, Column... columns) { + return groupBy(true, column, columns); + } + + Children groupBy(boolean condition, Column column, Column... columns); + + + default Children orderByAsc(Column column) { + return orderByAsc(true, column); + } + + default Children orderByAsc(List columns) { + return orderByAsc(true, columns); + } + + Children orderByAsc(boolean condition, List columns); + + + default Children orderByAsc(Column column, Column... columns) { + return orderByAsc(true, column, columns); + } + + + default Children orderByAsc(boolean condition, Column column, Column... columns) { + return orderBy(condition, true, column, columns); + } + + default Children orderByDesc(Column column) { + return orderByDesc(true, column); + } + + default Children orderByDesc(List columns) { + return orderByDesc(true, columns); + } + + Children orderByDesc(boolean condition, List columns); + + default Children orderByDesc(Column column, Column... columns) { + return orderByDesc(true, column, columns); + } + + default Children orderByDesc(boolean condition, Column column, Column... columns) { + return orderBy(condition, false, column, columns); + } + + Children orderBy(boolean condition, boolean isAsc, Column column, Column... columns); + + default Children having(String sqlHaving, Object... params) { + return having(true, sqlHaving, params); + } + + /** + * HAVING ( sql语句 ) + *

例1: having("sum(age) > 10")

+ *

例2: having("sum(age) > {0}", 10)

+ * + * @param condition 执行条件 + * @param sqlHaving sql 语句 + * @param params 参数数组 + * @return children + */ + Children having(boolean condition, String sqlHaving, Object... params); + + + default Children func(Consumer consumer) { + return func(true, consumer); + } + + /** + * 消费函数 + * + * @param consumer 消费函数 + * @return children + * @since 3.3.1 + */ + Children func(boolean condition, Consumer consumer); +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/OnCompare.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/OnCompare.java new file mode 100644 index 0000000..54d8d27 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/OnCompare.java @@ -0,0 +1,101 @@ +package com.github.yulichang.wrapper.apt.interfaces; + +import com.github.yulichang.apt.Column; + +import java.io.Serializable; + +/** + * 将原来的泛型R改成Column, Object改为Column + * 以及移除不会在ON语句中出现的条件 比如like相关 保留原来的like 只是不太可能会出现 on a.id like b.id 所以不会支持这种写法 + *

+ * {@link com.baomidou.mybatisplus.core.conditions.interfaces.Compare} + * + * @since 1.1.8 + */ +@SuppressWarnings("unused") +public interface OnCompare extends Serializable { + + default Children eq(Column column, Column val) { + return eq(true, column, val); + } + + /** + * 等于 = + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children eq(boolean condition, Column column, Column val); + + default Children ne(Column column, Column val) { + return ne(true, column, val); + } + + /** + * 不等于 <> + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children ne(boolean condition, Column column, Column val); + + default Children gt(Column column, Column val) { + return gt(true, column, val); + } + + /** + * 大于 > + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children gt(boolean condition, Column column, Column val); + + default Children ge(Column column, Column val) { + return ge(true, column, val); + } + + /** + * 大于等于 >= + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children ge(boolean condition, Column column, Column val); + + default Children lt(Column column, Column val) { + return lt(true, column, val); + } + + /** + * 小于 < + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children lt(boolean condition, Column column, Column val); + + default Children le(Column column, Column val) { + return le(true, column, val); + } + + /** + * 小于等于 <= + * + * @param condition 执行条件 + * @param column 字段 + * @param val 值 + * @return children + */ + Children le(boolean condition, Column column, Column val); +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Query.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Query.java new file mode 100644 index 0000000..1022380 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/apt/interfaces/Query.java @@ -0,0 +1,283 @@ +package com.github.yulichang.wrapper.apt.interfaces; + +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.github.yulichang.apt.BaseColumn; +import com.github.yulichang.apt.Column; +import com.github.yulichang.toolkit.Constant; +import com.github.yulichang.toolkit.LambdaUtils; +import com.github.yulichang.toolkit.MPJReflectionKit; +import com.github.yulichang.toolkit.TableHelper; +import com.github.yulichang.toolkit.support.ColumnCache; +import com.github.yulichang.toolkit.support.FieldCache; +import com.github.yulichang.wrapper.enums.BaseFuncEnum; +import com.github.yulichang.wrapper.enums.DefaultFuncEnum; +import com.github.yulichang.wrapper.segments.*; + +import java.io.Serializable; +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * 参考 {@link com.baomidou.mybatisplus.core.conditions.query.Query} + * + * @author yulichang + */ +@SuppressWarnings({"unused"}) +public interface Query extends Serializable { + + List getSelectColumns(); - Children selectAll(Class clazz); + Children selectAll(); boolean isResultMap(); - List getResultMapMybatisLabel(); + List> getResultMapMybatisLabel(); String getFrom(); diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/resultmap/IResult.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/resultmap/IResult.java index fdfb957..8d012cd 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/resultmap/IResult.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/resultmap/IResult.java @@ -1,5 +1,6 @@ package com.github.yulichang.wrapper.resultmap; +import com.github.yulichang.apt.BaseColumn; import com.github.yulichang.wrapper.segments.SelectCache; import org.apache.ibatis.type.JdbcType; @@ -16,4 +17,8 @@ public interface IResult { Class getJavaType(); JdbcType getJdbcType(); + + default BaseColumn getBaseColumn() { + return null; + } } diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/AptConsumer.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/AptConsumer.java new file mode 100644 index 0000000..8f74da6 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/AptConsumer.java @@ -0,0 +1,20 @@ +package com.github.yulichang.wrapper.segments; + +import com.github.yulichang.apt.Column; + +/** + * 用于selectFunc 和 applyFunc中的参数填充 + * 从原来的 {@link SelectFunc} 里的内部类中提取出来 + * + * @author yulichang + * @since 1.4.13 + */ +public class AptConsumer { + + public static final AptConsumer func = new AptConsumer(); + + public final Column[] accept(Column... a) { + return a; + } + +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/Select.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/Select.java index 5d1e0cb..4d9bf51 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/Select.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/Select.java @@ -1,6 +1,8 @@ package com.github.yulichang.wrapper.segments; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.github.yulichang.apt.BaseColumn; +import com.github.yulichang.apt.Column; import com.github.yulichang.wrapper.enums.BaseFuncEnum; import org.apache.ibatis.type.TypeHandler; @@ -51,4 +53,12 @@ public interface Select extends Serializable { boolean isLabel(); boolean isStr(); + + default Column[] getColumns() { + return null; + } + + default BaseColumn getBaseColumn() { + return null; + } } diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectApt.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectApt.java new file mode 100644 index 0000000..3b34896 --- /dev/null +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectApt.java @@ -0,0 +1,228 @@ +package com.github.yulichang.wrapper.segments; + +import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.github.yulichang.apt.BaseColumn; +import com.github.yulichang.apt.Column; +import com.github.yulichang.wrapper.enums.BaseFuncEnum; +import lombok.Getter; +import lombok.Setter; +import org.apache.ibatis.type.TypeHandler; + +/** + * 缓存列 + * + * @author yulichang + * @since 1.5.0 + */ + +public class SelectApt implements Select { + + private final SelectCache cache; + + @Getter + @Setter + private BaseColumn baseColumn; + + private final boolean isFunc; + + private final BaseFuncEnum func; + + private final boolean hasAlias; + + private final String alias; + + @Getter + private final Column[] columns; + + /** + * select(col) + */ + public SelectApt(SelectCache cache, Column column) { + this.cache = cache; + this.baseColumn = column.getRoot(); + this.isFunc = false; + this.func = null; + this.columns = null; + this.hasAlias = false; + this.alias = null; + } + + /** + * select(col) + */ + public SelectApt(SelectCache cache, BaseColumn baseColumn) { + this.cache = cache; + this.baseColumn = baseColumn; + this.isFunc = false; + this.func = null; + this.columns = null; + this.hasAlias = false; + this.alias = null; + } + + /** + * selectAs(col,alias) + */ + public SelectApt(SelectCache cache, Column column, String alias) { + this.cache = cache; + this.baseColumn = column.getRoot(); + this.isFunc = false; + this.func = null; + this.columns = null; + this.hasAlias = true; + this.alias = alias; + } + + /** + * select(()->"", col) + */ + public SelectApt(SelectCache cache, Column column, BaseFuncEnum baseFuncEnum, String alias) { + this.cache = cache; + this.baseColumn = column.getRoot(); + this.isFunc = true; + this.func = baseFuncEnum; + this.columns = new Column[]{column}; + this.hasAlias = true; + this.alias = alias; + } + + /** + * select("", arg.accept(col...)) + */ + public SelectApt(Column[] columns, BaseFuncEnum baseFuncEnum, String alias) { + this.cache = null; + this.baseColumn = null; + this.isFunc = true; + this.func = baseFuncEnum; + this.columns = columns; + this.hasAlias = true; + this.alias = alias; + } + + + @Override + public boolean isHasTableAlias() { + return true; + } + + @Override + public String getTableAlias() { + if (baseColumn != null) { + return baseColumn.getAlias(); + } + return null; + } + + + @Override + public Class getClazz() { + if (cache != null) { + return cache.getClazz(); + } + return null; + } + + @Override + public Integer getIndex() { + return null; + } + + @Override + public boolean isPk() { + if (cache != null) { + return cache.isPk(); + } + return false; + } + + @Override + public String getColumn() { + if (cache != null) { + return cache.getColumn(); + } + return null; + } + + @Override + public Class getColumnType() { + if (cache != null) { + return cache.getColumnType(); + } + return null; + } + + @Override + public String getTagColumn() { + if (cache != null) { + return cache.getTagColumn(); + } + return null; + } + + @Override + public String getColumProperty() { + if (cache != null) { + return cache.getColumProperty(); + } + return null; + } + + @Override + public boolean hasTypeHandle() { + if (cache != null) { + return cache.isHasTypeHandle(); + } + return false; + } + + @Override + public TypeHandler getTypeHandle() { + if (cache != null) { + return cache.getTypeHandler(); + } + return null; + } + + @Override + public boolean isHasAlias() { + return this.hasAlias; + } + + @Override + public String getAlias() { + return this.alias; + } + + @Override + public TableFieldInfo getTableFieldInfo() { + if (cache != null) { + return cache.getTableFieldInfo(); + } + return null; + } + + @Override + public boolean isFunc() { + return this.isFunc; + } + + @Override + public SelectFunc.Arg[] getArgs() { + return null; + } + + @Override + public BaseFuncEnum getFunc() { + return func; + } + + @Override + public boolean isLabel() { + return false; + } + + @Override + public boolean isStr() { + return false; + } +} diff --git a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectLabel.java b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectLabel.java index 148c9e4..a256140 100644 --- a/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectLabel.java +++ b/mybatis-plus-join-core/src/main/java/com/github/yulichang/wrapper/segments/SelectLabel.java @@ -2,6 +2,7 @@ package com.github.yulichang.wrapper.segments; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.github.yulichang.apt.BaseColumn; import com.github.yulichang.wrapper.enums.BaseFuncEnum; import lombok.Getter; import org.apache.ibatis.type.TypeHandler; @@ -29,24 +30,28 @@ public class SelectLabel implements Select { private final String tableAlias; - public SelectLabel(SelectCache cache, Integer index, Class tagClass, boolean hasTableAlias, String tableAlias) { + private final BaseColumn baseColumn; + + public SelectLabel(SelectCache cache, Integer index, Class tagClass, boolean hasTableAlias, String tableAlias, BaseColumn baseColumn) { this.cache = cache; this.index = index; this.tagClass = tagClass; this.hasAlias = false; this.alias = null; this.hasTableAlias = hasTableAlias; - this.tableAlias = tableAlias; + this.tableAlias = hasTableAlias ? tableAlias : null; + this.baseColumn = baseColumn; } - public SelectLabel(SelectCache cache, Integer index, Class tagClass, String column, boolean hasTableAlias, String tableAlias) { + public SelectLabel(SelectCache cache, Integer index, Class tagClass, String column, boolean hasTableAlias, String tableAlias, BaseColumn baseColumn) { this.cache = cache; this.index = index; this.tagClass = tagClass; this.hasAlias = true; this.alias = column; this.hasTableAlias = hasTableAlias; - this.tableAlias = tableAlias; + this.tableAlias = hasTableAlias ? tableAlias : null; + this.baseColumn = baseColumn; } @Override diff --git a/mybatis-plus-join-processor/pom.xml b/mybatis-plus-join-processor/pom.xml new file mode 100644 index 0000000..2869f36 --- /dev/null +++ b/mybatis-plus-join-processor/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + com.github.yulichang + mybatis-plus-join-root + ${revision} + + + mybatis-plus-join-processor + ${revision} + jar + + + 8 + 8 + UTF-8 + + + + + com.github.yulichang + mybatis-plus-join-core + 1.4.13 + + + com.baomidou + mybatis-plus-annotation + ${mpj.mybatis.plus.version} + + + org.mybatis + mybatis + 3.5.16 + + + + + + + src/main/resources + + **/* + + + + *.properties + + + + + target/generated-sources + + + + + + maven-compiler-plugin + 3.8.1 + + none + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/EntityProcessor.java b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/EntityProcessor.java new file mode 100644 index 0000000..446fd15 --- /dev/null +++ b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/EntityProcessor.java @@ -0,0 +1,266 @@ +package com.github.yulichang.processor; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.github.yulichang.annotation.Table; +import com.github.yulichang.apt.BaseColumn; +import com.github.yulichang.apt.Column; +import com.github.yulichang.processor.matedata.FieldInfo; +import com.github.yulichang.processor.matedata.TableInfo; +import com.github.yulichang.processor.utils.StringUtil; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.io.Writer; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * @author yulichang + * @since 1.5.0 + */ +public class EntityProcessor extends AbstractProcessor { + + private Elements elementUtils; + private Types typeUtils; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + this.elementUtils = processingEnv.getElementUtils(); + this.typeUtils = processingEnv.getTypeUtils(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + roundEnv.getElementsAnnotatedWith(Table.class).stream().filter(f -> f instanceof TypeElement) + .map(f -> (TypeElement) f).map(this::createColumn) + .collect(Collectors.groupingBy(TableInfo::getClassPackage)) + .forEach(this::createTables); + } + return false; + } + + + @Override + public Set getSupportedAnnotationTypes() { + Set supportedAnnotationTypes = new HashSet<>(); + supportedAnnotationTypes.add(Table.class.getCanonicalName()); + return supportedAnnotationTypes; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + /** + * 生成Column类 + */ + private TableInfo createColumn(TypeElement element) { + TableInfo tableInfo = new TableInfo(element.getAnnotation(Table.class)); + tableInfo.setClassName(element.toString()); + tableInfo.setSimpleClassName(element.getSimpleName().toString()); + tableInfo.setClassPackage(elementUtils.getPackageOf(element).getQualifiedName().toString()); + tableInfo.setClassComment(elementUtils.getDocComment(element)); + + Set fieldInfos = new HashSet<>(); + + TypeElement currElement = element; + do { + fieldInfos.addAll(currElement.getEnclosedElements().stream().filter(e -> + // 过滤static字段 + e.getKind() == ElementKind.FIELD && !e.getModifiers().contains(Modifier.STATIC)) + .filter(e -> { + // 过滤 exist = false 的字段 + TableField tableField = e.getAnnotation(TableField.class); + return tableField == null || tableField.exist(); + }) + .map(e -> new FieldInfo(e.toString(), elementUtils.getDocComment(e))).collect(Collectors.toList())); + currElement = (TypeElement) typeUtils.asElement(currElement.getSuperclass()); + } while (currElement != null); + + tableInfo.setFields(fieldInfos); + + StringBuilderHelper content = new StringBuilderHelper() + .addPackage(tableInfo.getTagPackage()) + .newLine() + .addImport(BaseColumn.class.getName()) + .addImport(Column.class.getName()) + .addImport(tableInfo.getClassName()) + .newLine() + .addClass(tableInfo.getClassComment(), tableInfo.getTagClassName(), + BaseColumn.class.getSimpleName() + "<" + tableInfo.getSimpleClassName() + ">", + c -> c + .addDefaultField(tableInfo.getSimpleClassName()) + .addConstructor(tableInfo) + .addFields(tableInfo) + .addMethod(tableInfo) + ); + writerFile(tableInfo.getTagPackage() + "." + tableInfo.getTagClassName(), content.getContent()); + return tableInfo; + } + + /** + * 生成Tables + */ + private void createTables(String tagPackage, List tableInfos) { + StringBuilderHelper content = new StringBuilderHelper(); + // package + content.addPackage(tagPackage + ".tables"); + content.newLine(); + // import + tableInfos.forEach(tableInfo -> content.addImport(tableInfo.getTagPackage() + "." + tableInfo.getTagClassName())); + content.newLine(); + // class + String tables = "Tables"; + content.addClass(" tables", tables, "", c -> c + .newLine() + // 私有构造 + .addPrivateConstructor(tables) + // 添加table字段 + .addTablesFields(tableInfos)); + + writerFile(tagPackage + ".tables.Tables", content.getContent()); + } + + private void writerFile(String fullClassName, String content) { + try { + JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(fullClassName); + + Writer writer = sourceFile.openWriter(); + writer.write(content); + writer.flush(); + writer.close(); + } catch (Exception e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); + } + } + + @SuppressWarnings("UnusedReturnValue") + public static class StringBuilderHelper { + private final StringBuilder sb = new StringBuilder(); + + public StringBuilderHelper addPackage(String packageName) { + sb.append("package ").append(packageName).append(";\n"); + return this; + } + + public StringBuilderHelper addImport(String importName) { + sb.append("import ").append(importName).append(";\n"); + return this; + } + + public StringBuilderHelper addClass(String comment, String tagClassName, String impl, + Consumer consumer) { + addComment("", comment); + sb.append("public class ").append(tagClassName); + if (StringUtil.isNotEmpty(impl)) { + sb.append(" implements ").append(impl); + } + sb.append(" {\n"); + consumer.accept(this); + sb.append("}\n"); + return this; + } + + public StringBuilderHelper addConstructor(TableInfo tableInfo) { + // 无参构造 + sb.append(String.format("\tpublic %s() {\n\t}\n", tableInfo.getTagClassName())); + newLine(); + //有参构造 + sb.append("\tpublic ").append(tableInfo.getTagClassName()).append("(String alias) {\n" + + "\t\tthis._alias_q2Gv$ = alias;\n" + + "\t}\n"); + newLine(); + return this; + } + + /** + * 私有构造 + */ + public StringBuilderHelper addPrivateConstructor(String tagClassName) { + sb.append(String.format("\tprivate %s() {\n\t}\n", tagClassName)); + newLine(); + return this; + } + + public StringBuilderHelper addFields(TableInfo tableInfo) { + tableInfo.getFields().forEach(fieldInfo -> { + addComment("\t", fieldInfo.getComment()); + sb.append(String.format("\tpublic final Column %s = new Column(this, %s.class, \"%s\", () -> this._alias_q2Gv$);\n", + fieldInfo.getProperty(), tableInfo.getSimpleClassName(), fieldInfo.getProperty())); + newLine(); + }); + return this; + } + + public StringBuilderHelper addTablesFields(List tableInfos) { + tableInfos.forEach(tableInfo -> { + addComment("\t", tableInfo.getClassComment()); + sb.append(String.format("\tpublic static final %s %s = new %s();\n", + tableInfo.getTagClassName(), + String.format(tableInfo.getTableAnnotation().tablesName(), tableInfo.getSimpleClassName()), + tableInfo.getTagClassName())); + newLine(); + }); + return this; + } + + public StringBuilderHelper addDefaultField(String simpleClassName) { + newLine(); + sb.append("\tprivate static final Class<").append(simpleClassName).append("> _class_e76G$ = ").append(simpleClassName).append(".class;\n"); + newLine(); + sb.append("\tprivate String _alias_q2Gv$;\n"); + newLine(); + return this; + } + + public StringBuilderHelper addMethod(TableInfo tableInfo) { + sb.append("\t@Override\n" + + "\tpublic Class<").append(tableInfo.getSimpleClassName()).append("> getColumnClass() {\n") + .append("\t\treturn this._class_e76G$;\n") + .append("\t}\n"); + newLine(); + sb.append("\t@Override\n" + + "\tpublic String getAlias() {\n" + + "\t\treturn this._alias_q2Gv$;\n" + + "\t}\n"); + newLine(); + return this; + } + + private StringBuilderHelper addComment(String prefix, String comment) { + if (StringUtil.isNotEmpty(comment)) { + sb.append(prefix).append("/**\n"); + sb.append(Arrays.stream(comment.split("\n")).map(f -> prefix + " *" + f).collect(Collectors.joining("\n"))); + sb.append("\n").append(prefix).append(" */"); + newLine(); + } + return this; + } + + public StringBuilderHelper newLine() { + sb.append("\n"); + return this; + } + + public String getContent() { + return sb.toString(); + } + + } +} \ No newline at end of file diff --git a/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/FieldInfo.java b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/FieldInfo.java new file mode 100644 index 0000000..2652951 --- /dev/null +++ b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/FieldInfo.java @@ -0,0 +1,56 @@ +package com.github.yulichang.processor.matedata; + +import java.util.Objects; + +/** + * @author yulichang + * @since 1.5.0 + */ +@SuppressWarnings("unused") +public class FieldInfo { + + /** + * 属性名。 + */ + private String property; + + /** + * 注释。 + */ + private String comment; + + public FieldInfo(String property, String comment) { + this.property = property; + this.comment = comment; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FieldInfo fieldInfo = (FieldInfo) o; + return Objects.equals(property, fieldInfo.property); + } + + @Override + public int hashCode() { + return Objects.hash(property); + } + +} diff --git a/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/TableInfo.java b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/TableInfo.java new file mode 100644 index 0000000..a4dd36c --- /dev/null +++ b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/matedata/TableInfo.java @@ -0,0 +1,109 @@ +package com.github.yulichang.processor.matedata; + +import com.github.yulichang.annotation.Table; +import com.github.yulichang.processor.utils.StringUtil; + +import java.util.Set; + +/** + * @author yulichang + * @since 1.5.0 + */ +@SuppressWarnings("unused") +public class TableInfo { + + public TableInfo(Table tableAnnotation) { + this.tableAnnotation = tableAnnotation; + } + + private String className; + + private String simpleClassName; + + private String classComment; + + private String classPackage; + + private Table tableAnnotation; + + private Set fields; + + /** + * 生成的类名 + */ + public String getTagClassName() { + String tag = simpleClassName; + if (StringUtil.isNotEmpty(tableAnnotation.prefix()) || StringUtil.isNotEmpty(tableAnnotation.suffix())) { + tag = tableAnnotation.prefix() + tag + tableAnnotation.suffix(); + } else { + tag = String.format(tableAnnotation.format(), tag); + } + return tag; + } + + /** + * 生成类的路径 + */ + public String getTagPackage() { + return classPackage + "." + tableAnnotation.packageName(); + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getSimpleClassName() { + return simpleClassName; + } + + public void setSimpleClassName(String simpleClassName) { + this.simpleClassName = simpleClassName; + } + + public String getClassComment() { + return classComment; + } + + public void setClassComment(String classComment) { + this.classComment = classComment; + } + + public String getClassPackage() { + return classPackage; + } + + public void setClassPackage(String classPackage) { + this.classPackage = classPackage; + } + + public Table getTableAnnotation() { + return tableAnnotation; + } + + public void setTableAnnotation(Table tableAnnotation) { + this.tableAnnotation = tableAnnotation; + } + + public Set getFields() { + return fields; + } + + public void setFields(Set fields) { + this.fields = fields; + } + + @Override + public String toString() { + return "TableInfo{" + + "className='" + className + '\'' + + ", simpleClassName='" + simpleClassName + '\'' + + ", classComment='" + classComment + '\'' + + ", classPackage='" + classPackage + '\'' + + ", fields=" + fields + + '}'; + } +} diff --git a/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/utils/StringUtil.java b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/utils/StringUtil.java new file mode 100644 index 0000000..f8d9f81 --- /dev/null +++ b/mybatis-plus-join-processor/src/main/java/com/github/yulichang/processor/utils/StringUtil.java @@ -0,0 +1,12 @@ +package com.github.yulichang.processor.utils; + +public final class StringUtil { + + public static boolean isEmpty(String str) { + return str == null || str.isEmpty(); + } + + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } +} diff --git a/mybatis-plus-join-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/mybatis-plus-join-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..391f570 --- /dev/null +++ b/mybatis-plus-join-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +com.github.yulichang.processor.EntityProcessor \ No newline at end of file diff --git a/mybatis-plus-join-test/test-join/pom.xml b/mybatis-plus-join-test/test-join/pom.xml index c5a36a3..d0c3e6e 100644 --- a/mybatis-plus-join-test/test-join/pom.xml +++ b/mybatis-plus-join-test/test-join/pom.xml @@ -41,4 +41,30 @@ jackson-core + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + com.github.yulichang + mybatis-plus-join-processor + 1.4.13 + + + org.projectlombok + lombok + 1.18.30 + + + + + + diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AddressDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AddressDO.java index 35bb725..9100cc0 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AddressDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AddressDO.java @@ -2,11 +2,13 @@ package com.github.yulichang.test.join.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.*; import lombok.experimental.Accessors; import java.io.Serializable; +@Table @Data @EqualsAndHashCode(callSuper = true) @ToString diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AreaDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AreaDO.java index ca6252b..5394164 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AreaDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/AreaDO.java @@ -3,6 +3,7 @@ package com.github.yulichang.test.join.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -10,6 +11,7 @@ import lombok.experimental.Accessors; import java.io.Serializable; +@Table @Data @ToString @Accessors(chain = true) diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/OrderDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/OrderDO.java index 3c7f5b4..860620e 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/OrderDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/OrderDO.java @@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.annotation.OrderBy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.Data; import lombok.ToString; import lombok.experimental.Accessors; import java.io.Serializable; +@Table @Data @ToString @Accessors(chain = true) diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDO.java index 6d574cd..e013528 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDO.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.github.yulichang.annotation.DynamicTableName; +import com.github.yulichang.annotation.Table; import com.github.yulichang.test.join.enums.Sex; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,6 +18,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Map; +@Table @Data @ToString @Accessors(chain = true) diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDto.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDto.java index e8ed336..73de19f 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDto.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserDto.java @@ -3,10 +3,12 @@ package com.github.yulichang.test.join.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.Data; import java.io.Serializable; +@Table @Data @TableName("user_dto") public class UserDto implements Serializable { diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantDO.java index f9397e3..3043f6f 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantDO.java @@ -4,10 +4,12 @@ package com.github.yulichang.test.join.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.Data; import lombok.ToString; import lombok.experimental.FieldNameConstants; +@Table @Data @ToString @FieldNameConstants diff --git a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantaDO.java b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantaDO.java index c792fc5..539e684 100644 --- a/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantaDO.java +++ b/mybatis-plus-join-test/test-join/src/main/java/com/github/yulichang/test/join/entity/UserTenantaDO.java @@ -4,14 +4,16 @@ package com.github.yulichang.test.join.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.github.yulichang.annotation.Table; import lombok.Data; import lombok.ToString; import lombok.experimental.FieldNameConstants; +@Table @Data @ToString @FieldNameConstants -@TableName(value = "user_tenanta",autoResultMap = true) +@TableName(value = "user_tenanta", autoResultMap = true) public class UserTenantaDO { @TableId("id") diff --git a/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/apt/AptWrapperTest.java b/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/apt/AptWrapperTest.java new file mode 100644 index 0000000..dff5e0c --- /dev/null +++ b/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/apt/AptWrapperTest.java @@ -0,0 +1,1124 @@ +package com.github.yulichang.test.join.apt; + +import com.baomidou.mybatisplus.core.MybatisPlusVersion; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.adapter.base.tookit.VersionUtils; +import com.github.yulichang.test.join.dto.AddressDTO; +import com.github.yulichang.test.join.dto.UserDTO; +import com.github.yulichang.test.join.dto.UserTenantDTO; +import com.github.yulichang.test.join.dto.UserTenantDescDTO; +import com.github.yulichang.test.join.entity.*; +import com.github.yulichang.test.join.entity.apt.*; +import com.github.yulichang.test.join.mapper.*; +import com.github.yulichang.test.util.Reset; +import com.github.yulichang.test.util.ThreadLocalUtils; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.apt.AptQueryWrapper; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.BadSqlGrammarException; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 连表测试类 + *

+ * 支持mybatis-plus 查询枚举字段 + * 支持mybatis-plus typeHandle功能 + *

+ * 移除了mybatis-plus 逻辑删除支持,逻辑删除需要在连表查询时自己添加对应的条件 + */ +@SuppressWarnings("unused") +@SpringBootTest +class AptWrapperTest { + @Autowired + private UserMapper userMapper; + + @Autowired + private UserDTOMapper userDTOMapper; + + @Autowired + private AddressMapper addressMapper; + + @Autowired + private OrderMapper orderMapper; + + @Autowired + private UserTenantMapper userTenantMapper; + + @BeforeEach + void setUp() { + Reset.reset(); + } + + @Test + void testSelectSort() { + ThreadLocalUtils.set("SELECT t.id, t.user_id, t.tenant_id FROM user_tenant t WHERE t.tenant_id = 1"); + + UserTenantDOCol ut = new UserTenantDOCol(); + + AptQueryWrapper lambda = JoinWrappers.apt(ut) + .selectAsClass(ut, UserTenantDTO.class); + List list = userTenantMapper.selectJoinList(UserTenantDO.class, lambda); + assert list.size() == 5 && list.get(0).getIdea() != null; + + + ThreadLocalUtils.set("SELECT t.tenant_id, t.user_id, t.id FROM user_tenant t WHERE t.tenant_id = 1"); + AptQueryWrapper lambda1 = JoinWrappers.apt(ut) + .selectAsClass(ut, UserTenantDescDTO.class); + List list1 = userTenantMapper.selectJoinList(UserTenantDO.class, lambda1); + assert list1.size() == 5 && list1.get(0).getIdea() != null; + } + + @Test + void testSimple() { + UserTenantDOCol ut = new UserTenantDOCol(); + AptQueryWrapper lambda = JoinWrappers.apt(ut); + lambda.selectAs(ut.idea, UserTenantDO::getIdea); + List list = userTenantMapper.selectList(lambda); + + assert list.size() == 5 && list.get(0).getIdea() != null; + } + + @Test + void testJoin() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id,\n" + + " t1.user_id,\n" + + " t1.area_id,\n" + + " t1.tel,\n" + + " t1.address,\n" + + " t1.del AS joina_del,\n" + + " t2.id AS joinb_id,\n" + + " t2.province,\n" + + " t2.city,\n" + + " t2.area,\n" + + " t2.postcode,\n" + + " t2.del AS joinb_del\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND t2.del = false\n" + + " AND (t.id <= ?)\n" + + "ORDER BY t.id DESC"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .selectAll() + .selectCollection(addr, UserDTO::getAddressList, ad -> ad + .association(ar, AddressDTO::getArea)) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId) + .le(u.id, 10000) + .orderByDesc(u.id); + + List list = userMapper.selectJoinList(UserDTO.class, wrapper); + + assert wrapper.checkJoinTable(AddressDO.class); + assert wrapper.checkJoinTable(AreaDO.class); + assert !wrapper.checkJoinTable(UserDO.class); + assert !wrapper.checkJoinTable(UserDto.class); + + assert list.get(0).getAddressList() != null && list.get(0).getAddressList().get(0).getId() != null; + list.forEach(System.out::println); + } + + @Test + void testJoinField() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND (t.id <= ?)\n" + + "ORDER BY t.id DESC"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .selectAll() + .selectCollection(addr, UserDTO::getAddressIds, e -> e + .id(addr.id)) + .leftJoin(addr, addr.userId, u.id) + .le(u.id, 10000) + .orderByDesc(u.id); + List list = userMapper.selectJoinList(UserDTO.class, wrapper); + + assert list.get(0).getAddressIds() != null; + list.forEach(System.out::println); + } + + + @Test + void testJoin1() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id,\n" + + " t1.user_id,\n" + + " t1.area_id,\n" + + " t1.tel,\n" + + " t1.address,\n" + + " t1.del AS joina_del,\n" + + " t2.id AS joinb_id,\n" + + " t2.province,\n" + + " t2.city,\n" + + " t2.area,\n" + + " t2.postcode,\n" + + " t2.del AS joinb_del\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND t2.del = false\n" + + "ORDER BY t.id DESC"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .selectAll() + .selectCollection(addr, UserDTO::getAddressList, ad -> ad + .association(ar, AddressDTO::getArea)) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId) + .orderByDesc(u.id); + List list = userMapper.selectJoinList(UserDTO.class, wrapper); + + assert list.get(0).getAddressList().get(0).getId() != null; + list.forEach(System.out::println); + } + + /** + * 基本数据类型测试 + */ + @Test + void testWrapper() { + ThreadLocalUtils.set("SELECT t.id\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND t2.del = false"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + //基本数据类型 和 String + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .select(u.id) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId); + List list = userMapper.selectJoinList(Integer.class, wrapper); + + assert list.get(0) != null; + System.out.println(list); + + + ThreadLocalUtils.set("SELECT t.create_time\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND t2.del = false"); + //java.sql包下的类 + AptQueryWrapper wrapper1 = new AptQueryWrapper<>(u) + .select(u.createTime) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId); + List list1 = userMapper.selectJoinList(Timestamp.class, wrapper1); + + assert list1.get(0) != null; + System.out.println(list); + } + + + @Test + void testMSCache() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by\n" + + "FROM `user` t\n" + + "WHERE t.id = ?\n" + + " AND t.del = false\n" + + " AND (t.id <= ?)\n" + + "ORDER BY t.id ASC, t.`name` ASC"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + UserDO userDO = new UserDO(); + userDO.setId(1); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u, userDO) + .selectAll() + .le(u.id, 100) + .orderByAsc(u.id, u.name); + + List list = userMapper.selectList(wrapper); + list.forEach(System.out::println); + } + + @Test + void testTableAliasR() { + ThreadLocalUtils.set("SELECT tt.id,\n" + + " tt.user_id,\n" + + " tt.create_by,\n" + + " tt.update_by,\n" + + " ua.`name` AS userName,\n" + + " ub.`name` AS createName,\n" + + " uc.`name` AS updateName\n" + + "FROM user_dto tt\n" + + " LEFT JOIN `user` ua ON (ua.id = tt.user_id)\n" + + " LEFT JOIN `user` ub ON (ub.id = tt.create_by)\n" + + " LEFT JOIN `user` uc ON (uc.id = tt.update_by)\n" + + "WHERE ua.del = false\n" + + " AND ub.del = false\n" + + " AND uc.del = false\n" + + " AND (ua.id <= ? AND ub.id >= ?)"); + + UserDtoCol tt = new UserDtoCol("tt"); + UserDOCol ua = new UserDOCol("ua"); + UserDOCol ub = new UserDOCol("ub"); + UserDOCol uc = new UserDOCol("uc"); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(tt) + .selectAll() + .selectAs(ua.name, UserDto::getUserName) + .selectAs(ub.name, UserDto::getCreateName) + .selectAs(uc.name, UserDto::getUpdateName) + .leftJoin(ua, ua.id, tt.userId) + .leftJoin(ub, ub.id, tt.createBy) + .leftJoin(uc, uc.id, tt.updateBy) + .le(ua.id, 100) + .ge(ub.id, 0); + + List userDtos = userDTOMapper.selectJoinList(UserDto.class, wrapper); + assert StringUtils.isNotBlank(userDtos.get(0).getUserName()); + assert StringUtils.isNotBlank(userDtos.get(0).getCreateName()); + assert StringUtils.isNotBlank(userDtos.get(0).getUpdateName()); + + + ThreadLocalUtils.set("SELECT tt.id,\n" + + " tt.pid,\n" + + " tt.`name`,\n" + + " tt.`json`,\n" + + " tt.sex,\n" + + " tt.head_img,\n" + + " tt.create_time,\n" + + " tt.address_id,\n" + + " tt.address_id2,\n" + + " tt.del,\n" + + " tt.create_by,\n" + + " tt.update_by,\n" + + " ua.id,\n" + + " ub.head_img\n" + + "FROM `user` tt\n" + + " LEFT JOIN `user` ua ON (ua.id = tt.pid)\n" + + " LEFT JOIN `user` ub ON (ub.id = tt.create_by)\n" + + " LEFT JOIN `user` uc ON (uc.id = tt.update_by)\n" + + "WHERE tt.del = false\n" + + " AND ua.del = false\n" + + " AND ub.del = false\n" + + " AND uc.del = false\n" + + " AND (ua.head_img = tt.`name` AND tt.id = ua.id)"); + + UserDOCol ut = new UserDOCol("tt"); + UserDOCol ua1 = new UserDOCol("ua"); + UserDOCol ub1 = new UserDOCol("ub"); + UserDOCol uc1 = new UserDOCol("uc"); + + AptQueryWrapper w = new AptQueryWrapper<>(ut) + .selectAll() + .select(ua1.id) + .select(ub1.img) + .leftJoin(ua1, ua1.id, ut.pid) + .leftJoin(ub1, ub1.id, ut.createBy) + .leftJoin(uc1, uc1.id, ut.updateBy) + .eq(ua1.img, ut.name) + .eq(ut.id, ua1.id); + + userMapper.selectJoinList(UserDO.class, w); + System.out.println(1); + } + + /** + * 自连接测试 + */ + @Test + void testInner() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id,\n" + + " t1.pid AS joina_pid,\n" + + " t1.`name` AS joina_name,\n" + + " t1.`json` AS joina_json,\n" + + " t1.sex AS joina_sex,\n" + + " t1.head_img AS joina_head_img,\n" + + " t1.create_time AS joina_create_time,\n" + + " t1.address_id AS joina_address_id,\n" + + " t1.address_id2 AS joina_address_id2,\n" + + " t1.del AS joina_del,\n" + + " t1.create_by AS joina_create_by,\n" + + " t1.update_by AS joina_update_by\n" + + "FROM `user` t\n" + + " LEFT JOIN `user` t1 ON (t1.pid = t.id)\n" + + "WHERE t.del = false\n" + + " AND (t.id > ?)"); + + UserDOCol u = new UserDOCol(); + UserDOCol ua = new UserDOCol(); + + //自连接 + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .disableSubLogicDel()//关闭副表逻辑删除 + .selectAll() + .selectCollection(ua, UserDO::getChildren) + .leftJoin(ua, ua.pid, u.id) + .gt(u.id, 0); + List list = userMapper.selectJoinList(UserDO.class, wrapper); + System.out.println(list); + + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.`name` AS createName,\n" + + " t2.`name` AS updateName\n" + + "FROM `user` t\n" + + " LEFT JOIN `user` t1 ON (t1.id = t.create_by)\n" + + " LEFT JOIN `user` t2 ON (t2.id = t.update_by)\n" + + "WHERE (t2.id = t.update_by AND t.id = t1.id)"); + + UserDOCol uu = new UserDOCol(); + UserDOCol uua = new UserDOCol(); + UserDOCol uub = new UserDOCol(); + + //关联一张表多次 + AptQueryWrapper w = new AptQueryWrapper<>(uu) + .disableLogicDel() + .disableSubLogicDel() + .selectAll(uu) + .selectAs(uua.name, UserDO::getCreateName) + .selectAs(uub.name, UserDO::getUpdateName) + .leftJoin(uua, uua.id, uu.createBy) + .leftJoin(uub, uub.id, uu.updateBy) + .eq(uub.id, uu.updateBy) + .eq(uu.id, uua.id); + List dos = userMapper.selectJoinList(UserDO.class, w); + assert dos.get(0).getCreateName() != null && dos.get(0).getUpdateName() != null; + + + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.`name` AS alias,\n" + + " t1.id AS joina_id,\n" + + " t1.pid AS joina_pid,\n" + + " t1.`name` AS joina_name,\n" + + " t1.`json` AS joina_json,\n" + + " t1.sex AS joina_sex,\n" + + " t1.head_img AS joina_head_img,\n" + + " t1.create_time AS joina_create_time,\n" + + " t1.address_id AS joina_address_id,\n" + + " t1.address_id2 AS joina_address_id2,\n" + + " t1.del AS joina_del,\n" + + " t1.create_by AS joina_create_by,\n" + + " t1.update_by AS joina_update_by,\n" + + " t2.id AS joinb_id,\n" + + " t2.pid AS joinb_pid,\n" + + " t2.`name` AS joinb_name,\n" + + " t2.`json` AS joinb_json,\n" + + " t2.sex AS joinb_sex,\n" + + " t2.head_img AS joinb_head_img,\n" + + " t2.create_time AS joinb_create_time,\n" + + " t2.address_id AS joinb_address_id,\n" + + " t2.address_id2 AS joinb_address_id2,\n" + + " t2.del AS joinb_del,\n" + + " t2.create_by AS joinb_create_by,\n" + + " t2.update_by AS joinb_update_by\n" + + "FROM `user` t\n" + + " LEFT JOIN `user` t1 ON (t1.pid = t.id)\n" + + " LEFT JOIN `user` t2 ON (t2.pid = t1.id)\n" + + "WHERE t.del = false\n" + + " AND (t1.id <= ? AND t.id <= ?)"); + + UserDOCol uuu = new UserDOCol(); + UserDOCol uuua = new UserDOCol(); + UserDOCol uuub = new UserDOCol(); + + AptQueryWrapper wrapper1 = new AptQueryWrapper<>(uuu) + .disableSubLogicDel() + .selectAll() + .selectAs(uuua.name, UserDO::getAlias) + .selectCollection(uuua, UserDO::getChildren, c -> c + .collection(uuub, UserDO::getChildren)) + .leftJoin(uuua, uuua.pid, uuu.id) + .leftJoin(uuub, uuub.pid, uuua.id) + .le(uuua.id, 5) + .le(uuu.id, 4); + List list1 = userMapper.selectJoinList(UserDO.class, wrapper1); + System.out.println(list1); + } + + /** + * 逻辑删除测试 + */ + @Test + void testLogicDel() { + UserDOCol u1 = new UserDOCol(); + List l1 = userMapper.selectJoinList(UserDTO.class, new AptQueryWrapper<>(u1)); + assert l1.size() == 14; + + UserDOCol u2 = new UserDOCol(); + AddressDOCol addr2 = new AddressDOCol(); + List l2 = userMapper.selectJoinList(UserDTO.class, new AptQueryWrapper<>(u2) + .selectAll() + .select(addr2.address) + .leftJoin(addr2, addr2.userId, u2.id)); + assert l2.size() == 10; + + UserDOCol u3 = new UserDOCol(); + AddressDOCol addr3 = new AddressDOCol(); + List l3 = userMapper.selectJoinList(UserDTO.class, new AptQueryWrapper<>(u3) + .disableSubLogicDel() + .selectAll() + .selectCollection(addr3, UserDTO::getAddressList) + .leftJoin(addr3, addr3.userId, u3.id)); + assert l3.size() == 14 && l3.get(0).getAddressList().size() == 9; + + UserDOCol u4 = new UserDOCol(); + AddressDOCol addr4 = new AddressDOCol(); + List l4 = userMapper.selectJoinList(UserDTO.class, new AptQueryWrapper<>(u4) + .disableSubLogicDel() + .selectAll() + .selectCollection(addr4, UserDTO::getAddressList) + .leftJoin(addr4, on -> on + .eq(addr4.userId, u4.id) + .eq(addr4.del, false))); + assert l4.size() == 14 && l4.get(0).getAddressList().size() == 5; + } + + /** + * 别名测试 + */ + @Test + void testAlias() { + UserDOCol u = new UserDOCol(); + UserDOCol uu = new UserDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) +// .disableSubLogicDel()//关闭副表逻辑删除 +// .disableLogicDel()//关闭主表逻辑删除 + .selectAll() + .selectCollection(uu, UserDO::getChildren) + .leftJoin(uu, uu.pid, u.id); + List list = userMapper.selectJoinList(UserDO.class, wrapper); + assert list.get(0).getName() != null && list.get(0).getChildren().get(0).getName() != null; + assert list.get(0).getImg() != null && list.get(0).getChildren().get(0).getImg() != null; + System.out.println(list); + } + + /** + * 别名测试 + */ + @Test + void testObj() { + ThreadLocalUtils.set("SELECT DISTINCT t.id FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) WHERE t.del=false AND t1.del=false ORDER BY t.id DESC"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .distinct() + .select(u.id) + .leftJoin(addr, addr.userId, u.id) + .orderByDesc(u.id); + List list = userMapper.selectObjs(wrapper); + } + + @Test + void testTableAlias() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " aa.id,\n" + + " aa.user_id,\n" + + " aa.area_id,\n" + + " aa.tel,\n" + + " aa.address,\n" + + " aa.del\n" + + "FROM `user` t\n" + + " LEFT JOIN address aa ON (aa.user_id = t.id)\n" + + "WHERE t.del = false\n" + + " AND aa.del = false"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol("aa"); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) +// .disableLogicDel()//关闭主表逻辑删除 + .selectAll() + .selectAll(addr) +// .selectCollection(UserDO.class, UserDO::getChildren) + .leftJoin(addr, addr.userId, u.id); + List list = userMapper.selectJoinList(UserDO.class, wrapper); + + System.out.println(list); + } + + @Test + void testLabel() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr1 = new AddressDOCol("t1"); + AddressDOCol addr2 = new AddressDOCol("t2"); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .disableSubLogicDel() + .selectAll() + .selectCollection(addr1, UserDO::getAddressList) + .selectCollection(addr2, UserDO::getAddressList2) + .leftJoin(addr1, addr1.id, u.addressId) + .leftJoin(addr2, addr2.id, u.addressId2); + List list = userMapper.selectJoinList(UserDO.class, wrapper); + + assert list.get(0).getAddressList().get(0).getAddress() != null; + assert list.get(0).getAddressList2().get(0).getAddress() != null; + System.out.println(list); + } + + + /** + * 简单的分页关联查询 lambda + */ + @Test + void test1() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + Page page = new Page<>(1, 10); + page.setSearchCount(false); + IPage iPage = userMapper.selectJoinPage(page, UserDTO.class, JoinWrappers.apt(u) + .selectAll() + .select(addr.address) + .select(ar.province) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId)); + iPage.getRecords().forEach(System.out::println); + } + + /** + * 简单的分页关联查询 lambda + * ON语句多条件 + */ + @Test + void test3() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.address\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t.id = t1.user_id AND t.id = t1.user_id)\n" + + "WHERE t.del = false\n" + + " AND t1.del = false\n" + + " AND (t.id = ? AND (t.head_img = ? OR t1.user_id = ?) AND t.id = ?)\n" + + "LIMIT ?", + "SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( 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.address FROM `user` t " + + "LEFT JOIN address t1 ON (t.id = t1.user_id AND t.id = t1.user_id) WHERE t.del = false AND t1.del = false AND " + + "(t.id = ? AND (t.head_img = ? OR t1.user_id = ?) AND t.id = ?) ) TMP WHERE ROWNUM <=?) WHERE ROW_ID > ?"); + + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + + IPage page = userMapper.selectJoinPage(new Page<>(1, 10), UserDTO.class, JoinWrappers.apt(u) + .selectAll() + .select(addr.address) + .leftJoin(addr, on -> on + .eq(u.id, addr.userId) + .eq(u.id, addr.userId)) + .eq(u.id, 1) + .and(i -> i + .eq(u.img, "er") + .or() + .eq(addr.userId, 1)) + .eq(u.id, 1)); + page.getRecords().forEach(System.out::println); + } + + /** + * 简单的函数使用 + */ + @Test + void test4() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .selectSum(u.id) + .selectMax(u.id, UserDTO::getHeadImg) + .leftJoin(addr, addr.userId, u.id); + + UserDTO one = userMapper.selectJoinOne(UserDTO.class, wrapper); + System.out.println(one); + } + + /** + * 忽略个别查询字段 + */ + @Test + @SneakyThrows + void test8() throws BadSqlGrammarException { + ThreadLocalUtils.set("SELECT t.`name` FROM `user` t WHERE t.del=false AND (t.`name` = ?)"); + UserDOCol u = new UserDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .select(u.name) + .eq(u.name, "ref"); + userMapper.selectList(wrapper); + } + + + /** + * 关联查询返回map + */ + @Test + void test7() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + List> list = userMapper.selectJoinMaps(JoinWrappers.apt(u) + .selectAll() + .select(addr.address) + .leftJoin(addr, addr.userId, u.id)); + assert list.get(0).get("ADDRESS") != null || list.get(0).get("address") != null; + list.forEach(System.out::println); + } + + /** + * 原生查询 + */ + @Test + void testMP() { + List dos = userMapper.selectList(new LambdaQueryWrapper() + .gt(UserDO::getId, 3) + .lt(UserDO::getId, 8)); + assert dos.size() == 4; + + 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 FROM `user` t WHERE t.del = false AND (t.id > ? AND t.id < ?)", + "SELECT id, pid, `name`, `json`, sex, head_img, create_time, address_id, address_id2, del, create_by, update_by FROM `user` t WHERE t.del = false AND (t.id > ? AND t.id < ?)", + "SELECT * FROM `user` t WHERE t.del=false AND (t.id > ? AND t.id < ?) "); + UserDOCol u = new UserDOCol(); + List dos1 = userMapper.selectList(new AptQueryWrapper<>(u) + .gt(u.id, 3) + .lt(u.id, 8)); + assert dos1.size() == 4; + } + + /** + * 泛型测试 + */ + @Test + void testGeneric() { + AddressDOCol addr = new AddressDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(addr) + .selectAll() + .le(addr.id, 10000) + .orderByDesc(addr.id); + List list = addressMapper.selectJoinList(AddressDTO.class, wrapper); + assert Objects.equals("[AddressDTO(id=22, userId=22, areaId=10022, tel=10000000022, address=朝阳22, del=false, areaList=null, area=null), AddressDTO(id=21, userId=21, areaId=10021, tel=10000000021, address=朝阳21, del=false, areaList=null, area=null), AddressDTO(id=20, userId=20, areaId=10020, tel=10000000020, address=朝阳20, del=false, areaList=null, area=null), AddressDTO(id=19, userId=19, areaId=10019, tel=10000000019, address=朝阳19, del=false, areaList=null, area=null), AddressDTO(id=18, userId=18, areaId=10018, tel=10000000018, address=朝阳18, del=false, areaList=null, area=null), AddressDTO(id=17, userId=17, areaId=10017, tel=10000000017, address=朝阳17, del=false, areaList=null, area=null), AddressDTO(id=16, userId=16, areaId=10016, tel=10000000016, address=朝阳16, del=false, areaList=null, area=null), AddressDTO(id=15, userId=15, areaId=10015, tel=10000000015, address=朝阳15, del=false, areaList=null, area=null), AddressDTO(id=14, userId=14, areaId=10014, tel=10000000014, address=朝阳14, del=false, areaList=null, area=null), AddressDTO(id=13, userId=13, areaId=10013, tel=10000000013, address=朝阳13, del=false, areaList=null, area=null), AddressDTO(id=12, userId=12, areaId=10012, tel=10000000012, address=朝阳12, del=false, areaList=null, area=null), AddressDTO(id=11, userId=11, areaId=10011, tel=10000000011, address=朝阳11, del=false, areaList=null, area=null), AddressDTO(id=10, userId=10, areaId=10010, tel=10000000010, address=朝阳10, del=false, areaList=null, area=null), AddressDTO(id=5, userId=1, areaId=10005, tel=10000000005, address=朝阳05, del=false, areaList=null, area=null), AddressDTO(id=4, userId=1, areaId=10004, tel=10000000004, address=朝阳04, del=false, areaList=null, area=null), AddressDTO(id=3, userId=1, areaId=10003, tel=10000000003, address=朝阳03, del=false, areaList=null, area=null), AddressDTO(id=2, userId=1, areaId=10002, tel=10000000002, address=朝阳02, del=false, areaList=null, area=null), AddressDTO(id=1, userId=1, areaId=10001, tel=10000000001, address=朝阳01, del=false, areaList=null, area=null)]" + , list.toString()); + } + + /** + * count + */ + @Test + void testCount() { + ThreadLocalUtils.set( + "SELECT COUNT( 1 ) FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) LEFT JOIN area t2 ON (t2.id = t1.area_id) WHERE t.del=false AND t1.del=false AND t2.del=false", + "SELECT COUNT( * ) FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) LEFT JOIN area t2 ON (t2.id = t1.area_id) WHERE t.del=false AND t1.del=false AND t2.del=false", + "SELECT COUNT( * ) AS total FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) LEFT JOIN area t2 ON (t2.id = t1.area_id) WHERE t.del=false AND t1.del=false AND t2.del=false"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId); + Object integer = userMapper.selectCount(wrapper); + + ThreadLocalUtils.set("SELECT COUNT( * ) FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) LEFT JOIN area t2 ON (t2.id = t1.area_id) WHERE t.del=false AND t1.del=false AND t2.del=false"); + UserDOCol u1 = new UserDOCol(); + AddressDOCol addr1 = new AddressDOCol(); + AreaDOCol ar1 = new AreaDOCol(); + AptQueryWrapper wrapper1 = new AptQueryWrapper<>(u1) + .leftJoin(addr1, addr1.userId, u1.id) + .leftJoin(ar1, ar1.id, addr1.areaId); + Long aLong1 = userMapper.selectJoinCount(wrapper1); + } + + + /** + * 动态别名 + */ + @Test + void testTable() { + ThreadLocalUtils.set("SELECT t.id FROM (SELECT * FROM `user`) t LEFT JOIN (SELECT * FROM address) t1 ON " + + "(t1.user_id = t.id) LEFT JOIN area t2 ON (t2.id = t1.area_id) WHERE t.del = false AND t1.del = false " + + "AND t2.del = false AND (t.id <= ?) ORDER BY t.id DESC"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .select(u.id) + .leftJoin(addr, on -> on + .eq(addr.userId, u.id) + .setTableName(name -> String.format("(select * from %s)", name))) + .leftJoin(ar, ar.id, addr.areaId) + .le(u.id, 10000) + .orderByDesc(u.id) + .setTableName(name -> String.format("(select * from %s)", name)); + + List list = userMapper.selectJoinList(UserDTO.class, wrapper); + } + + + /** + * 逻辑删除类型 + */ + @Test + void logicDelType() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id,\n" + + " t1.user_id,\n" + + " t1.area_id,\n" + + " t1.tel,\n" + + " t1.address,\n" + + " t1.del AS joina_del,\n" + + " t2.id AS joinb_id,\n" + + " t2.province,\n" + + " t2.city,\n" + + " t2.area,\n" + + " t2.postcode,\n" + + " t2.del AS joinb_del\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id AND t1.del = false)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id AND t2.del = false)\n" + + "WHERE t.del = false\n" + + " AND (t.id <= ?)\n" + + "ORDER BY t.id DESC\n"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + AptQueryWrapper wrapper = new AptQueryWrapper<>(u) + .logicDelToOn() + .selectAll() + .selectCollection(addr, UserDTO::getAddressList, add -> add + .association(ar, AddressDTO::getArea)) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId) + .le(u.id, 10000) + .orderByDesc(u.id); + List list = userMapper.selectJoinList(UserDTO.class, wrapper); + + assert list.get(0).getAddressList() != null && list.get(0).getAddressList().get(0).getId() != null; + list.forEach(System.out::println); + } + + /** + * wrappers 测试 + */ + @Test + void joinWrapper() { + ThreadLocalUtils.set("SELECT t.id,\n" + + " t.pid,\n" + + " t.`name`,\n" + + " t.`json`,\n" + + " t.sex,\n" + + " t.head_img,\n" + + " t.create_time,\n" + + " t.address_id,\n" + + " t.address_id2,\n" + + " t.del,\n" + + " t.create_by,\n" + + " t.update_by,\n" + + " t1.id AS joina_id,\n" + + " t1.user_id,\n" + + " t1.area_id,\n" + + " t1.tel,\n" + + " t1.address,\n" + + " t1.del AS joina_del,\n" + + " t2.id AS joinb_id,\n" + + " t2.province,\n" + + " t2.city,\n" + + " t2.area,\n" + + " t2.postcode,\n" + + " t2.del AS joinb_del\n" + + "FROM `user` t\n" + + " LEFT JOIN address t1 ON (t1.user_id = t.id AND t1.del = false)\n" + + " LEFT JOIN area t2 ON (t2.id = t1.area_id AND t2.del = false)\n" + + "WHERE t.del = false\n" + + " AND (t.id <= ?)\n" + + "ORDER BY t.id DESC\n"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .logicDelToOn() + .selectAll() + .selectCollection(addr, UserDTO::getAddressList, ad -> ad + .association(ar, AddressDTO::getArea)) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId) + .le(u.id, 10000) + .orderByDesc(u.id); + + List list = wrapper.list(UserDTO.class); + + System.out.println(list); + assert list.get(0).getAddressList() != null && list.get(0).getAddressList().get(0).getId() != null; + list.forEach(System.out::println); + } + + @Test + void joinRandomMap() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AreaDOCol ar = new AreaDOCol(); + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .logicDelToOn() + .selectAll() + .selectCollection(UserDTO::getAddressList, ad -> ad + .id(addr.id, AddressDTO::getId) + .result(u.name, AddressDTO::getAddress) + .collection(AddressDTO::getAreaList, map -> map + .id(ar.id) + .result(ar.area, AreaDO::getArea))) + .leftJoin(addr, addr.userId, u.id) + .leftJoin(ar, ar.id, addr.areaId) + .le(u.id, 10000) + .orderByDesc(u.id); + + List list = wrapper.list(UserDTO.class); + + System.out.println(list); + assert list.get(0).getAddressList() != null && list.get(0).getAddressList().get(0).getId() != null; + list.forEach(System.out::println); + } + + @Test + void joinRandomMap111() { + ThreadLocalUtils.set("SELECT t.id,t.user_id,t.area_id,t.tel,t.address,t.del FROM address t LEFT JOIN `user` t1 ON (t1.address_id = t.id) LEFT JOIN `user` t2 ON (t2.pid = t1.id) WHERE t.del=false AND t1.del=false AND t2.del=false"); + UserDOCol u = new UserDOCol(); + UserDOCol u1 = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AptQueryWrapper wrapper = JoinWrappers.apt(addr) + .selectAll() + .leftJoin(u, u.addressId, addr.id) + .leftJoin(u1, u1.pid, u.id); + + List addressDOS = wrapper.list(); + } + + /** + * 同一个类字段比较 + */ + @Test + void joinOwn() { + 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 FROM `user` t LEFT JOIN address t1 ON (t1.user_id = t.id) WHERE t.del=false AND t1.del=false AND (t1.id = t1.id)"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .selectAll() + .leftJoin(addr, addr.userId, u.id) + .eq(addr.id, addr.id); + + List addressDOS = wrapper.list(); + } + + /** + * 同一个类字段比较 + */ + @Test + void joinOwn1() { + 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 FROM `user` t " + + "LEFT JOIN address aaa ON (aaa.user_id = t.id) WHERE t.del=false AND aaa.del=false AND (aaa.id = t.id AND aaa.id = aaa.id)"); + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol("aaa"); + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .selectAll() + .leftJoin(addr, addr.userId, u.id) + .eq(addr.id, u.id) + .eq(addr.id, addr.id); + List addressDOS = wrapper.list(); + } +//执行sql: 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 FROM `user` t LEFT JOIN address aaa ON (aaa.user_id = aaa.id) WHERE t.del = false AND aaa.del = false AND (aaa.id = t.id AND aaa.id = aaa.id) +//预期sql: 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 FROM `user` t LEFT JOIN address aaa ON (aaa.user_id = t.id) WHERE t.del=false AND aaa.del=false AND (aaa.id = t.id AND aaa.id = aaa.id) + + /** + * 同一个类字段比较 + */ + @Test + void joinOrder() { + if (VersionUtils.compare(MybatisPlusVersion.getVersion(), "3.4.3") >= 0) { + ThreadLocalUtils.set("SELECT id,user_id,name FROM order_t t ORDER BY t.name DESC", + "SELECT t.id, t.user_id, t.name FROM order_t t ORDER BY t.name DESC", + "SELECT id,user_id,name FROM order_t t ORDER BY t.name desc"); + } else { + ThreadLocalUtils.set("SELECT id,user_id,name FROM order_t t", + "SELECT id,user_id,name FROM order_t t"); + } + OrderDOCol o = new OrderDOCol(); + AptQueryWrapper wrapper = JoinWrappers.apt(o); + List list = wrapper.list(); + + if (VersionUtils.compare(MybatisPlusVersion.getVersion(), "3.4.3") >= 0) { + ThreadLocalUtils.set("SELECT t.id,t.user_id,t.name,t1.`name` AS userName FROM order_t t LEFT JOIN `user` t1 ON (t1.id = t.user_id) WHERE t1.del=false ORDER BY t.name DESC", + "SELECT t.id,t.user_id,t.name,t1.`name` AS userName FROM order_t t LEFT JOIN `user` t1 ON (t1.id = t.user_id) WHERE t1.del=false ORDER BY t.name desc"); + } else { + ThreadLocalUtils.set("SELECT t.id,t.user_id,t.name,t1.`name` AS userName FROM order_t t LEFT JOIN `user` t1 ON (t1.id = t.user_id) WHERE t1.del=false", + "SELECT t.id,t.user_id,t.name,t1.`name` AS userName FROM order_t t LEFT JOIN `user` t1 ON (t1.id = t.user_id) WHERE t1.del=false"); + } + + UserDOCol u = new UserDOCol(); + OrderDOCol o1 = new OrderDOCol(); + AptQueryWrapper w = JoinWrappers.apt(o1) + .selectAll() + .selectAs(u.name, OrderDO::getUserName) + .leftJoin(u, u.id, o1.userId); + List l = w.list(); + } + + /** + * select 子查询 + */ + @Test + void checkOrderBy() { + UserDOCol u = new UserDOCol(); + AddressDOCol addr = new AddressDOCol(); + AptQueryWrapper wrapper = JoinWrappers.apt(u) + .selectAll() + .leftJoin(addr, addr.userId, u.id) + .le(u.id, 100) + .checkSqlInjection() + .orderByDesc("t.id"); + wrapper.list(); + } +} diff --git a/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/unit/UnionTest.java b/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/unit/UnionTest.java index 7991c37..47a6bf2 100644 --- a/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/unit/UnionTest.java +++ b/mybatis-plus-join-test/test-join/src/test/java/com/github/yulichang/test/join/unit/UnionTest.java @@ -13,7 +13,6 @@ import org.springframework.boot.test.context.SpringBootTest; import java.util.List; -@SuppressWarnings("deprecation") @SpringBootTest public class UnionTest { @@ -23,45 +22,6 @@ public class UnionTest { } - @Test - void union() { - 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 FROM `user` t WHERE t.del=false AND (t.id = ?) UNION 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 FROM `user` t WHERE (t.`name` = ? AND (t.`name` = ?)) UNION 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 FROM `user` t WHERE t.del=false AND (t.pid = ?)"); - MPJLambdaWrapper wrapper = JoinWrappers.lambda(UserDO.class) - .selectAll(UserDO.class) - .eq(UserDO::getId, 1); - MPJLambdaWrapper wrapper1 = JoinWrappers.lambda(UserDO.class) - .selectAll(UserDO.class) - .disableLogicDel() - .eq(UserDO::getName, "张三 2") - .and(w -> w.eq(UserDO::getName, "张三 2")); - MPJLambdaWrapper wrapper2 = JoinWrappers.lambda(UserDO.class) - .selectAll(UserDO.class) - .eq(UserDO::getPid, 2); - wrapper.union(wrapper1, wrapper2); - List list = wrapper.list(); - - System.out.println(wrapper.getUnionSql()); - assert list.size() == 7 && list.get(0).getId() != null; - } - - @Test - void unionAll() { - ThreadLocalUtils.set("SELECT t.id FROM `user` t WHERE t.del = false AND (t.id = ?) UNION ALL SELECT t.id FROM address t UNION ALL SELECT t.id FROM area t WHERE t.del = false AND (t.id = ?)"); - MPJLambdaWrapper wrapper = JoinWrappers.lambda(UserDO.class) - .select(UserDO::getId) - .eq(UserDO::getId, 1); - MPJLambdaWrapper wrapper1 = JoinWrappers.lambda(AddressDO.class) - .select(AddressDO::getId) - .disableLogicDel(); - MPJLambdaWrapper wrapper2 = JoinWrappers.lambda(AreaDO.class) - .select(AreaDO::getId) - .eq(AreaDO::getId, 2); - wrapper.unionAll(wrapper1, wrapper2); - List list = wrapper.list(); - - assert list.size() == 23 && list.get(0).getId() != null; - } - @Test void unionAll1() { ThreadLocalUtils.set("SELECT t.id FROM `user` t WHERE t.del = false AND (t.id = ?) UNION ALL SELECT t.id FROM address t WHERE (t.id = ?) UNION ALL SELECT (SELECT st.id FROM area st WHERE st.del = false AND (st.id = ? AND (st.id = ?))) AS id FROM area t WHERE t.del = false AND (t.id = ? AND (t.id = ?))"); diff --git a/pom.xml b/pom.xml index 6e7a32a..adc4c33 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ mybatis-plus-join-annotation mybatis-plus-join-extension mybatis-plus-join-solon-plugin + mybatis-plus-join-processor mybatis-plus-join-test