diff --git a/src/main/java/com/baomidou/mybatisplus/core/metadata/MPJTableInfoHelper.java b/src/main/java/com/baomidou/mybatisplus/core/metadata/MPJTableInfoHelper.java
new file mode 100644
index 0000000..c72771b
--- /dev/null
+++ b/src/main/java/com/baomidou/mybatisplus/core/metadata/MPJTableInfoHelper.java
@@ -0,0 +1,387 @@
+package com.baomidou.mybatisplus.core.metadata;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.core.toolkit.*;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.reflection.Reflector;
+import org.apache.ibatis.reflection.ReflectorFactory;
+import org.apache.ibatis.session.Configuration;
+
+import java.lang.reflect.Field;
+import java.util.*;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * 拷贝 {@link TableInfoHelper}
+ *
+ *
用于构建resultType(DTO)对应的TableInfo
+ *
拷贝这个类用于更好的兼容mybatis-plus的全部功能
+ *
由于 {@link TableInfo} 权限限制,所以新建 com.baomidou.mybatisplus.core.metadata 这个包
+ *
为什么不把 {@link TableInfo} 这个类拷贝出来? 因为无法限制用户使用那个版本, 而TableInfo会随着版本而改动,
+ * 使用 mybatis-plus 的TableInfo能够兼容所有版本,也能跟好的维护
+ *
+ * @author yulichang
+ * @see TableInfoHelper
+ */
+public class MPJTableInfoHelper {
+
+ private static final Log logger = LogFactory.getLog(TableInfoHelper.class);
+
+ /**
+ * 默认表主键名称
+ */
+ private static final String DEFAULT_ID_NAME = "id";
+
+ /**
+ *
+ * 实体类反射获取表信息【初始化】
+ *
+ *
+ * @param clazz 反射实体类
+ * @return 数据库表反射信息
+ */
+ public synchronized static TableInfo initTableInfo(Configuration configuration, String currentNamespace, Class> clazz) {
+ /* 没有获取到缓存信息,则初始化 */
+ TableInfo tableInfo = new TableInfo(clazz);
+ tableInfo.setCurrentNamespace(currentNamespace);
+ tableInfo.setConfiguration(configuration);
+ GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
+
+ /* 初始化表名相关 */
+ final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);
+
+ List excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();
+
+ /* 初始化字段相关 */
+ initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);
+
+ /* 自动构建 resultMap */
+ tableInfo.initResultMapIfNeed();
+
+ /* 缓存 lambda */
+ LambdaUtils.installCache(tableInfo);
+ return tableInfo;
+ }
+
+ /**
+ *
+ * 初始化 表数据库类型,表名,resultMap
+ *
+ *
+ * @param clazz 实体类
+ * @param globalConfig 全局配置
+ * @param tableInfo 数据库表反射信息
+ * @return 需要排除的字段名
+ */
+ private static String[] initTableName(Class> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
+ /* 数据库全局配置 */
+ GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
+ TableName table = clazz.getAnnotation(TableName.class);
+
+ String tableName = clazz.getSimpleName();
+ String tablePrefix = dbConfig.getTablePrefix();
+ String schema = dbConfig.getSchema();
+ boolean tablePrefixEffect = true;
+ String[] excludeProperty = null;
+
+ if (table != null) {
+ if (StringUtils.isNotBlank(table.value())) {
+ tableName = table.value();
+ if (StringUtils.isNotBlank(tablePrefix) && !table.keepGlobalPrefix()) {
+ tablePrefixEffect = false;
+ }
+ } else {
+ tableName = initTableNameWithDbConfig(tableName, dbConfig);
+ }
+ if (StringUtils.isNotBlank(table.schema())) {
+ schema = table.schema();
+ }
+ /* 表结果集映射 */
+ if (StringUtils.isNotBlank(table.resultMap())) {
+ tableInfo.setResultMap(table.resultMap());
+ }
+ tableInfo.setAutoInitResultMap(table.autoResultMap());
+ excludeProperty = table.excludeProperty();
+ } else {
+ tableName = initTableNameWithDbConfig(tableName, dbConfig);
+ }
+
+ String targetTableName = tableName;
+ if (StringUtils.isNotBlank(tablePrefix) && tablePrefixEffect) {
+ targetTableName = tablePrefix + targetTableName;
+ }
+ if (StringUtils.isNotBlank(schema)) {
+ targetTableName = schema + StringPool.DOT + targetTableName;
+ }
+
+ tableInfo.setTableName(targetTableName);
+
+ /* 开启了自定义 KEY 生成器 */
+ if (CollectionUtils.isNotEmpty(dbConfig.getKeyGenerators())) {
+ tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
+ }
+ return excludeProperty;
+ }
+
+ /**
+ * 根据 DbConfig 初始化 表名
+ *
+ * @param className 类名
+ * @param dbConfig DbConfig
+ * @return 表名
+ */
+ private static String initTableNameWithDbConfig(String className, GlobalConfig.DbConfig dbConfig) {
+ String tableName = className;
+ // 开启表名下划线申明
+ if (dbConfig.isTableUnderline()) {
+ tableName = StringUtils.camelToUnderline(tableName);
+ }
+ // 大写命名判断
+ if (dbConfig.isCapitalMode()) {
+ tableName = tableName.toUpperCase();
+ } else {
+ // 首字母小写
+ tableName = StringUtils.firstToLowerCase(tableName);
+ }
+ return tableName;
+ }
+
+ /**
+ *
+ * 初始化 表主键,表字段
+ *
+ *
+ * @param clazz 实体类
+ * @param globalConfig 全局配置
+ * @param tableInfo 数据库表反射信息
+ */
+ private static void initTableFields(Class> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List excludeProperty) {
+ /* 数据库全局配置 */
+ GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
+ ReflectorFactory reflectorFactory = tableInfo.getConfiguration().getReflectorFactory();
+ Reflector reflector = reflectorFactory.findForClass(clazz);
+ List list = getAllFields(clazz);
+ // 标记是否读取到主键
+ boolean isReadPK = false;
+ // 是否存在 @TableId 注解
+ boolean existTableId = isExistTableId(list);
+ // 是否存在 @TableLogic 注解
+ boolean existTableLogic = isExistTableLogic(list);
+ // 是否存在 @OrderBy 注解
+ boolean existOrderBy = isExistOrderBy(list);
+
+ List fieldList = new ArrayList<>(list.size());
+ for (Field field : list) {
+ if (excludeProperty.contains(field.getName())) {
+ continue;
+ }
+
+ /* 主键ID 初始化 */
+ if (existTableId) {
+ TableId tableId = field.getAnnotation(TableId.class);
+ if (tableId != null) {
+ if (isReadPK) {
+ throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
+ } else {
+ initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId, reflector);
+ isReadPK = true;
+ continue;
+ }
+ }
+ } else if (!isReadPK) {
+ isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, reflector);
+ if (isReadPK) {
+ continue;
+ }
+ }
+ final TableField tableField = field.getAnnotation(TableField.class);
+
+ /* 有 @TableField 注解的字段初始化 */
+ if (tableField != null) {
+ fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic, existOrderBy));
+ continue;
+ }
+
+ /* 无 @TableField 注解的字段初始化 */
+ fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, existOrderBy));
+ }
+
+ /* 字段列表 */
+ tableInfo.setFieldList(fieldList);
+
+ /* 未发现主键注解,提示警告信息 */
+ if (!isReadPK) {
+ logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
+ }
+ }
+
+ /**
+ *
+ * 判断主键注解是否存在
+ *
+ *
+ * @param list 字段列表
+ * @return true 为存在 @TableId 注解;
+ */
+ public static boolean isExistTableId(List list) {
+ return list.stream().anyMatch(field -> field.isAnnotationPresent(TableId.class));
+ }
+
+ /**
+ *
+ * 判断逻辑删除注解是否存在
+ *
+ *
+ * @param list 字段列表
+ * @return true 为存在 @TableId 注解;
+ */
+ public static boolean isExistTableLogic(List list) {
+ return list.stream().anyMatch(field -> field.isAnnotationPresent(TableLogic.class));
+ }
+
+ /**
+ *
+ * 判断排序注解是否存在
+ *
+ *
+ * @param list 字段列表
+ * @return true 为存在 @TableId 注解;
+ */
+ public static boolean isExistOrderBy(List list) {
+ return list.stream().anyMatch(field -> field.isAnnotationPresent(OrderBy.class));
+ }
+
+ /**
+ *
+ * 主键属性初始化
+ *
+ *
+ * @param dbConfig 全局配置信息
+ * @param tableInfo 表信息
+ * @param field 字段
+ * @param tableId 注解
+ * @param reflector Reflector
+ */
+ private static void initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
+ Field field, TableId tableId, Reflector reflector) {
+ boolean underCamel = tableInfo.isUnderCamel();
+ final String property = field.getName();
+ if (field.getAnnotation(TableField.class) != null) {
+ logger.warn(String.format("This \"%s\" is the table primary key by @TableId annotation in Class: \"%s\",So @TableField annotation will not work!",
+ property, tableInfo.getEntityType().getName()));
+ }
+ /* 主键策略( 注解 > 全局 ) */
+ // 设置 Sequence 其他策略无效
+ if (IdType.NONE == tableId.type()) {
+ tableInfo.setIdType(dbConfig.getIdType());
+ } else {
+ tableInfo.setIdType(tableId.type());
+ }
+
+ /* 字段 */
+ String column = property;
+ if (StringUtils.isNotBlank(tableId.value())) {
+ column = tableId.value();
+ } else {
+ // 开启字段下划线申明
+ if (underCamel) {
+ column = StringUtils.camelToUnderline(column);
+ }
+ // 全局大写命名
+ if (dbConfig.isCapitalMode()) {
+ column = column.toUpperCase();
+ }
+ }
+ final Class> keyType = reflector.getGetterType(property);
+ if (keyType.isPrimitive()) {
+ logger.warn(String.format("This primary key of \"%s\" is primitive !不建议如此请使用包装类 in Class: \"%s\"",
+ property, tableInfo.getEntityType().getName()));
+ }
+ tableInfo.setKeyRelated(checkRelated(underCamel, property, column))
+ .setKeyColumn(column)
+ .setKeyProperty(property)
+ .setKeyType(keyType);
+ }
+
+ /**
+ *
+ * 主键属性初始化
+ *
+ *
+ * @param tableInfo 表信息
+ * @param field 字段
+ * @param reflector Reflector
+ * @return true 继续下一个属性判断,返回 continue;
+ */
+ private static boolean initTableIdWithoutAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
+ Field field, Reflector reflector) {
+ final String property = field.getName();
+ if (DEFAULT_ID_NAME.equalsIgnoreCase(property)) {
+ if (field.getAnnotation(TableField.class) != null) {
+ logger.warn(String.format("This \"%s\" is the table primary key by default name for `id` in Class: \"%s\",So @TableField will not work!",
+ property, tableInfo.getEntityType().getName()));
+ }
+ String column = property;
+ if (dbConfig.isCapitalMode()) {
+ column = column.toUpperCase();
+ }
+ final Class> keyType = reflector.getGetterType(property);
+ if (keyType.isPrimitive()) {
+ logger.warn(String.format("This primary key of \"%s\" is primitive !不建议如此请使用包装类 in Class: \"%s\"",
+ property, tableInfo.getEntityType().getName()));
+ }
+ tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column))
+ .setIdType(dbConfig.getIdType())
+ .setKeyColumn(column)
+ .setKeyProperty(property)
+ .setKeyType(keyType);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 判定 related 的值
+ *
+ * 为 true 表示不符合规则
+ *
+ * @param underCamel 驼峰命名
+ * @param property 属性名
+ * @param column 字段名
+ * @return related
+ */
+ public static boolean checkRelated(boolean underCamel, String property, String column) {
+ column = StringUtils.getTargetColumn(column);
+ String propertyUpper = property.toUpperCase(Locale.ENGLISH);
+ String columnUpper = column.toUpperCase(Locale.ENGLISH);
+ if (underCamel) {
+ // 开启了驼峰并且 column 包含下划线
+ return !(propertyUpper.equals(columnUpper) ||
+ propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));
+ } else {
+ // 未开启驼峰,直接判断 property 是否与 column 相同(全大写)
+ return !propertyUpper.equals(columnUpper);
+ }
+ }
+
+ /**
+ *
+ * 获取该类的所有属性列表
+ *
+ *
+ * @param clazz 反射类
+ * @return 属性集合
+ */
+ public static List getAllFields(Class> clazz) {
+ List fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
+ return fieldList.stream()
+ .filter(field -> {
+ /* 过滤注解非表字段属性 */
+ TableField tableField = field.getAnnotation(TableField.class);
+ return (tableField == null || tableField.exist());
+ }).collect(toList());
+ }
+}
diff --git a/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java b/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java
index 0fa4cdc..fc983d9 100644
--- a/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java
+++ b/src/main/java/com/github/yulichang/interceptor/MPJInterceptor.java
@@ -1,16 +1,15 @@
package com.github.yulichang.interceptor;
-import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
+import com.baomidou.mybatisplus.core.metadata.MPJTableInfoHelper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
-import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.github.yulichang.exception.MPJException;
import com.github.yulichang.method.MPJResultType;
import com.github.yulichang.toolkit.Constant;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
-import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.plugin.Interceptor;
@@ -20,12 +19,11 @@ import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
-import org.apache.ibatis.type.JdbcType;
-import org.apache.ibatis.type.TypeHandler;
-import org.apache.ibatis.type.TypeHandlerRegistry;
-import org.apache.ibatis.type.UnknownTypeHandler;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -44,7 +42,7 @@ public class MPJInterceptor implements Interceptor {
/**
* 缓存MappedStatement,不需要每次都去重写构建MappedStatement
*/
- private static final Map MS_CACHE = new ConcurrentHashMap<>();
+ private static final Map> MS_CACHE = new ConcurrentHashMap<>();
@Override
public Object intercept(Invocation invocation) throws Throwable {
@@ -77,9 +75,12 @@ public class MPJInterceptor implements Interceptor {
*/
public MappedStatement newMappedStatement(MappedStatement ms, Class> resultType) {
String id = ms.getId() + StringPool.UNDERSCORE + resultType.getName();
- MappedStatement statement = MS_CACHE.get(id);
- if (Objects.nonNull(statement)) {
- return statement;
+ Map statementMap = MS_CACHE.get(id);
+ if (CollectionUtils.isNotEmpty(statementMap)) {
+ MappedStatement statement = statementMap.get(ms.getConfiguration());
+ if (Objects.nonNull(statement)) {
+ return statement;
+ }
}
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), id, ms.getSqlSource(), ms.getSqlCommandType())
.resource(ms.getResource())
@@ -99,7 +100,12 @@ public class MPJInterceptor implements Interceptor {
resultMaps.add(newResultMap(ms, resultType));
builder.resultMaps(resultMaps);
MappedStatement mappedStatement = builder.build();
- MS_CACHE.put(id, mappedStatement);
+
+ if (statementMap == null) {
+ statementMap = new ConcurrentHashMap<>();
+ MS_CACHE.put(id, statementMap);
+ }
+ statementMap.put(ms.getConfiguration(), mappedStatement);
return mappedStatement;
}
@@ -109,50 +115,13 @@ public class MPJInterceptor implements Interceptor {
private ResultMap newResultMap(MappedStatement ms, Class> resultType) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(resultType);
if (tableInfo != null && tableInfo.isAutoInitResultMap() && tableInfo.getEntityType() == resultType) {
- return initResultMapIfNeed(tableInfo, resultType);
+ return ms.getConfiguration().getResultMap(tableInfo.getResultMap());
+ }
+ TableInfo infoDTO = MPJTableInfoHelper.initTableInfo(ms.getConfiguration(),
+ ms.getId().substring(0, ms.getId().lastIndexOf(".")), resultType);
+ if (infoDTO.isAutoInitResultMap()) {
+ return ms.getConfiguration().getResultMap(infoDTO.getResultMap());
}
return new ResultMap.Builder(ms.getConfiguration(), ms.getId(), resultType, EMPTY_RESULT_MAPPING).build();
}
-
- /**
- * 构建resultMap
- */
- private ResultMap initResultMapIfNeed(TableInfo info, Class> resultType) {
- String id = info.getCurrentNamespace() + ".mybatis-plus-join_" + resultType.getSimpleName();
- List resultMappings = new ArrayList<>();
- if (info.havePK()) {
- ResultMapping idMapping = new ResultMapping.Builder(info.getConfiguration(), info.getKeyProperty(), info.getKeyColumn(), info.getKeyType())
- .flags(Collections.singletonList(ResultFlag.ID)).build();
- resultMappings.add(idMapping);
- }
- if (CollectionUtils.isNotEmpty(info.getFieldList())) {
- info.getFieldList().forEach(i -> resultMappings.add(getResultMapping(i, info.getConfiguration())));
- }
- return new ResultMap.Builder(info.getConfiguration(), id, resultType, resultMappings).build();
- }
-
-
- /**
- * 获取 ResultMapping
- *
- * @param configuration MybatisConfiguration
- * @return ResultMapping
- */
- private ResultMapping getResultMapping(TableFieldInfo info, final Configuration configuration) {
- ResultMapping.Builder builder = new ResultMapping.Builder(configuration, info.getProperty(),
- StringUtils.getTargetColumn(info.getColumn()), info.getPropertyType());
- TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
- if (info.getJdbcType() != null && info.getJdbcType() != JdbcType.UNDEFINED) {
- builder.jdbcType(info.getJdbcType());
- }
- if (info.getTypeHandler() != null && info.getTypeHandler() != UnknownTypeHandler.class) {
- TypeHandler> typeHandler = registry.getMappingTypeHandler(info.getTypeHandler());
- if (typeHandler == null) {
- typeHandler = registry.getInstance(info.getPropertyType(), info.getTypeHandler());
- // todo 这会有影响 registry.register(typeHandler);
- }
- builder.typeHandler(typeHandler);
- }
- return builder.build();
- }
}
diff --git a/src/main/java/com/github/yulichang/toolkit/Constant.java b/src/main/java/com/github/yulichang/toolkit/Constant.java
index 352be8a..31854d7 100644
--- a/src/main/java/com/github/yulichang/toolkit/Constant.java
+++ b/src/main/java/com/github/yulichang/toolkit/Constant.java
@@ -15,8 +15,6 @@ public interface Constant {
String ON = " ON ";
- String EQUALS = " = ";
-
String JOIN = "JOIN";
String LEFT = "LEFT";
@@ -46,14 +44,4 @@ public interface Constant {
* " t"
*/
String SPACE_TABLE_ALIAS = StringPool.SPACE + Constant.TABLE_ALIAS;
-
- /**
- * " ON t"
- */
- String ON_TABLE_ALIAS = Constant.ON + Constant.TABLE_ALIAS;
-
- /**
- * " = t"
- */
- String EQUALS_TABLE_ALIAS = Constant.EQUALS + Constant.TABLE_ALIAS;
}