mirror of https://github.com/elunez/eladmin
# 抛弃 改造 MybatisPlus 源码方式 ,支持JPA 注解,在EL 中 进行MybatisPlus 适配
parent
b8b23c351e
commit
810f0c6fd6
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baomidou.mybatisplus.core.metadata;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
||||
import com.baomidou.mybatisplus.core.toolkit.*;
|
||||
import me.zhengjie.mybatis.annotation.impl.TableFieldImp;
|
||||
import me.zhengjie.mybatis.annotation.impl.TableIdImp;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.apache.ibatis.builder.StaticSqlSource;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.reflection.Reflector;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 针对ElAdmin 原有JPA 注解 定向改造 支持基本的JPA 注解
|
||||
* 实体类反射表辅助类
|
||||
* </p>
|
||||
*
|
||||
* @author hubin sjy
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:07
|
||||
*/
|
||||
public class ElTableInfoHelper {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ElTableInfoHelper.class);
|
||||
|
||||
/**
|
||||
* 储存反射类表信息
|
||||
*/
|
||||
private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 默认表主键名称
|
||||
*/
|
||||
private static final String DEFAULT_ID_NAME = "id";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取实体映射表信息
|
||||
* </p>
|
||||
*
|
||||
* @param clazz 反射实体类
|
||||
* @return 数据库表反射信息
|
||||
*/
|
||||
public static TableInfo getTableInfo(Class<?> clazz) {
|
||||
if (clazz == null
|
||||
|| ReflectionKit.isPrimitiveOrWrapper(clazz)
|
||||
|| clazz == String.class) {
|
||||
return null;
|
||||
}
|
||||
// https://github.com/baomidou/mybatis-plus/issues/299
|
||||
TableInfo tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(clazz));
|
||||
if (null != tableInfo) {
|
||||
return tableInfo;
|
||||
}
|
||||
//尝试获取父类缓存
|
||||
Class<?> currentClass = clazz;
|
||||
while (null == tableInfo && Object.class != currentClass) {
|
||||
currentClass = currentClass.getSuperclass();
|
||||
tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(currentClass));
|
||||
}
|
||||
if (tableInfo != null) {
|
||||
TABLE_INFO_CACHE.put(ClassUtils.getUserClass(clazz), tableInfo);
|
||||
}
|
||||
return tableInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取所有实体映射表信息
|
||||
* </p>
|
||||
*
|
||||
* @return 数据库表反射信息集合
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static List<TableInfo> getTableInfos() {
|
||||
return new ArrayList<>(TABLE_INFO_CACHE.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 实体类反射获取表信息【初始化】
|
||||
* </p>
|
||||
*
|
||||
* @param clazz 反射实体类
|
||||
* @return 数据库表反射信息
|
||||
*/
|
||||
public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
|
||||
TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
|
||||
if (tableInfo != null) {
|
||||
if (builderAssistant != null) {
|
||||
tableInfo.setConfiguration(builderAssistant.getConfiguration());
|
||||
}
|
||||
return tableInfo;
|
||||
}
|
||||
|
||||
/* 没有获取到缓存信息,则初始化 */
|
||||
tableInfo = new TableInfo(clazz);
|
||||
GlobalConfig globalConfig;
|
||||
if (null != builderAssistant) {
|
||||
tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
|
||||
tableInfo.setConfiguration(builderAssistant.getConfiguration());
|
||||
globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
|
||||
} else {
|
||||
// 兼容测试场景
|
||||
globalConfig = GlobalConfigUtils.defaults();
|
||||
}
|
||||
|
||||
/* 初始化表名相关 */
|
||||
final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);
|
||||
|
||||
List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();
|
||||
|
||||
/* 初始化字段相关 */
|
||||
initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);
|
||||
|
||||
/* 放入缓存 */
|
||||
TABLE_INFO_CACHE.put(clazz, tableInfo);
|
||||
|
||||
/* 缓存 lambda */
|
||||
LambdaUtils.installCache(tableInfo);
|
||||
|
||||
/* 自动构建 resultMap */
|
||||
tableInfo.initResultMapIfNeed();
|
||||
|
||||
return tableInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 初始化 表数据库类型,表名,resultMap
|
||||
* </p>
|
||||
*
|
||||
* @param clazz 实体类
|
||||
* @param globalConfig 全局配置
|
||||
* @param tableInfo 数据库表反射信息
|
||||
* @return 需要排除的字段名
|
||||
*/
|
||||
private static String[] initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
|
||||
/* 数据库全局配置 */
|
||||
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
|
||||
/**
|
||||
* JPA 注解
|
||||
*/
|
||||
final Table jpaTableAnnotation = clazz.getAnnotation(Table.class);
|
||||
/**
|
||||
* MP 注解
|
||||
*/
|
||||
final TableName mpTableAnnotation = clazz.getAnnotation(TableName.class);
|
||||
|
||||
String tableName = clazz.getSimpleName();
|
||||
String tablePrefix = dbConfig.getTablePrefix();
|
||||
String schema = dbConfig.getSchema();
|
||||
boolean tablePrefixEffect = true;
|
||||
String[] excludeProperty = null;
|
||||
/**
|
||||
* JPA 注解优先加载,后期 MP 可以覆盖
|
||||
*/
|
||||
if (Objects.nonNull(jpaTableAnnotation)) {
|
||||
if (StringUtils.isNotBlank(jpaTableAnnotation.name())) {
|
||||
tableName = jpaTableAnnotation.name();
|
||||
} else {
|
||||
tableName = initTableNameWithDbConfig(tableName, dbConfig);
|
||||
}
|
||||
if (StringUtils.isNotBlank(jpaTableAnnotation.schema())) {
|
||||
schema = jpaTableAnnotation.schema();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* MP table 注解
|
||||
*/
|
||||
if (mpTableAnnotation != null) {
|
||||
if (StringUtils.isNotBlank(mpTableAnnotation.value())) {
|
||||
tableName = mpTableAnnotation.value();
|
||||
if (StringUtils.isNotBlank(tablePrefix) && !mpTableAnnotation.keepGlobalPrefix()) {
|
||||
tablePrefixEffect = false;
|
||||
}
|
||||
} else if (Objects.isNull(jpaTableAnnotation)) {
|
||||
tableName = initTableNameWithDbConfig(tableName, dbConfig);
|
||||
}
|
||||
if (StringUtils.isNotBlank(mpTableAnnotation.schema())) {
|
||||
schema = mpTableAnnotation.schema();
|
||||
}
|
||||
/* 表结果集映射 */
|
||||
if (StringUtils.isNotBlank(mpTableAnnotation.resultMap())) {
|
||||
tableInfo.setResultMap(mpTableAnnotation.resultMap());
|
||||
}
|
||||
tableInfo.setAutoInitResultMap(mpTableAnnotation.autoResultMap());
|
||||
excludeProperty = mpTableAnnotation.excludeProperty();
|
||||
} else if (Objects.isNull(jpaTableAnnotation)) {
|
||||
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 (null != dbConfig.getKeyGenerator()) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 初始化 表主键,表字段
|
||||
* </p>
|
||||
*
|
||||
* @param clazz 实体类
|
||||
* @param globalConfig 全局配置
|
||||
* @param tableInfo 数据库表反射信息
|
||||
*/
|
||||
public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {
|
||||
/* 数据库全局配置 */
|
||||
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
|
||||
ReflectorFactory reflectorFactory = tableInfo.getConfiguration().getReflectorFactory();
|
||||
//TODO @咩咩 有空一起来撸完这反射模块.
|
||||
Reflector reflector = reflectorFactory.findForClass(clazz);
|
||||
List<Field> list = getAllFields(clazz);
|
||||
// 标记是否读取到主键
|
||||
boolean isReadPK = false;
|
||||
/**
|
||||
* 使用JPA 初始化
|
||||
*/
|
||||
boolean jpaReadPK = false;
|
||||
// 是否存在 @TableId 注解
|
||||
boolean existTableId = isExistTableId(list);
|
||||
|
||||
List<TableFieldInfo> fieldList = new ArrayList<>(list.size());
|
||||
for (Field field : list) {
|
||||
/**
|
||||
* 手动排除或者使用 transient注解的 排除
|
||||
*/
|
||||
final boolean exclude = excludeProperty.contains(field.getName());
|
||||
final boolean transientField = Objects.nonNull(field.getAnnotation(Transient.class));
|
||||
if (exclude || transientField) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 主键ID 初始化 */
|
||||
if (existTableId) {
|
||||
Id id = field.getAnnotation(Id.class);
|
||||
TableId tableId = field.getAnnotation(TableId.class);
|
||||
if (tableId != null || id != null) {
|
||||
if (isReadPK) {
|
||||
if (jpaReadPK) {
|
||||
throw ExceptionUtils.mpe("JPA @Id has been Init: \"%s\".", clazz.getName());
|
||||
} else {
|
||||
throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
|
||||
}
|
||||
} else {
|
||||
if (Objects.isNull(tableId)) {
|
||||
TableIdImp tableIdImp = new TableIdImp();
|
||||
Column column = field.getAnnotation(Column.class);
|
||||
if (Objects.nonNull(column)) {
|
||||
tableIdImp.setValue(column.name());
|
||||
}
|
||||
GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);
|
||||
if (Objects.nonNull(generatedValue)) {
|
||||
tableIdImp.setType(IdType.ASSIGN_ID);
|
||||
logger.warn("JPA compatible mode, []com.baomidou.mybatisplus.annotation.IdType.ASSIGN_ID] is the only way to generate primary key");
|
||||
}
|
||||
tableId = tableIdImp;
|
||||
jpaReadPK = true;
|
||||
|
||||
}
|
||||
initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId, reflector);
|
||||
isReadPK = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (!isReadPK) {
|
||||
isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, reflector);
|
||||
if (isReadPK) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
|
||||
/* 有 @TableField 注解的字段初始化 */
|
||||
if (tableField != null) {
|
||||
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField));
|
||||
continue;
|
||||
} else {
|
||||
final Column column = field.getAnnotation(Column.class);
|
||||
if (Objects.nonNull(column)) {
|
||||
TableFieldImp tableFieldImp = new TableFieldImp();
|
||||
tableFieldImp.setValue(column.name());
|
||||
tableField = tableFieldImp;
|
||||
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* 无 @TableField 注解的字段初始化 */
|
||||
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));
|
||||
}
|
||||
|
||||
/* 检查逻辑删除字段只能有最多一个 */
|
||||
Assert.isTrue(fieldList.parallelStream().filter(com.baomidou.mybatisplus.core.metadata.TableFieldInfo::isLogicDelete).count() < 2L,
|
||||
String.format("@TableLogic can't more than one in Class: \"%s\".", clazz.getName()));
|
||||
|
||||
/* 字段列表,不可变集合 */
|
||||
tableInfo.setFieldList(Collections.unmodifiableList(fieldList));
|
||||
|
||||
/* 未发现主键注解,提示警告信息 */
|
||||
if (!isReadPK) {
|
||||
logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 判断主键注解是否存在
|
||||
* </p>
|
||||
*
|
||||
* @param list 字段列表
|
||||
* @return true 为存在 @TableId 注解;
|
||||
*/
|
||||
public static boolean isExistTableId(List<Field> list) {
|
||||
return list.stream().anyMatch(field ->
|
||||
field.isAnnotationPresent(TableId.class) ||
|
||||
field.isAnnotationPresent(Id.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 主键属性初始化
|
||||
* </p>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 主键属性初始化
|
||||
* </p>
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column))
|
||||
.setIdType(dbConfig.getIdType())
|
||||
.setKeyColumn(column)
|
||||
.setKeyProperty(property)
|
||||
.setKeyType(reflector.getGetterType(property));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 字段属性初始化
|
||||
* </p>
|
||||
*
|
||||
* @param dbConfig 数据库全局配置
|
||||
* @param tableInfo 表信息
|
||||
* @param fieldList 字段列表
|
||||
* @return true 继续下一个属性判断,返回 continue;
|
||||
*/
|
||||
private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
|
||||
List<TableFieldInfo> fieldList, Field field) {
|
||||
/* 获取注解属性,自定义字段 */
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
if (null == tableField) {
|
||||
return false;
|
||||
}
|
||||
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 判定 related 的值
|
||||
* </p>
|
||||
*
|
||||
* @param underCamel 驼峰命名
|
||||
* @param property 属性名
|
||||
* @param column 字段名
|
||||
* @return related
|
||||
*/
|
||||
public static boolean checkRelated(boolean underCamel, String property, String column) {
|
||||
if (StringUtils.isNotColumnName(column)) {
|
||||
// 首尾有转义符,手动在注解里设置了转义符,去除掉转义符
|
||||
column = column.substring(1, column.length() - 1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取该类的所有属性列表
|
||||
* </p>
|
||||
*
|
||||
* @param clazz 反射类
|
||||
* @return 属性集合
|
||||
*/
|
||||
public static List<Field> getAllFields(Class<?> clazz) {
|
||||
List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
|
||||
return fieldList.stream()
|
||||
.filter(field -> {
|
||||
/* 过滤注解非表字段属性 */
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
return (tableField == null || tableField.exist());
|
||||
}).collect(toList());
|
||||
}
|
||||
|
||||
public static KeyGenerator genKeyGenerator(String baseStatementId, TableInfo tableInfo, MapperBuilderAssistant builderAssistant) {
|
||||
IKeyGenerator keyGenerator = GlobalConfigUtils.getKeyGenerator(builderAssistant.getConfiguration());
|
||||
if (null == keyGenerator) {
|
||||
throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");
|
||||
}
|
||||
Configuration configuration = builderAssistant.getConfiguration();
|
||||
//TODO 这里不加上builderAssistant.getCurrentNamespace()的会导致com.baomidou.mybatisplus.core.parser.SqlParserHelper.getSqlParserInfo越(chu)界(gui)
|
||||
String id = builderAssistant.getCurrentNamespace() + StringPool.DOT + baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
||||
ResultMap resultMap = new ResultMap.Builder(builderAssistant.getConfiguration(), id, tableInfo.getKeyType(), new ArrayList<>()).build();
|
||||
MappedStatement mappedStatement = new MappedStatement.Builder(builderAssistant.getConfiguration(), id,
|
||||
new StaticSqlSource(configuration, keyGenerator.executeSql(tableInfo.getKeySequence().value())), SqlCommandType.SELECT)
|
||||
.keyProperty(tableInfo.getKeyProperty())
|
||||
.resultMaps(Collections.singletonList(resultMap))
|
||||
.build();
|
||||
configuration.addMappedStatement(mappedStatement);
|
||||
return new SelectKeyGenerator(mappedStatement, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package me.zhengjie.base;
|
||||
package me.zhengjie.base.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
|
@ -20,7 +20,7 @@ import java.util.List;
|
|||
public class BaseDao<I extends IService<T>, J extends JpaRepository<T, ID>, T, ID extends Serializable> {
|
||||
protected I mpService;
|
||||
protected J jpaRepository;
|
||||
@Value("${db.type.switch:true}")
|
||||
@Value("${db.type.switch:false}")
|
||||
protected boolean dbSwitch;
|
||||
|
||||
public BaseDao(I mpService, J jpaRepository) {
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package me.zhengjie.base.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.ElTableInfoHelper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.toolkit.*;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import org.apache.ibatis.binding.MapperMethod;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.reflection.ExceptionUtil;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.MyBatisExceptionTranslator;
|
||||
import org.mybatis.spring.SqlSessionHolder;
|
||||
import org.mybatis.spring.SqlSessionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* IService 实现类( 泛型:M 是 mapper 对象,T 是实体 )
|
||||
*
|
||||
* @author hubin
|
||||
* @since 2018-06-23
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
|
||||
|
||||
protected Log log = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
protected M baseMapper;
|
||||
|
||||
@Override
|
||||
public M getBaseMapper() {
|
||||
return baseMapper;
|
||||
}
|
||||
|
||||
protected Class<?> entityClass = currentModelClass();
|
||||
|
||||
/**
|
||||
* 判断数据库操作是否成功
|
||||
*
|
||||
* @param result 数据库操作返回影响条数
|
||||
* @return boolean
|
||||
* @deprecated 3.3.1
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean retBool(Integer result) {
|
||||
return SqlHelper.retBool(result);
|
||||
}
|
||||
|
||||
protected Class<T> currentModelClass() {
|
||||
return (Class<T>) ReflectionKit.getSuperClassGenericType(getClass(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作 SqlSession
|
||||
*
|
||||
* @deprecated 3.3.0
|
||||
*/
|
||||
@Deprecated
|
||||
protected SqlSession sqlSessionBatch() {
|
||||
return SqlHelper.sqlSessionBatch(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放sqlSession
|
||||
*
|
||||
* @param sqlSession session
|
||||
* @deprecated 3.3.0
|
||||
*/
|
||||
@Deprecated
|
||||
protected void closeSqlSession(SqlSession sqlSession) {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(entityClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 SqlStatement
|
||||
*
|
||||
* @param sqlMethod ignore
|
||||
* @return ignore
|
||||
*/
|
||||
protected String sqlStatement(SqlMethod sqlMethod) {
|
||||
return SqlHelper.table(entityClass).getSqlStatement(sqlMethod.getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入
|
||||
*
|
||||
* @param entityList ignore
|
||||
* @param batchSize ignore
|
||||
* @return ignore
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean saveBatch(Collection<T> entityList, int batchSize) {
|
||||
String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
|
||||
return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* TableId 注解存在更新记录,否插入一条记录
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @return boolean
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean saveOrUpdate(T entity) {
|
||||
if (null != entity) {
|
||||
Class<?> cls = entity.getClass();
|
||||
TableInfo tableInfo = ElTableInfoHelper.getTableInfo(cls);
|
||||
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
|
||||
String keyProperty = tableInfo.getKeyProperty();
|
||||
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
|
||||
Object idVal = ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty());
|
||||
return StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal)) ? save(entity) : updateById(entity);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
|
||||
TableInfo tableInfo = ElTableInfoHelper.getTableInfo(entityClass);
|
||||
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
|
||||
String keyProperty = tableInfo.getKeyProperty();
|
||||
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
|
||||
return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
|
||||
Object idVal = ReflectionKit.getFieldValue(entity, keyProperty);
|
||||
if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {
|
||||
sqlSession.insert(tableInfo.getSqlStatement(SqlMethod.INSERT_ONE.getMethod()), entity);
|
||||
} else {
|
||||
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
|
||||
param.put(Constants.ENTITY, entity);
|
||||
sqlSession.update(tableInfo.getSqlStatement(SqlMethod.UPDATE_BY_ID.getMethod()), param);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean updateBatchById(Collection<T> entityList, int batchSize) {
|
||||
String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
|
||||
return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
|
||||
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
|
||||
param.put(Constants.ENTITY, entity);
|
||||
sqlSession.update(sqlStatement, param);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
|
||||
if (throwEx) {
|
||||
return baseMapper.selectOne(queryWrapper);
|
||||
}
|
||||
return SqlHelper.getObject(log, baseMapper.selectList(queryWrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
|
||||
return SqlHelper.getObject(log, baseMapper.selectMaps(queryWrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
|
||||
return SqlHelper.getObject(log, listObjs(queryWrapper, mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行批量操作
|
||||
*
|
||||
* @param consumer consumer
|
||||
* @since 3.3.0
|
||||
* @deprecated 3.3.1 后面我打算移除掉 {@link #executeBatch(Collection, int, BiConsumer)} }.
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean executeBatch(Consumer<SqlSession> consumer) {
|
||||
SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(entityClass);
|
||||
SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
|
||||
boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
|
||||
if (sqlSessionHolder != null) {
|
||||
SqlSession sqlSession = sqlSessionHolder.getSqlSession();
|
||||
//原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
|
||||
//按道理来说,这里的值应该一直为false。
|
||||
sqlSession.commit(!transaction);
|
||||
}
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
if (!transaction) {
|
||||
log.warn("SqlSession [" + sqlSession + "] was not registered for synchronization because DataSource is not transactional");
|
||||
}
|
||||
try {
|
||||
consumer.accept(sqlSession);
|
||||
//非事物情况下,强制commit。
|
||||
sqlSession.commit(!transaction);
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
sqlSession.rollback();
|
||||
Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
|
||||
if (unwrapped instanceof RuntimeException) {
|
||||
MyBatisExceptionTranslator myBatisExceptionTranslator
|
||||
= new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
|
||||
throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped));
|
||||
}
|
||||
throw ExceptionUtils.mpe(unwrapped);
|
||||
} finally {
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行批量操作
|
||||
*
|
||||
* @param list 数据集合
|
||||
* @param batchSize 批量大小
|
||||
* @param consumer 执行方法
|
||||
* @param <E> 泛型
|
||||
* @return 操作结果
|
||||
* @since 3.3.1
|
||||
*/
|
||||
protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
|
||||
Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
|
||||
return !CollectionUtils.isEmpty(list) && executeBatch(sqlSession -> {
|
||||
int size = list.size();
|
||||
int i = 1;
|
||||
for (E element : list) {
|
||||
consumer.accept(sqlSession, element);
|
||||
if ((i % batchSize == 0) || i == size) {
|
||||
sqlSession.flushStatements();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行批量操作(默认批次提交数量{@link IService#DEFAULT_BATCH_SIZE})
|
||||
*
|
||||
* @param list 数据集合
|
||||
* @param consumer 执行方法
|
||||
* @param <E> 泛型
|
||||
* @return 操作结果
|
||||
* @since 3.3.1
|
||||
*/
|
||||
protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
|
||||
return executeBatch(list, DEFAULT_BATCH_SIZE, consumer);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
|
||||
import com.baomidou.mybatisplus.core.metadata.ElTableInfoHelper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SQL 自动注入器
|
||||
*
|
||||
* @author hubin
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:14
|
||||
*/
|
||||
public abstract class AbstractSqlInjector implements ISqlInjector {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractSqlInjector.class);
|
||||
|
||||
@Override
|
||||
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
|
||||
Class<?> modelClass = extractModelClass(mapperClass);
|
||||
if (modelClass != null) {
|
||||
String className = mapperClass.toString();
|
||||
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
|
||||
if (!mapperRegistryCache.contains(className)) {
|
||||
List<AbstractMethod> methodList = this.getMethodList(mapperClass);
|
||||
if (CollectionUtils.isNotEmpty(methodList)) {
|
||||
TableInfo tableInfo = ElTableInfoHelper.initTableInfo(builderAssistant, modelClass);
|
||||
// 循环注入自定义方法
|
||||
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
|
||||
} else {
|
||||
logger.debug(mapperClass.toString() + ", No effective injection method was found.");
|
||||
}
|
||||
mapperRegistryCache.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取 注入的方法
|
||||
* </p>
|
||||
*
|
||||
* @param mapperClass 当前mapper
|
||||
* @return 注入的方法集合
|
||||
* @since 3.1.2 add mapperClass
|
||||
*/
|
||||
public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass);
|
||||
|
||||
/**
|
||||
* 提取泛型模型,多泛型的时候请将泛型T放在第一位
|
||||
*
|
||||
* @param mapperClass mapper 接口
|
||||
* @return mapper 泛型
|
||||
*/
|
||||
protected Class<?> extractModelClass(Class<?> mapperClass) {
|
||||
Type[] types = mapperClass.getGenericInterfaces();
|
||||
ParameterizedType target = null;
|
||||
for (Type type : types) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type[] typeArray = ((ParameterizedType) type).getActualTypeArguments();
|
||||
if (ArrayUtils.isNotEmpty(typeArray)) {
|
||||
for (Type t : typeArray) {
|
||||
if (t instanceof TypeVariable || t instanceof WildcardType) {
|
||||
break;
|
||||
} else {
|
||||
target = (ParameterizedType) type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.methods.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* SQL 默认注入器
|
||||
*
|
||||
* @author hubin
|
||||
* /**
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:14
|
||||
*/
|
||||
@Component
|
||||
public class DefaultSqlInjector extends AbstractSqlInjector {
|
||||
|
||||
@Override
|
||||
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
|
||||
return Stream.of(
|
||||
new Insert(),
|
||||
new Delete(),
|
||||
new DeleteByMap(),
|
||||
new DeleteById(),
|
||||
new DeleteBatchByIds(),
|
||||
new Update(),
|
||||
new UpdateById(),
|
||||
new SelectById(),
|
||||
new SelectBatchByIds(),
|
||||
new SelectByMap(),
|
||||
new SelectOne(),
|
||||
new SelectCount(),
|
||||
new SelectMaps(),
|
||||
new SelectMapsPage(),
|
||||
new SelectObjs(),
|
||||
new SelectList(),
|
||||
new SelectPage()
|
||||
).collect(toList());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.annotation.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.apache.ibatis.type.UnknownTypeHandler;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* JPA 转换使用 javax.persistence.Column
|
||||
*
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/28 14:11
|
||||
*/
|
||||
public class TableFieldImp implements TableField {
|
||||
private String value = "";
|
||||
private Class<? extends Annotation> annotationType;
|
||||
private boolean exist = true;
|
||||
private String condition = "";
|
||||
private FieldStrategy insertStrategy = FieldStrategy.DEFAULT;
|
||||
private String update = "";
|
||||
private FieldStrategy updateStrategy = FieldStrategy.DEFAULT;
|
||||
private FieldStrategy whereStrategy = FieldStrategy.DEFAULT;
|
||||
private FieldFill fill = FieldFill.DEFAULT;
|
||||
private boolean select = true;
|
||||
private boolean keepGlobalFormat = false;
|
||||
private JdbcType jdbcType = JdbcType.UNDEFINED;
|
||||
private Class<? extends TypeHandler> typeHandler = UnknownTypeHandler.class;
|
||||
private String numericScale = "";
|
||||
|
||||
/**
|
||||
* 数据库字段值,
|
||||
* 不需要配置该值的情况:
|
||||
* <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,
|
||||
* (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase() </li>
|
||||
* <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,
|
||||
* 数据库字段值.toUpperCase() == 实体属性名.toUpperCase()</li>
|
||||
*/
|
||||
@Override
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为数据库表字段
|
||||
* 默认 true 存在,false 不存在
|
||||
*/
|
||||
@Override
|
||||
public boolean exist() {
|
||||
return exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段 where 实体查询比较条件
|
||||
* 默认 `=` 等值
|
||||
*/
|
||||
@Override
|
||||
public String condition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段 update set 部分注入, 该注解优于 el 注解使用
|
||||
* <p>
|
||||
* 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
|
||||
* 输出 SQL 为:update 表 set 字段=字段+1 where ...
|
||||
* <p>
|
||||
* 例2:@TableField(.. , update="now()") 使用数据库时间
|
||||
* 输出 SQL 为:update 表 set 字段=now() where ...
|
||||
*/
|
||||
@Override
|
||||
public String update() {
|
||||
return update;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段验证策略之 insert: 当insert操作时,该字段拼接insert语句时的策略
|
||||
* IGNORED: 直接拼接 insert into table_a(column) values (#{columnProperty});
|
||||
* NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
|
||||
* NOT_EMPTY: insert into table_a(<if test="columnProperty != null and columnProperty!=''">column</if>) values (<if test="columnProperty != null and columnProperty!=''">#{columnProperty}</if>)
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public FieldStrategy insertStrategy() {
|
||||
return insertStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段验证策略之 update: 当更新操作时,该字段拼接set语句时的策略
|
||||
* IGNORED: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去
|
||||
* NOT_NULL: update table_a set <if test="columnProperty != null">column=#{columnProperty}</if>
|
||||
* NOT_EMPTY: update table_a set <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public FieldStrategy updateStrategy() {
|
||||
return updateStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段验证策略之 where: 表示该字段在拼接where条件时的策略
|
||||
* IGNORED: 直接拼接 column=#{columnProperty}
|
||||
* NOT_NULL: <if test="columnProperty != null">column=#{columnProperty}</if>
|
||||
* NOT_EMPTY: <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public FieldStrategy whereStrategy() {
|
||||
return whereStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段自动填充策略
|
||||
*/
|
||||
@Override
|
||||
public FieldFill fill() {
|
||||
return fill;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否进行 select 查询
|
||||
* <p>大字段可设置为 false 不加入 select 查询范围</p>
|
||||
*/
|
||||
@Override
|
||||
public boolean select() {
|
||||
return select;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否保持使用全局的 Format 的值
|
||||
* <p> 只生效于 既设置了全局的 Format 也设置了上面 {@link #value()} 的值 </p>
|
||||
* <li> 如果是 false , 全局的 Format 不生效 </li>
|
||||
*
|
||||
* @since 3.1.1
|
||||
*/
|
||||
@Override
|
||||
public boolean keepGlobalFormat() {
|
||||
return keepGlobalFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* JDBC类型 (该默认值不代表会按照该值生效),
|
||||
* 只生效与 mp 自动注入的 method,
|
||||
* 建议配合 {@link TableName#autoResultMap()} 一起使用
|
||||
* <p>
|
||||
* {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public JdbcType jdbcType() {
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型处理器 (该默认值不代表会按照该值生效),
|
||||
* 只生效与 mp 自动注入的 method,
|
||||
* 建议配合 {@link TableName#autoResultMap()} 一起使用
|
||||
* <p>
|
||||
* {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends TypeHandler> typeHandler() {
|
||||
return typeHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定小数点后保留的位数,
|
||||
* 只生效与 mp 自动注入的 method,
|
||||
* 建议配合 {@link TableName#autoResultMap()} 一起使用
|
||||
* <p>
|
||||
* {@link ParameterMapping#numericScale}
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public String numericScale() {
|
||||
return numericScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotation type of this annotation.
|
||||
*
|
||||
* @return the annotation type of this annotation
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
this.annotationType = annotationType;
|
||||
}
|
||||
|
||||
public void setExist(boolean exist) {
|
||||
this.exist = exist;
|
||||
}
|
||||
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public void setInsertStrategy(FieldStrategy insertStrategy) {
|
||||
this.insertStrategy = insertStrategy;
|
||||
}
|
||||
|
||||
public void setUpdate(String update) {
|
||||
this.update = update;
|
||||
}
|
||||
|
||||
public void setUpdateStrategy(FieldStrategy updateStrategy) {
|
||||
this.updateStrategy = updateStrategy;
|
||||
}
|
||||
|
||||
public void setWhereStrategy(FieldStrategy whereStrategy) {
|
||||
this.whereStrategy = whereStrategy;
|
||||
}
|
||||
|
||||
public void setFill(FieldFill fill) {
|
||||
this.fill = fill;
|
||||
}
|
||||
|
||||
public void setSelect(boolean select) {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
public void setKeepGlobalFormat(boolean keepGlobalFormat) {
|
||||
this.keepGlobalFormat = keepGlobalFormat;
|
||||
}
|
||||
|
||||
public void setJdbcType(JdbcType jdbcType) {
|
||||
this.jdbcType = jdbcType;
|
||||
}
|
||||
|
||||
public void setTypeHandler(Class<? extends TypeHandler> typeHandler) {
|
||||
this.typeHandler = typeHandler;
|
||||
}
|
||||
|
||||
public void setNumericScale(String numericScale) {
|
||||
this.numericScale = numericScale;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.annotation.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* JPA 转换 ID
|
||||
* javax.persistence.Id
|
||||
* javax.persistence.Column
|
||||
*
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/28 14:01
|
||||
*/
|
||||
public class TableIdImp implements TableId {
|
||||
private String value = "";
|
||||
private IdType type = IdType.NONE;
|
||||
private Class<? extends Annotation> annotationType;
|
||||
|
||||
|
||||
/**
|
||||
* 字段值(驼峰命名方式,该值可无)
|
||||
*/
|
||||
@Override
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
* {@link IdType}
|
||||
*/
|
||||
@Override
|
||||
public IdType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotation type of this annotation.
|
||||
*
|
||||
* @return the annotation type of this annotation
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setType(IdType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
this.annotationType = annotationType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.annotation.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* JAP 转换使用
|
||||
*
|
||||
* @author liaojinlong
|
||||
* javax.persistence.Table
|
||||
* @since 2020/6/28 13:56
|
||||
*/
|
||||
public class TableNameImp implements TableName {
|
||||
private String value = "";
|
||||
private String schema = "";
|
||||
private String resultMap = "";
|
||||
private boolean keepGlobalPrefix = false;
|
||||
private String[] excludeProperty = new String[0];
|
||||
private boolean autoResultMap = false;
|
||||
private Class<? extends Annotation> annotationType;
|
||||
|
||||
/**
|
||||
* 实体对应的表名
|
||||
*/
|
||||
@Override
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* schema
|
||||
*
|
||||
* @since 3.1.1
|
||||
*/
|
||||
@Override
|
||||
public String schema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否保持使用全局的 tablePrefix 的值
|
||||
* <p> 只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值 </p>
|
||||
* <li> 如果是 false , 全局的 tablePrefix 不生效 </li>
|
||||
*
|
||||
* @since 3.1.1
|
||||
*/
|
||||
@Override
|
||||
public boolean keepGlobalPrefix() {
|
||||
return keepGlobalPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体映射结果集,
|
||||
* 只生效与 mp 自动注入的 method
|
||||
*/
|
||||
@Override
|
||||
public String resultMap() {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动构建 resultMap 并使用,
|
||||
* 只生效与 mp 自动注入的 method,
|
||||
* 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入,
|
||||
* 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
public boolean autoResultMap() {
|
||||
return autoResultMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 需要排除的属性名
|
||||
*
|
||||
* @since 3.3.1
|
||||
*/
|
||||
@Override
|
||||
public String[] excludeProperty() {
|
||||
return excludeProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotation type of this annotation.
|
||||
*
|
||||
* @return the annotation type of this annotation
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public void setResultMap(String resultMap) {
|
||||
this.resultMap = resultMap;
|
||||
}
|
||||
|
||||
public void setKeepGlobalPrefix(boolean keepGlobalPrefix) {
|
||||
this.keepGlobalPrefix = keepGlobalPrefix;
|
||||
}
|
||||
|
||||
public void setExcludeProperty(String[] excludeProperty) {
|
||||
this.excludeProperty = excludeProperty;
|
||||
}
|
||||
|
||||
public void setAutoResultMap(boolean autoResultMap) {
|
||||
this.autoResultMap = autoResultMap;
|
||||
}
|
||||
|
||||
public void setAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
this.annotationType = annotationType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
/**
|
||||
* Callback interface that can be customized a {@link MybatisConfiguration} object generated on auto-configuration.
|
||||
*
|
||||
* @author Kazuki Shimizu
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:08
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given a {@link MybatisConfiguration} object.
|
||||
*
|
||||
* @param configuration the configuration object to customize
|
||||
*/
|
||||
void customize(Configuration configuration);
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
|
||||
import me.zhengjie.mybatis.core.MybatisConfiguration;
|
||||
import me.zhengjie.mybatis.core.MybatisSqlSessionFactoryBean;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.mapper.MapperFactoryBean;
|
||||
import org.mybatis.spring.mapper.MapperScannerConfigurer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a
|
||||
* {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.
|
||||
* <p>
|
||||
* If {@link org.mybatis.spring.annotation.MapperScan} is used, or a
|
||||
* configuration file is specified as a property, those will be considered,
|
||||
* otherwise this auto-configuration will attempt to register mappers based on
|
||||
* the interface definitions in or under the root auto-configuration package.
|
||||
* </p>
|
||||
* <p> copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}</p>
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Josh Long
|
||||
* @author Kazuki Shimizu
|
||||
* @author Eduardo Macarrón
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:10
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
|
||||
@ConditionalOnSingleCandidate(DataSource.class)
|
||||
@EnableConfigurationProperties(MybatisPlusProperties.class)
|
||||
@AutoConfigureAfter({DataSourceAutoConfiguration.class,})
|
||||
public class MybatisPlusAutoConfiguration implements InitializingBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);
|
||||
|
||||
private final MybatisPlusProperties properties;
|
||||
|
||||
private final Interceptor[] interceptors;
|
||||
|
||||
private final TypeHandler[] typeHandlers;
|
||||
|
||||
private final LanguageDriver[] languageDrivers;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final DatabaseIdProvider databaseIdProvider;
|
||||
|
||||
private final List<ConfigurationCustomizer> configurationCustomizers;
|
||||
|
||||
private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
|
||||
public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,
|
||||
ObjectProvider<Interceptor[]> interceptorsProvider,
|
||||
ObjectProvider<TypeHandler[]> typeHandlersProvider,
|
||||
ObjectProvider<LanguageDriver[]> languageDriversProvider,
|
||||
ResourceLoader resourceLoader,
|
||||
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
|
||||
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
|
||||
ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,
|
||||
ApplicationContext applicationContext) {
|
||||
this.properties = properties;
|
||||
this.interceptors = interceptorsProvider.getIfAvailable();
|
||||
this.typeHandlers = typeHandlersProvider.getIfAvailable();
|
||||
this.languageDrivers = languageDriversProvider.getIfAvailable();
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
|
||||
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
|
||||
this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) {
|
||||
mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));
|
||||
}
|
||||
checkConfigFileExists();
|
||||
}
|
||||
|
||||
private void checkConfigFileExists() {
|
||||
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
|
||||
Assert.state(resource.exists(),
|
||||
"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
||||
// TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
|
||||
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
|
||||
factory.setDataSource(dataSource);
|
||||
factory.setVfs(SpringBootVFS.class);
|
||||
if (StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
|
||||
}
|
||||
applyConfiguration(factory);
|
||||
if (this.properties.getConfigurationProperties() != null) {
|
||||
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.interceptors)) {
|
||||
factory.setPlugins(this.interceptors);
|
||||
}
|
||||
if (this.databaseIdProvider != null) {
|
||||
factory.setDatabaseIdProvider(this.databaseIdProvider);
|
||||
}
|
||||
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
|
||||
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
|
||||
}
|
||||
if (this.properties.getTypeAliasesSuperType() != null) {
|
||||
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
|
||||
}
|
||||
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
|
||||
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
|
||||
factory.setTypeHandlers(this.typeHandlers);
|
||||
}
|
||||
Resource[] mapperLocations = this.properties.resolveMapperLocations();
|
||||
if (!ObjectUtils.isEmpty(mapperLocations)) {
|
||||
factory.setMapperLocations(mapperLocations);
|
||||
}
|
||||
// TODO 修改源码支持定义 TransactionFactory
|
||||
this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);
|
||||
|
||||
// TODO 对源码做了一定的修改(因为源码适配了老旧的mybatis版本,但我们不需要适配)
|
||||
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
|
||||
if (!ObjectUtils.isEmpty(this.languageDrivers)) {
|
||||
factory.setScriptingLanguageDrivers(this.languageDrivers);
|
||||
}
|
||||
Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);
|
||||
|
||||
// TODO 自定义枚举包
|
||||
if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
|
||||
factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
|
||||
}
|
||||
// TODO 此处必为非 NULL
|
||||
GlobalConfig globalConfig = this.properties.getGlobalConfig();
|
||||
// TODO 注入填充器
|
||||
this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
|
||||
// TODO 注入主键生成器
|
||||
this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
|
||||
// TODO 注入sql注入器
|
||||
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
|
||||
// TODO 注入ID生成器
|
||||
this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
|
||||
// TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
|
||||
factory.setGlobalConfig(globalConfig);
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查spring容器里是否有对应的bean,有则进行消费
|
||||
*
|
||||
* @param clazz class
|
||||
* @param consumer 消费
|
||||
* @param <T> 泛型
|
||||
*/
|
||||
private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {
|
||||
if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {
|
||||
consumer.accept(this.applicationContext.getBean(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 入参使用 MybatisSqlSessionFactoryBean
|
||||
private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {
|
||||
// TODO 使用 MybatisConfiguration
|
||||
MybatisConfiguration configuration = this.properties.getConfiguration();
|
||||
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
configuration = new MybatisConfiguration();
|
||||
}
|
||||
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
|
||||
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
|
||||
customizer.customize(configuration);
|
||||
}
|
||||
}
|
||||
factory.setConfiguration(configuration);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
|
||||
ExecutorType executorType = this.properties.getExecutorType();
|
||||
if (executorType != null) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory, executorType);
|
||||
} else {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use
|
||||
* {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,
|
||||
* similar to using Spring Data JPA repositories.
|
||||
*/
|
||||
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
if (!AutoConfigurationPackages.has(this.beanFactory)) {
|
||||
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Searching for mappers annotated with @Mapper");
|
||||
|
||||
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
|
||||
if (logger.isDebugEnabled()) {
|
||||
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
|
||||
builder.addPropertyValue("processPropertyPlaceHolders", true);
|
||||
builder.addPropertyValue("annotationClass", Mapper.class);
|
||||
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
|
||||
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
|
||||
Stream.of(beanWrapper.getPropertyDescriptors())
|
||||
// Need to mybatis-spring 2.0.2+
|
||||
.filter(x -> x.getName().equals("lazyInitialization")).findAny()
|
||||
.ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
|
||||
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
|
||||
* mappers based on the same component-scanning path as Spring Boot itself.
|
||||
*/
|
||||
@Configuration
|
||||
@Import(AutoConfiguredMapperScannerRegistrar.class)
|
||||
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
|
||||
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
logger.debug(
|
||||
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import me.zhengjie.mybatis.core.MybatisConfiguration;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Configuration properties for MyBatis.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:11
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS)
|
||||
public class MybatisPlusProperties {
|
||||
|
||||
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
/**
|
||||
* Location of MyBatis xml config file.
|
||||
*/
|
||||
private String configLocation;
|
||||
|
||||
/**
|
||||
* Locations of MyBatis mapper files.
|
||||
*
|
||||
* @since 3.1.2 add default value
|
||||
*/
|
||||
private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
|
||||
|
||||
/**
|
||||
* Packages to search type aliases. (Package delimiters are ",; \t\n")
|
||||
*/
|
||||
private String typeAliasesPackage;
|
||||
|
||||
/**
|
||||
* The super class for filtering type alias.
|
||||
* If this not specifies, the MyBatis deal as type alias all classes that searched from typeAliasesPackage.
|
||||
*/
|
||||
private Class<?> typeAliasesSuperType;
|
||||
|
||||
/**
|
||||
* Packages to search for type handlers. (Package delimiters are ",; \t\n")
|
||||
*/
|
||||
private String typeHandlersPackage;
|
||||
|
||||
/**
|
||||
* Indicates whether perform presence check of the MyBatis xml config file.
|
||||
*/
|
||||
private boolean checkConfigLocation = false;
|
||||
|
||||
/**
|
||||
* Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
|
||||
*/
|
||||
private ExecutorType executorType;
|
||||
|
||||
/**
|
||||
* The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
|
||||
* <p>
|
||||
* 如果设置了这个,你会至少失去几乎所有 mp 提供的功能
|
||||
*/
|
||||
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
|
||||
|
||||
/**
|
||||
* Externalized properties for MyBatis configuration.
|
||||
*/
|
||||
private Properties configurationProperties;
|
||||
|
||||
/**
|
||||
* A Configuration object for customize default settings. If {@link #configLocation}
|
||||
* is specified, this property is not used.
|
||||
* TODO 使用 MybatisConfiguration
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private MybatisConfiguration configuration;
|
||||
|
||||
/**
|
||||
* TODO 枚举包扫描
|
||||
*/
|
||||
private String typeEnumsPackage;
|
||||
|
||||
/**
|
||||
* TODO 全局配置
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private GlobalConfig globalConfig = GlobalConfigUtils.defaults();
|
||||
|
||||
|
||||
public Resource[] resolveMapperLocations() {
|
||||
return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))
|
||||
.flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new);
|
||||
}
|
||||
|
||||
private Resource[] getResources(String location) {
|
||||
try {
|
||||
return resourceResolver.getResources(location);
|
||||
} catch (IOException e) {
|
||||
return new Resource[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
/**
|
||||
* Callback interface that can be customized a {@link MybatisPlusProperties} object generated on auto-configuration.
|
||||
*
|
||||
* <p> 慎用 </p>
|
||||
*
|
||||
* @author miemie
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:11
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MybatisPlusPropertiesCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given a {@link MybatisPlusProperties} object.
|
||||
*
|
||||
* @param properties the MybatisPlusProperties object to customize
|
||||
*/
|
||||
void customize(MybatisPlusProperties properties);
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.AES;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.OriginTrackedMapPropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.SimpleCommandLinePropertySource;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 安全加密处理器
|
||||
*
|
||||
* @author hubin
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:12
|
||||
*/
|
||||
public class SafetyEncryptProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
/**
|
||||
* 命令行中获取密钥
|
||||
*/
|
||||
String mpwKey = null;
|
||||
for (PropertySource<?> ps : environment.getPropertySources()) {
|
||||
if (ps instanceof SimpleCommandLinePropertySource) {
|
||||
SimpleCommandLinePropertySource source = (SimpleCommandLinePropertySource) ps;
|
||||
mpwKey = source.getProperty("mpw.key");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 处理加密内容
|
||||
*/
|
||||
if (StringUtils.isNotBlank(mpwKey)) {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
for (PropertySource<?> ps : environment.getPropertySources()) {
|
||||
if (ps instanceof OriginTrackedMapPropertySource) {
|
||||
OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) ps;
|
||||
for (String name : source.getPropertyNames()) {
|
||||
Object value = source.getProperty(name);
|
||||
if (value instanceof String) {
|
||||
String str = (String) value;
|
||||
if (str.startsWith("mpw:")) {
|
||||
map.put(name, AES.decrypt(str.substring(4), mpwKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将解密的数据放入环境变量,并处于第一优先级上
|
||||
if (CollectionUtils.isNotEmpty(map)) {
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.autoconfig;
|
||||
|
||||
|
||||
import org.apache.ibatis.io.VFS;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Hans Westerbeek
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
*/
|
||||
public class SpringBootVFS extends VFS {
|
||||
|
||||
private final ResourcePatternResolver resourceResolver;
|
||||
|
||||
public SpringBootVFS() {
|
||||
this.resourceResolver = new PathMatchingResourcePatternResolver(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String preserveSubpackageName(final String baseUrlString, final Resource resource,
|
||||
final String rootPath) {
|
||||
try {
|
||||
return rootPath + (rootPath.endsWith("/") ? "" : "/")
|
||||
+ resource.getURL().toString().substring(baseUrlString.length());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> list(URL url, String path) throws IOException {
|
||||
String urlString = url.toString();
|
||||
String baseUrlString = urlString.endsWith("/") ? urlString : urlString.concat("/");
|
||||
Resource[] resources = resourceResolver.getResources(baseUrlString + "**/*.class");
|
||||
return Stream.of(resources).map(resource -> preserveSubpackageName(baseUrlString, resource, path))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
||||
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Assert;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Mybatis全局缓存工具类
|
||||
*
|
||||
* @author Caratacus
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:12
|
||||
*/
|
||||
public class GlobalConfigUtils {
|
||||
|
||||
/**
|
||||
* 获取当前的SqlSessionFactory
|
||||
*
|
||||
* @param clazz 实体类
|
||||
*/
|
||||
public static SqlSessionFactory currentSessionFactory(Class<?> clazz) {
|
||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
|
||||
Assert.notNull(tableInfo, ClassUtils.getUserClass(clazz).getName() + " Not Found TableInfoCache.");
|
||||
return getGlobalConfig(tableInfo.getConfiguration()).getSqlSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认 MybatisGlobalConfig
|
||||
* <p>FIXME 这可能是一个伪装成单例模式的原型模式,暂时不确定</p>
|
||||
*/
|
||||
public static GlobalConfig defaults() {
|
||||
return new GlobalConfig().setDbConfig(new GlobalConfig.DbConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MybatisGlobalConfig (统一所有入口)
|
||||
*
|
||||
* @param configuration Mybatis 容器配置对象
|
||||
*/
|
||||
public static GlobalConfig getGlobalConfig(Configuration configuration) {
|
||||
Assert.notNull(configuration, "Error: You need Initialize MybatisConfiguration !");
|
||||
return ((MybatisConfiguration) configuration).getGlobalConfig();
|
||||
}
|
||||
|
||||
public static IKeyGenerator getKeyGenerator(Configuration configuration) {
|
||||
return getGlobalConfig(configuration).getDbConfig().getKeyGenerator();
|
||||
}
|
||||
|
||||
public static IdType getIdType(Configuration configuration) {
|
||||
return getGlobalConfig(configuration).getDbConfig().getIdType();
|
||||
}
|
||||
|
||||
public static ISqlInjector getSqlInjector(Configuration configuration) {
|
||||
return getGlobalConfig(configuration).getSqlInjector();
|
||||
}
|
||||
|
||||
public static Optional<MetaObjectHandler> getMetaObjectHandler(Configuration configuration) {
|
||||
return Optional.ofNullable(getGlobalConfig(configuration).getMetaObjectHandler());
|
||||
}
|
||||
|
||||
public static Class<?> getSuperMapperClass(Configuration configuration) {
|
||||
return getGlobalConfig(configuration).getSuperMapperClass();
|
||||
}
|
||||
|
||||
public static boolean isSupperMapperChildren(Configuration configuration, Class<?> mapperClass) {
|
||||
return getSuperMapperClass(configuration).isAssignableFrom(mapperClass);
|
||||
}
|
||||
|
||||
public static Set<String> getMapperRegistryCache(Configuration configuration) {
|
||||
return getGlobalConfig(configuration).getMapperRegistryCache();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.executor.MybatisBatchExecutor;
|
||||
import com.baomidou.mybatisplus.core.executor.MybatisCachingExecutor;
|
||||
import com.baomidou.mybatisplus.core.executor.MybatisReuseExecutor;
|
||||
import com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor;
|
||||
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.ibatis.binding.MapperRegistry;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* replace default Configuration class
|
||||
* <p>Caratacus 2016/9/25 replace mapperRegistry</p>
|
||||
* 适配
|
||||
*
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 13:54
|
||||
*/
|
||||
public class MybatisConfiguration extends com.baomidou.mybatisplus.core.MybatisConfiguration {
|
||||
private static final Log logger = LogFactory.getLog(MybatisConfiguration.class);
|
||||
/**
|
||||
* Mapper 注册
|
||||
*/
|
||||
protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
|
||||
|
||||
public MybatisConfiguration(Environment environment) {
|
||||
this();
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private GlobalConfig globalConfig = GlobalConfigUtils.defaults();
|
||||
|
||||
/**
|
||||
* 初始化调用
|
||||
*/
|
||||
public MybatisConfiguration() {
|
||||
super();
|
||||
this.mapUnderscoreToCamelCase = true;
|
||||
languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* MybatisPlus 加载 SQL 顺序:
|
||||
* <p>1、加载XML中的SQL</p>
|
||||
* <p>2、加载sqlProvider中的SQL</p>
|
||||
* <p>3、xmlSql 与 sqlProvider不能包含相同的SQL</p>
|
||||
* <p>调整后的SQL优先级:xmlSql > sqlProvider > curdSql</p>
|
||||
*/
|
||||
@Override
|
||||
public void addMappedStatement(MappedStatement ms) {
|
||||
logger.debug("addMappedStatement: " + ms.getId());
|
||||
if (mappedStatements.containsKey(ms.getId())) {
|
||||
/*
|
||||
* 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
|
||||
*/
|
||||
logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
|
||||
return;
|
||||
}
|
||||
super.addMappedStatement(ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public MapperRegistry getMapperRegistry() {
|
||||
return mybatisMapperRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public <T> void addMapper(Class<T> type) {
|
||||
mybatisMapperRegistry.addMapper(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public void addMappers(String packageName, Class<?> superType) {
|
||||
mybatisMapperRegistry.addMappers(packageName, superType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public void addMappers(String packageName) {
|
||||
mybatisMapperRegistry.addMappers(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
||||
return mybatisMapperRegistry.getMapper(type, sqlSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 MybatisMapperRegistry
|
||||
*/
|
||||
@Override
|
||||
public boolean hasMapper(Class<?> type) {
|
||||
return mybatisMapperRegistry.hasMapper(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定动态SQL生成的默认语言
|
||||
*
|
||||
* @param driver LanguageDriver
|
||||
*/
|
||||
@Override
|
||||
public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> driver) {
|
||||
if (driver == null) {
|
||||
//todo 替换动态SQL生成的默认语言为自己的。
|
||||
driver = MybatisXMLLanguageDriver.class;
|
||||
}
|
||||
getLanguageRegistry().setDefaultDriverClass(driver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
|
||||
executorType = executorType == null ? defaultExecutorType : executorType;
|
||||
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
|
||||
Executor executor;
|
||||
if (ExecutorType.BATCH == executorType) {
|
||||
executor = new MybatisBatchExecutor(this, transaction);
|
||||
} else if (ExecutorType.REUSE == executorType) {
|
||||
executor = new MybatisReuseExecutor(this, transaction);
|
||||
} else {
|
||||
executor = new MybatisSimpleExecutor(this, transaction);
|
||||
}
|
||||
if (cacheEnabled) {
|
||||
executor = new MybatisCachingExecutor(executor);
|
||||
}
|
||||
executor = (Executor) interceptorChain.pluginAll(executor);
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisMethodResolver;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.annotations.ResultMap;
|
||||
import org.apache.ibatis.annotations.Options.FlushCachePolicy;
|
||||
import org.apache.ibatis.binding.BindingException;
|
||||
import org.apache.ibatis.binding.MapperMethod;
|
||||
import org.apache.ibatis.builder.BuilderException;
|
||||
import org.apache.ibatis.builder.CacheRefResolver;
|
||||
import org.apache.ibatis.builder.IncompleteElementException;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
|
||||
import org.apache.ibatis.builder.annotation.MethodResolver;
|
||||
import org.apache.ibatis.builder.annotation.ProviderSqlSource;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.io.Resources;
|
||||
import org.apache.ibatis.mapping.*;
|
||||
import org.apache.ibatis.parsing.PropertyParser;
|
||||
import org.apache.ibatis.reflection.TypeParameterResolver;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
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.UnknownTypeHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 继承
|
||||
* <p>
|
||||
* 只重写了 {@link MapperAnnotationBuilder#parse} 和 #getReturnType
|
||||
* 没有XML配置文件注入基础CRUD方法
|
||||
* </p>
|
||||
*
|
||||
* @author Caratacus
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:13
|
||||
*/
|
||||
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
|
||||
|
||||
private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();
|
||||
private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();
|
||||
|
||||
static {
|
||||
SQL_ANNOTATION_TYPES.add(Select.class);
|
||||
SQL_ANNOTATION_TYPES.add(Insert.class);
|
||||
SQL_ANNOTATION_TYPES.add(Update.class);
|
||||
SQL_ANNOTATION_TYPES.add(Delete.class);
|
||||
|
||||
SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class);
|
||||
SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class);
|
||||
SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class);
|
||||
SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class);
|
||||
}
|
||||
|
||||
private final MybatisConfiguration configuration;
|
||||
private final MapperBuilderAssistant assistant;
|
||||
private final Class<?> type;
|
||||
|
||||
public MybatisMapperAnnotationBuilder(MybatisConfiguration configuration, Class<?> type) {
|
||||
super(configuration, type);
|
||||
String resource = type.getName().replace('.', '/') + ".java (best guess)";
|
||||
this.assistant = new MapperBuilderAssistant(configuration, resource);
|
||||
this.configuration = configuration;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() {
|
||||
String resource = type.toString();
|
||||
if (!configuration.isResourceLoaded(resource)) {
|
||||
loadXmlResource();
|
||||
configuration.addLoadedResource(resource);
|
||||
final String typeName = type.getName();
|
||||
assistant.setCurrentNamespace(typeName);
|
||||
parseCache();
|
||||
parseCacheRef();
|
||||
SqlParserHelper.initSqlParserInfoCache(type);
|
||||
Method[] methods = type.getMethods();
|
||||
for (Method method : methods) {
|
||||
try {
|
||||
// issue #237
|
||||
if (!method.isBridge()) {
|
||||
parseStatement(method);
|
||||
SqlParserHelper.initSqlParserInfoCache(typeName, method);
|
||||
}
|
||||
} catch (IncompleteElementException e) {
|
||||
// TODO 使用 MybatisMethodResolver 而不是 MethodResolver
|
||||
configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));
|
||||
}
|
||||
}
|
||||
// TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sql
|
||||
if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
|
||||
GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
|
||||
}
|
||||
}
|
||||
parsePendingMethods();
|
||||
}
|
||||
|
||||
private void parsePendingMethods() {
|
||||
Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
|
||||
synchronized (incompleteMethods) {
|
||||
Iterator<MethodResolver> iter = incompleteMethods.iterator();
|
||||
while (iter.hasNext()) {
|
||||
try {
|
||||
iter.next().resolve();
|
||||
iter.remove();
|
||||
} catch (IncompleteElementException e) {
|
||||
// This method is still missing a resource
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadXmlResource() {
|
||||
// Spring may not know the real resource name so we check a flag
|
||||
// to prevent loading again a resource twice
|
||||
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
|
||||
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
|
||||
String xmlResource = type.getName().replace('.', '/') + ".xml";
|
||||
// #1347
|
||||
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
|
||||
if (inputStream == null) {
|
||||
// Search XML mapper that is not in the module but in the classpath.
|
||||
try {
|
||||
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
|
||||
} catch (IOException e2) {
|
||||
// ignore, resource is not required
|
||||
}
|
||||
}
|
||||
if (inputStream != null) {
|
||||
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
|
||||
xmlParser.parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCache() {
|
||||
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
|
||||
if (cacheDomain != null) {
|
||||
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
|
||||
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
|
||||
Properties props = convertToProperties(cacheDomain.properties());
|
||||
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties convertToProperties(Property[] properties) {
|
||||
if (properties.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Properties props = new Properties();
|
||||
for (Property property : properties) {
|
||||
props.setProperty(property.name(),
|
||||
PropertyParser.parse(property.value(), configuration.getVariables()));
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private void parseCacheRef() {
|
||||
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
|
||||
if (cacheDomainRef != null) {
|
||||
Class<?> refType = cacheDomainRef.value();
|
||||
String refName = cacheDomainRef.name();
|
||||
if (refType == void.class && refName.isEmpty()) {
|
||||
throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
|
||||
}
|
||||
if (refType != void.class && !refName.isEmpty()) {
|
||||
throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
|
||||
}
|
||||
String namespace = (refType != void.class) ? refType.getName() : refName;
|
||||
try {
|
||||
assistant.useCacheRef(namespace);
|
||||
} catch (IncompleteElementException e) {
|
||||
configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String parseResultMap(Method method) {
|
||||
Class<?> returnType = getReturnType(method);
|
||||
Arg[] args = method.getAnnotationsByType(Arg.class);
|
||||
Result[] results = method.getAnnotationsByType(Result.class);
|
||||
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
|
||||
String resultMapId = generateResultMapName(method);
|
||||
applyResultMap(resultMapId, returnType, args, results, typeDiscriminator);
|
||||
return resultMapId;
|
||||
}
|
||||
|
||||
private String generateResultMapName(Method method) {
|
||||
Results results = method.getAnnotation(Results.class);
|
||||
if (results != null && !results.id().isEmpty()) {
|
||||
return type.getName() + "." + results.id();
|
||||
}
|
||||
StringBuilder suffix = new StringBuilder();
|
||||
for (Class<?> c : method.getParameterTypes()) {
|
||||
suffix.append("-");
|
||||
suffix.append(c.getSimpleName());
|
||||
}
|
||||
if (suffix.length() < 1) {
|
||||
suffix.append("-void");
|
||||
}
|
||||
return type.getName() + "." + method.getName() + suffix;
|
||||
}
|
||||
|
||||
private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
|
||||
List<ResultMapping> resultMappings = new ArrayList<>();
|
||||
applyConstructorArgs(args, returnType, resultMappings);
|
||||
applyResults(results, returnType, resultMappings);
|
||||
Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
|
||||
// TODO add AutoMappingBehaviour
|
||||
assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
|
||||
createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
|
||||
}
|
||||
|
||||
private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
||||
if (discriminator != null) {
|
||||
for (Case c : discriminator.cases()) {
|
||||
String caseResultMapId = resultMapId + "-" + c.value();
|
||||
List<ResultMapping> resultMappings = new ArrayList<>();
|
||||
// issue #136
|
||||
applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
|
||||
applyResults(c.results(), resultType, resultMappings);
|
||||
// TODO add AutoMappingBehaviour
|
||||
assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
||||
if (discriminator != null) {
|
||||
String column = discriminator.column();
|
||||
Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
|
||||
JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
|
||||
(discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());
|
||||
Case[] cases = discriminator.cases();
|
||||
Map<String, String> discriminatorMap = new HashMap<>();
|
||||
for (Case c : cases) {
|
||||
String value = c.value();
|
||||
String caseResultMapId = resultMapId + "-" + value;
|
||||
discriminatorMap.put(value, caseResultMapId);
|
||||
}
|
||||
return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void parseStatement(Method method) {
|
||||
Class<?> parameterTypeClass = getParameterType(method);
|
||||
LanguageDriver languageDriver = getLanguageDriver(method);
|
||||
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
|
||||
if (sqlSource != null) {
|
||||
Options options = method.getAnnotation(Options.class);
|
||||
final String mappedStatementId = type.getName() + "." + method.getName();
|
||||
Integer fetchSize = null;
|
||||
Integer timeout = null;
|
||||
StatementType statementType = StatementType.PREPARED;
|
||||
ResultSetType resultSetType = configuration.getDefaultResultSetType();
|
||||
SqlCommandType sqlCommandType = getSqlCommandType(method);
|
||||
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
|
||||
boolean flushCache = !isSelect;
|
||||
boolean useCache = isSelect;
|
||||
|
||||
KeyGenerator keyGenerator;
|
||||
String keyProperty = null;
|
||||
String keyColumn = null;
|
||||
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
|
||||
// first check for SelectKey annotation - that overrides everything else
|
||||
SelectKey selectKey = method.getAnnotation(SelectKey.class);
|
||||
if (selectKey != null) {
|
||||
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
|
||||
keyProperty = selectKey.keyProperty();
|
||||
} else if (options == null) {
|
||||
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
|
||||
} else {
|
||||
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
|
||||
keyProperty = options.keyProperty();
|
||||
keyColumn = options.keyColumn();
|
||||
}
|
||||
} else {
|
||||
keyGenerator = NoKeyGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
if (options != null) {
|
||||
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
|
||||
flushCache = true;
|
||||
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
|
||||
flushCache = false;
|
||||
}
|
||||
useCache = options.useCache();
|
||||
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
|
||||
timeout = options.timeout() > -1 ? options.timeout() : null;
|
||||
statementType = options.statementType();
|
||||
if (options.resultSetType() != ResultSetType.DEFAULT) {
|
||||
resultSetType = options.resultSetType();
|
||||
}
|
||||
}
|
||||
|
||||
String resultMapId = null;
|
||||
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
|
||||
if (resultMapAnnotation != null) {
|
||||
resultMapId = String.join(",", resultMapAnnotation.value());
|
||||
} else if (isSelect) {
|
||||
resultMapId = parseResultMap(method);
|
||||
}
|
||||
|
||||
assistant.addMappedStatement(
|
||||
mappedStatementId,
|
||||
sqlSource,
|
||||
statementType,
|
||||
sqlCommandType,
|
||||
fetchSize,
|
||||
timeout,
|
||||
// ParameterMapID
|
||||
null,
|
||||
parameterTypeClass,
|
||||
resultMapId,
|
||||
getReturnType(method),
|
||||
resultSetType,
|
||||
flushCache,
|
||||
useCache,
|
||||
// TODO gcode issue #577
|
||||
false,
|
||||
keyGenerator,
|
||||
keyProperty,
|
||||
keyColumn,
|
||||
// DatabaseID
|
||||
null,
|
||||
languageDriver,
|
||||
// ResultSets
|
||||
options != null ? nullOrEmpty(options.resultSets()) : null);
|
||||
}
|
||||
}
|
||||
|
||||
private LanguageDriver getLanguageDriver(Method method) {
|
||||
Lang lang = method.getAnnotation(Lang.class);
|
||||
Class<? extends LanguageDriver> langClass = null;
|
||||
if (lang != null) {
|
||||
langClass = lang.value();
|
||||
}
|
||||
return configuration.getLanguageDriver(langClass);
|
||||
}
|
||||
|
||||
private Class<?> getParameterType(Method method) {
|
||||
Class<?> parameterType = null;
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
for (Class<?> currentParameterType : parameterTypes) {
|
||||
if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
|
||||
if (parameterType == null) {
|
||||
parameterType = currentParameterType;
|
||||
} else {
|
||||
// issue #135
|
||||
parameterType = MapperMethod.ParamMap.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameterType;
|
||||
}
|
||||
|
||||
private Class<?> getReturnType(Method method) {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
|
||||
if (resolvedReturnType instanceof Class) {
|
||||
returnType = (Class<?>) resolvedReturnType;
|
||||
if (returnType.isArray()) {
|
||||
returnType = returnType.getComponentType();
|
||||
}
|
||||
// gcode issue #508
|
||||
if (void.class.equals(returnType)) {
|
||||
ResultType rt = method.getAnnotation(ResultType.class);
|
||||
if (rt != null) {
|
||||
returnType = rt.value();
|
||||
}
|
||||
}
|
||||
} else if (resolvedReturnType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
|
||||
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
|
||||
if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
|
||||
Type returnTypeParameter = actualTypeArguments[0];
|
||||
if (returnTypeParameter instanceof Class<?>) {
|
||||
returnType = (Class<?>) returnTypeParameter;
|
||||
} else if (returnTypeParameter instanceof ParameterizedType) {
|
||||
// (gcode issue #443) actual type can be a also a parameterized type
|
||||
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
||||
} else if (returnTypeParameter instanceof GenericArrayType) {
|
||||
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
|
||||
// (gcode issue #525) support List<byte[]>
|
||||
returnType = Array.newInstance(componentType, 0).getClass();
|
||||
}
|
||||
}
|
||||
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
|
||||
// (gcode issue 504) Do not look into Maps if there is not MapKey annotation
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
|
||||
Type returnTypeParameter = actualTypeArguments[1];
|
||||
if (returnTypeParameter instanceof Class<?>) {
|
||||
returnType = (Class<?>) returnTypeParameter;
|
||||
} else if (returnTypeParameter instanceof ParameterizedType) {
|
||||
// (gcode issue 443) actual type can be a also a parameterized type
|
||||
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
||||
}
|
||||
}
|
||||
} else if (Optional.class.equals(rawType)) {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
Type returnTypeParameter = actualTypeArguments[0];
|
||||
if (returnTypeParameter instanceof Class<?>) {
|
||||
returnType = (Class<?>) returnTypeParameter;
|
||||
}
|
||||
}
|
||||
// TODO 下面是支援 IPage 及其子类作为返回值的
|
||||
else if (IPage.class.isAssignableFrom(rawType)) {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
Type returnTypeParameter = actualTypeArguments[0];
|
||||
if (returnTypeParameter instanceof Class<?>) {
|
||||
returnType = (Class<?>) returnTypeParameter;
|
||||
} else if (returnTypeParameter instanceof ParameterizedType) {
|
||||
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
||||
}
|
||||
}
|
||||
// TODO 上面是支援 IPage 及其子类作为返回值的
|
||||
}
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
|
||||
try {
|
||||
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
|
||||
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
|
||||
if (sqlAnnotationType != null) {
|
||||
if (sqlProviderAnnotationType != null) {
|
||||
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
|
||||
}
|
||||
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
|
||||
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
|
||||
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
|
||||
} else if (sqlProviderAnnotationType != null) {
|
||||
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
|
||||
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
||||
final StringBuilder sql = new StringBuilder();
|
||||
for (String fragment : strings) {
|
||||
sql.append(fragment);
|
||||
sql.append(" ");
|
||||
}
|
||||
return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
|
||||
}
|
||||
|
||||
private SqlCommandType getSqlCommandType(Method method) {
|
||||
Class<? extends Annotation> type = getSqlAnnotationType(method);
|
||||
|
||||
if (type == null) {
|
||||
type = getSqlProviderAnnotationType(method);
|
||||
|
||||
if (type == null) {
|
||||
return SqlCommandType.UNKNOWN;
|
||||
}
|
||||
|
||||
if (type == SelectProvider.class) {
|
||||
type = Select.class;
|
||||
} else if (type == InsertProvider.class) {
|
||||
type = Insert.class;
|
||||
} else if (type == UpdateProvider.class) {
|
||||
type = Update.class;
|
||||
} else if (type == DeleteProvider.class) {
|
||||
type = Delete.class;
|
||||
}
|
||||
}
|
||||
|
||||
return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> getSqlAnnotationType(Method method) {
|
||||
return chooseAnnotationType(method, SQL_ANNOTATION_TYPES);
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
|
||||
return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES);
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
|
||||
for (Class<? extends Annotation> type : types) {
|
||||
Annotation annotation = method.getAnnotation(type);
|
||||
if (annotation != null) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
|
||||
for (Result result : results) {
|
||||
List<ResultFlag> flags = new ArrayList<>();
|
||||
if (result.id()) {
|
||||
flags.add(ResultFlag.ID);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
|
||||
((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler());
|
||||
ResultMapping resultMapping = assistant.buildResultMapping(
|
||||
resultType,
|
||||
nullOrEmpty(result.property()),
|
||||
nullOrEmpty(result.column()),
|
||||
result.javaType() == void.class ? null : result.javaType(),
|
||||
result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
|
||||
hasNestedSelect(result) ? nestedSelectId(result) : null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
typeHandler,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
isLazy(result));
|
||||
resultMappings.add(resultMapping);
|
||||
}
|
||||
}
|
||||
|
||||
private String nestedSelectId(Result result) {
|
||||
String nestedSelect = result.one().select();
|
||||
if (nestedSelect.length() < 1) {
|
||||
nestedSelect = result.many().select();
|
||||
}
|
||||
if (!nestedSelect.contains(".")) {
|
||||
nestedSelect = type.getName() + "." + nestedSelect;
|
||||
}
|
||||
return nestedSelect;
|
||||
}
|
||||
|
||||
private boolean isLazy(Result result) {
|
||||
boolean isLazy = configuration.isLazyLoadingEnabled();
|
||||
if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
|
||||
isLazy = result.one().fetchType() == FetchType.LAZY;
|
||||
} else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
|
||||
isLazy = result.many().fetchType() == FetchType.LAZY;
|
||||
}
|
||||
return isLazy;
|
||||
}
|
||||
|
||||
private boolean hasNestedSelect(Result result) {
|
||||
if (result.one().select().length() > 0 && result.many().select().length() > 0) {
|
||||
throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
|
||||
}
|
||||
return result.one().select().length() > 0 || result.many().select().length() > 0;
|
||||
}
|
||||
|
||||
private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
|
||||
for (Arg arg : args) {
|
||||
List<ResultFlag> flags = new ArrayList<>();
|
||||
flags.add(ResultFlag.CONSTRUCTOR);
|
||||
if (arg.id()) {
|
||||
flags.add(ResultFlag.ID);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
|
||||
(arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());
|
||||
ResultMapping resultMapping = assistant.buildResultMapping(
|
||||
resultType,
|
||||
nullOrEmpty(arg.name()),
|
||||
nullOrEmpty(arg.column()),
|
||||
arg.javaType() == void.class ? null : arg.javaType(),
|
||||
arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
|
||||
nullOrEmpty(arg.select()),
|
||||
nullOrEmpty(arg.resultMap()),
|
||||
null,
|
||||
nullOrEmpty(arg.columnPrefix()),
|
||||
typeHandler,
|
||||
flags,
|
||||
null,
|
||||
null,
|
||||
false);
|
||||
resultMappings.add(resultMapping);
|
||||
}
|
||||
}
|
||||
|
||||
private String nullOrEmpty(String value) {
|
||||
return value == null || value.trim().length() == 0 ? null : value;
|
||||
}
|
||||
|
||||
private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
||||
String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
||||
Class<?> resultTypeClass = selectKeyAnnotation.resultType();
|
||||
StatementType statementType = selectKeyAnnotation.statementType();
|
||||
String keyProperty = selectKeyAnnotation.keyProperty();
|
||||
String keyColumn = selectKeyAnnotation.keyColumn();
|
||||
boolean executeBefore = selectKeyAnnotation.before();
|
||||
|
||||
// defaults
|
||||
boolean useCache = false;
|
||||
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
|
||||
Integer fetchSize = null;
|
||||
Integer timeout = null;
|
||||
boolean flushCache = false;
|
||||
String parameterMap = null;
|
||||
String resultMap = null;
|
||||
ResultSetType resultSetTypeEnum = null;
|
||||
|
||||
SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
|
||||
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
|
||||
|
||||
assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
|
||||
flushCache, useCache, false,
|
||||
keyGenerator, keyProperty, keyColumn, null, languageDriver, null);
|
||||
|
||||
id = assistant.applyCurrentNamespace(id, false);
|
||||
|
||||
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
|
||||
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
|
||||
configuration.addKeyGenerator(id, answer);
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory;
|
||||
import org.apache.ibatis.binding.BindingException;
|
||||
import org.apache.ibatis.binding.MapperRegistry;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 继承至MapperRegistry
|
||||
*
|
||||
* @author Caratacus hubin
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:13
|
||||
*/
|
||||
public class MybatisMapperRegistry extends MapperRegistry {
|
||||
|
||||
private final Map<Class<?>, MybatisMapperProxyFactory<?>> knownMappers = new HashMap<>();
|
||||
private final MybatisConfiguration config;
|
||||
|
||||
public MybatisMapperRegistry(MybatisConfiguration config) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
||||
// TODO 这里换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
|
||||
final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
|
||||
if (mapperProxyFactory == null) {
|
||||
throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
|
||||
}
|
||||
try {
|
||||
return mapperProxyFactory.newInstance(sqlSession);
|
||||
} catch (Exception e) {
|
||||
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean hasMapper(Class<T> type) {
|
||||
return knownMappers.containsKey(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addMapper(Class<T> type) {
|
||||
if (type.isInterface()) {
|
||||
if (hasMapper(type)) {
|
||||
// TODO 如果之前注入 直接返回
|
||||
return;
|
||||
// TODO 这里就不抛异常了
|
||||
// throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
|
||||
}
|
||||
boolean loadCompleted = false;
|
||||
try {
|
||||
// TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
|
||||
knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
|
||||
// It's important that the type is added before the parser is run
|
||||
// otherwise the binding may automatically be attempted by the
|
||||
// mapper parser. If the type is already known, it won't try.
|
||||
// TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
|
||||
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
|
||||
parser.parse();
|
||||
loadCompleted = true;
|
||||
} finally {
|
||||
if (!loadCompleted) {
|
||||
knownMappers.remove(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己的 knownMappers
|
||||
*/
|
||||
@Override
|
||||
public Collection<Class<?>> getMappers() {
|
||||
return Collections.unmodifiableCollection(knownMappers.keySet());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,681 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisPlusVersion;
|
||||
import com.baomidou.mybatisplus.core.MybatisXMLConfigBuilder;
|
||||
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Assert;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import lombok.Setter;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.cache.Cache;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.io.Resources;
|
||||
import org.apache.ibatis.io.VFS;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.reflection.factory.ObjectFactory;
|
||||
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.apache.ibatis.type.TypeHandlerRegistry;
|
||||
import org.mybatis.logging.Logger;
|
||||
import org.mybatis.logging.LoggerFactory;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.NestedIOException;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.springframework.util.Assert.notNull;
|
||||
import static org.springframework.util.Assert.state;
|
||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||
import static org.springframework.util.StringUtils.hasLength;
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
/**
|
||||
* 拷贝类 {@link SqlSessionFactoryBean} 修改方法 buildSqlSessionFactory() 加载自定义
|
||||
* <p> MybatisXmlConfigBuilder </p>
|
||||
* <p> 移除 sqlSessionFactoryBuilder 属性,强制使用 `new MybatisSqlSessionFactoryBuilder()` </p>
|
||||
* <p> 移除 environment 属性,强制使用 `MybatisSqlSessionFactoryBean.class.getSimpleName()` </p>
|
||||
*
|
||||
* @author hubin
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/29 18:13
|
||||
*/
|
||||
public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MybatisSqlSessionFactoryBean.class);
|
||||
|
||||
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
|
||||
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
// TODO 使用 MybatisConfiguration
|
||||
private MybatisConfiguration configuration;
|
||||
|
||||
private Resource[] mapperLocations;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private TransactionFactory transactionFactory;
|
||||
|
||||
private Properties configurationProperties;
|
||||
|
||||
private SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
private boolean failFast;
|
||||
|
||||
private Interceptor[] plugins;
|
||||
|
||||
private TypeHandler<?>[] typeHandlers;
|
||||
|
||||
private String typeHandlersPackage;
|
||||
|
||||
private Class<?>[] typeAliases;
|
||||
|
||||
private String typeAliasesPackage;
|
||||
|
||||
private Class<?> typeAliasesSuperType;
|
||||
|
||||
private LanguageDriver[] scriptingLanguageDrivers;
|
||||
|
||||
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
|
||||
|
||||
// issue #19. No default provider.
|
||||
private DatabaseIdProvider databaseIdProvider;
|
||||
|
||||
private Class<? extends VFS> vfs;
|
||||
|
||||
private Cache cache;
|
||||
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
private ObjectWrapperFactory objectWrapperFactory;
|
||||
|
||||
// TODO 自定义枚举包
|
||||
@Setter
|
||||
private String typeEnumsPackage;
|
||||
|
||||
// TODO 自定义全局配置
|
||||
@Setter
|
||||
private GlobalConfig globalConfig;
|
||||
|
||||
/**
|
||||
* Sets the ObjectFactory.
|
||||
*
|
||||
* @param objectFactory a custom ObjectFactory
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public void setObjectFactory(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ObjectWrapperFactory.
|
||||
*
|
||||
* @param objectWrapperFactory a specified ObjectWrapperFactory
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
|
||||
this.objectWrapperFactory = objectWrapperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DatabaseIdProvider
|
||||
*
|
||||
* @return a specified DatabaseIdProvider
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public DatabaseIdProvider getDatabaseIdProvider() {
|
||||
return databaseIdProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DatabaseIdProvider. As of version 1.2.2 this variable is not initialized by default.
|
||||
*
|
||||
* @param databaseIdProvider a DatabaseIdProvider
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
|
||||
this.databaseIdProvider = databaseIdProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the VFS.
|
||||
*
|
||||
* @return a specified VFS
|
||||
*/
|
||||
public Class<? extends VFS> getVfs() {
|
||||
return this.vfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the VFS.
|
||||
*
|
||||
* @param vfs a VFS
|
||||
*/
|
||||
public void setVfs(Class<? extends VFS> vfs) {
|
||||
this.vfs = vfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Cache.
|
||||
*
|
||||
* @return a specified Cache
|
||||
*/
|
||||
public Cache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Cache.
|
||||
*
|
||||
* @param cache a Cache
|
||||
*/
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mybatis plugin list.
|
||||
*
|
||||
* @param plugins list of plugins
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setPlugins(Interceptor... plugins) {
|
||||
this.plugins = plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages to search for type aliases.
|
||||
*
|
||||
* <p>
|
||||
* Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.model}.
|
||||
*
|
||||
* @param typeAliasesPackage package to scan for domain objects
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setTypeAliasesPackage(String typeAliasesPackage) {
|
||||
this.typeAliasesPackage = typeAliasesPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Super class which domain objects have to extend to have a type alias created. No effect if there is no package to
|
||||
* scan configured.
|
||||
*
|
||||
* @param typeAliasesSuperType super class for domain objects
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
|
||||
this.typeAliasesSuperType = typeAliasesSuperType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages to search for type handlers.
|
||||
*
|
||||
* <p>
|
||||
* Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.typehandler}.
|
||||
*
|
||||
* @param typeHandlersPackage package to scan for type handlers
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setTypeHandlersPackage(String typeHandlersPackage) {
|
||||
this.typeHandlersPackage = typeHandlersPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
|
||||
*
|
||||
* @param typeHandlers Type handler list
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setTypeHandlers(TypeHandler<?>... typeHandlers) {
|
||||
this.typeHandlers = typeHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of type aliases to register. They can be annotated with {@code Alias}
|
||||
*
|
||||
* @param typeAliases Type aliases list
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setTypeAliases(Class<?>... typeAliases) {
|
||||
this.typeAliases = typeAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, a final check is done on Configuration to assure that all mapped statements are fully loaded and there is
|
||||
* no one still pending to resolve includes. Defaults to false.
|
||||
*
|
||||
* @param failFast enable failFast
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setFailFast(boolean failFast) {
|
||||
this.failFast = failFast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
|
||||
* "WEB-INF/mybatis-configuration.xml".
|
||||
*
|
||||
* @param configLocation a location the MyBatis config file
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a customized MyBatis configuration.
|
||||
* TODO 这里的入参使用 MybatisConfiguration 而不是 Configuration
|
||||
*
|
||||
* @param configuration MyBatis configuration
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public void setConfiguration(MybatisConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public MybatisConfiguration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
|
||||
* at runtime.
|
||||
* <p>
|
||||
* This is an alternative to specifying "<sqlmapper>" entries in an MyBatis config file. This property being
|
||||
* based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
|
||||
* "classpath*:sqlmap/*-mapper.xml".
|
||||
*
|
||||
* @param mapperLocations location of MyBatis mapper files
|
||||
*/
|
||||
public void setMapperLocations(Resource... mapperLocations) {
|
||||
this.mapperLocations = mapperLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set optional properties to be passed into the SqlSession configuration, as alternative to a
|
||||
* {@code <properties>} tag in the configuration xml file. This will be used to resolve placeholders in the
|
||||
* config file.
|
||||
*
|
||||
* @param sqlSessionFactoryProperties optional properties for the SqlSessionFactory
|
||||
*/
|
||||
public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
|
||||
this.configurationProperties = sqlSessionFactoryProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} should
|
||||
* match the one used by the {@code SqlSessionFactory}: for example, you could specify the same JNDI DataSource for
|
||||
* both.
|
||||
* <p>
|
||||
* A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code accessing
|
||||
* this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
|
||||
* <p>
|
||||
* The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not a
|
||||
* {@code TransactionAwareDataSourceProxy}. Only data access code may work with
|
||||
* {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the underlying target
|
||||
* {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in, it will be
|
||||
* unwrapped to extract its target {@code DataSource}.
|
||||
*
|
||||
* @param dataSource a JDBC {@code DataSource}
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
if (dataSource instanceof TransactionAwareDataSourceProxy) {
|
||||
// If we got a TransactionAwareDataSourceProxy, we need to perform
|
||||
// transactions for its underlying target DataSource, else data
|
||||
// access code won't see properly exposed transactions (i.e.
|
||||
// transactions for the target DataSource).
|
||||
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
|
||||
} else {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
|
||||
* <p>
|
||||
* The default {@code SpringManagedTransactionFactory} should be appropriate for all cases:
|
||||
* be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
|
||||
* SqlSession operations will execute SQL statements non-transactionally.
|
||||
*
|
||||
* <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any
|
||||
* attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if
|
||||
* a transaction is active.
|
||||
*
|
||||
* @param transactionFactory the MyBatis TransactionFactory
|
||||
* @see SpringManagedTransactionFactory
|
||||
*/
|
||||
public void setTransactionFactory(TransactionFactory transactionFactory) {
|
||||
this.transactionFactory = transactionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scripting language drivers.
|
||||
*
|
||||
* @param scriptingLanguageDrivers scripting language drivers
|
||||
* @since 2.0.2
|
||||
*/
|
||||
public void setScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
|
||||
this.scriptingLanguageDrivers = scriptingLanguageDrivers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a default scripting language driver class.
|
||||
*
|
||||
* @param defaultScriptingLanguageDriver A default scripting language driver class
|
||||
* @since 2.0.2
|
||||
*/
|
||||
public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> defaultScriptingLanguageDriver) {
|
||||
this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
notNull(dataSource, "Property 'dataSource' is required");
|
||||
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
|
||||
"Property 'configuration' and 'configLocation' can not specified with together");
|
||||
|
||||
this.sqlSessionFactory = buildSqlSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@code SqlSessionFactory} instance.
|
||||
* <p>
|
||||
* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
|
||||
* {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a
|
||||
* {@link Configuration} instance directly(without config file).
|
||||
* </p>
|
||||
*
|
||||
* @return SqlSessionFactory
|
||||
* @throws IOException if loading the config file failed
|
||||
*/
|
||||
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
|
||||
|
||||
final com.baomidou.mybatisplus.core.MybatisConfiguration targetConfiguration;
|
||||
|
||||
// TODO 使用 MybatisXmlConfigBuilder 而不是 XMLConfigBuilder
|
||||
MybatisXMLConfigBuilder xmlConfigBuilder = null;
|
||||
if (this.configuration != null) {
|
||||
targetConfiguration = this.configuration;
|
||||
if (targetConfiguration.getVariables() == null) {
|
||||
targetConfiguration.setVariables(this.configurationProperties);
|
||||
} else if (this.configurationProperties != null) {
|
||||
targetConfiguration.getVariables().putAll(this.configurationProperties);
|
||||
}
|
||||
} else if (this.configLocation != null) {
|
||||
// TODO 使用 MybatisXMLConfigBuilder
|
||||
xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
|
||||
targetConfiguration = xmlConfigBuilder.getConfiguration();
|
||||
} else {
|
||||
LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
|
||||
// TODO 使用 MybatisConfiguration
|
||||
targetConfiguration = new MybatisConfiguration();
|
||||
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
|
||||
}
|
||||
|
||||
// TODO 无配置启动所必须的
|
||||
this.globalConfig = Optional.ofNullable(this.globalConfig).orElseGet(GlobalConfigUtils::defaults);
|
||||
this.globalConfig.setDbConfig(Optional.ofNullable(this.globalConfig.getDbConfig()).orElseGet(GlobalConfig.DbConfig::new));
|
||||
|
||||
// TODO 初始化 id-work 以及 打印骚东西
|
||||
targetConfiguration.setGlobalConfig(this.globalConfig);
|
||||
|
||||
// TODO 自定义枚举类扫描处理
|
||||
if (hasLength(this.typeEnumsPackage)) {
|
||||
Set<Class<?>> classes;
|
||||
if (typeEnumsPackage.contains(StringPool.STAR) && !typeEnumsPackage.contains(StringPool.COMMA)
|
||||
&& !typeEnumsPackage.contains(StringPool.SEMICOLON)) {
|
||||
classes = scanClasses(typeEnumsPackage, null);
|
||||
if (classes.isEmpty()) {
|
||||
LOGGER.warn(() -> "Can't find class in '[" + typeEnumsPackage + "]' package. Please check your configuration.");
|
||||
}
|
||||
} else {
|
||||
classes = new HashSet<>();
|
||||
String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
Assert.notNull(typeEnumsPackageArray, "not find typeEnumsPackage:" + typeEnumsPackage);
|
||||
Stream.of(typeEnumsPackageArray).forEach(typePackage -> {
|
||||
try {
|
||||
Set<Class<?>> scanTypePackage = scanClasses(typePackage, null);
|
||||
if (scanTypePackage.isEmpty()) {
|
||||
LOGGER.warn(() -> "Can't find class in '[" + typePackage + "]' package. Please check your configuration.");
|
||||
} else {
|
||||
classes.addAll(scanTypePackage);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MybatisPlusException("Cannot scan class in '[" + typePackage + "]' package", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 取得类型转换注册器
|
||||
TypeHandlerRegistry typeHandlerRegistry = targetConfiguration.getTypeHandlerRegistry();
|
||||
classes.stream()
|
||||
.filter(Class::isEnum)
|
||||
.filter(MybatisEnumTypeHandler::isMpEnums)
|
||||
.forEach(cls -> typeHandlerRegistry.register(cls, MybatisEnumTypeHandler.class));
|
||||
}
|
||||
|
||||
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
|
||||
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
|
||||
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
|
||||
|
||||
if (hasLength(this.typeAliasesPackage)) {
|
||||
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
|
||||
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
|
||||
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
|
||||
}
|
||||
|
||||
if (!isEmpty(this.typeAliases)) {
|
||||
Stream.of(this.typeAliases).forEach(typeAlias -> {
|
||||
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
|
||||
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
|
||||
});
|
||||
}
|
||||
|
||||
if (!isEmpty(this.plugins)) {
|
||||
Stream.of(this.plugins).forEach(plugin -> {
|
||||
targetConfiguration.addInterceptor(plugin);
|
||||
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
|
||||
});
|
||||
}
|
||||
|
||||
if (hasLength(this.typeHandlersPackage)) {
|
||||
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
|
||||
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
|
||||
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
|
||||
}
|
||||
|
||||
if (!isEmpty(this.typeHandlers)) {
|
||||
Stream.of(this.typeHandlers).forEach(typeHandler -> {
|
||||
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
|
||||
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
|
||||
});
|
||||
}
|
||||
|
||||
if (!isEmpty(this.scriptingLanguageDrivers)) {
|
||||
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
|
||||
targetConfiguration.getLanguageRegistry().register(languageDriver);
|
||||
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
|
||||
});
|
||||
}
|
||||
|
||||
Optional.ofNullable(this.defaultScriptingLanguageDriver).ifPresent(targetConfiguration::setDefaultScriptingLanguage);
|
||||
|
||||
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
|
||||
try {
|
||||
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
|
||||
} catch (SQLException e) {
|
||||
throw new NestedIOException("Failed getting a databaseId", e);
|
||||
}
|
||||
}
|
||||
|
||||
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
|
||||
|
||||
if (xmlConfigBuilder != null) {
|
||||
try {
|
||||
xmlConfigBuilder.parse();
|
||||
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
|
||||
} catch (Exception ex) {
|
||||
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
}
|
||||
}
|
||||
|
||||
targetConfiguration.setEnvironment(new Environment(MybatisSqlSessionFactoryBean.class.getSimpleName(),
|
||||
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
|
||||
this.dataSource));
|
||||
|
||||
if (this.mapperLocations != null) {
|
||||
if (this.mapperLocations.length == 0) {
|
||||
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
|
||||
} else {
|
||||
for (Resource mapperLocation : this.mapperLocations) {
|
||||
if (mapperLocation == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
|
||||
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
|
||||
xmlMapperBuilder.parse();
|
||||
} catch (Exception e) {
|
||||
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
}
|
||||
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
|
||||
}
|
||||
|
||||
final SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(targetConfiguration);
|
||||
|
||||
// TODO SqlRunner
|
||||
SqlHelper.FACTORY = sqlSessionFactory;
|
||||
|
||||
// TODO 打印骚东西 Banner
|
||||
if (globalConfig.isBanner()) {
|
||||
System.out.println(" | | o | / | | o \\ ");
|
||||
System.out.println(",-.-., .|---.,---.|--- . ,---.,---.| . .,---.| ,---.| ,---.,---|,-.-..,---. |");
|
||||
System.out.println("| | || || |,---|| |---`---.| || | |`---.| |---'| ,---|| || | ||| | |");
|
||||
System.out.println("` ' '`---|`---'`---^`---'` `---'|---'`---'`---'`---'| `---'`---'`---^`---'` ' '`` ' |");
|
||||
System.out.println(" `---' | \\ / ");
|
||||
System.out.println(" " + MybatisPlusVersion.getVersion() + " ");
|
||||
}
|
||||
|
||||
return sqlSessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public SqlSessionFactory getObject() throws Exception {
|
||||
if (this.sqlSessionFactory == null) {
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
return this.sqlSessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends SqlSessionFactory> getObjectType() {
|
||||
return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (failFast && event instanceof ContextRefreshedEvent) {
|
||||
// fail-fast -> check all statements are completed
|
||||
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
String[] packagePatternArray = tokenizeToStringArray(packagePatterns,
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
for (String packagePattern : packagePatternArray) {
|
||||
Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
|
||||
+ ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
|
||||
for (Resource resource : resources) {
|
||||
try {
|
||||
ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
|
||||
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
||||
if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
|
||||
classes.add(clazz);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.mybatis.core;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisXMLConfigBuilder;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.injector.SqlRunnerInjector;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.apache.ibatis.exceptions.ExceptionFactory;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 重写SqlSessionFactoryBuilder
|
||||
*
|
||||
* @author nieqiurong 2019/2/23.
|
||||
*/
|
||||
public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Override
|
||||
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
|
||||
try {
|
||||
//TODO 这里换成 MybatisXMLConfigBuilder 而不是 XMLConfigBuilder
|
||||
MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(reader, environment, properties);
|
||||
return build(parser.parse());
|
||||
} catch (Exception e) {
|
||||
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
// Intentionally ignore. Prefer previous error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Override
|
||||
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
|
||||
try {
|
||||
//TODO 这里换成 MybatisXMLConfigBuilder 而不是 XMLConfigBuilder
|
||||
MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties);
|
||||
return build(parser.parse());
|
||||
} catch (Exception e) {
|
||||
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// Intentionally ignore. Prefer previous error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 使用自己的逻辑,注入必须组件
|
||||
@Override
|
||||
public SqlSessionFactory build(Configuration config) {
|
||||
MybatisConfiguration configuration = (MybatisConfiguration) config;
|
||||
GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
|
||||
final IdentifierGenerator identifierGenerator;
|
||||
if (globalConfig.getIdentifierGenerator() == null) {
|
||||
if (null != globalConfig.getWorkerId() && null != globalConfig.getDatacenterId()) {
|
||||
identifierGenerator = new DefaultIdentifierGenerator(globalConfig.getWorkerId(), globalConfig.getDatacenterId());
|
||||
} else {
|
||||
identifierGenerator = new DefaultIdentifierGenerator();
|
||||
}
|
||||
globalConfig.setIdentifierGenerator(identifierGenerator);
|
||||
} else {
|
||||
identifierGenerator = globalConfig.getIdentifierGenerator();
|
||||
}
|
||||
//TODO 这里只是为了兼容下,并没多大重要,方法标记过时了.
|
||||
IdWorker.setIdentifierGenerator(identifierGenerator);
|
||||
|
||||
if (globalConfig.isEnableSqlRunner()) {
|
||||
new SqlRunnerInjector().inject(configuration);
|
||||
}
|
||||
|
||||
SqlSessionFactory sqlSessionFactory = super.build(configuration);
|
||||
|
||||
// 缓存 sqlSessionFactory
|
||||
globalConfig.setSqlSessionFactory(sqlSessionFactory);
|
||||
|
||||
return sqlSessionFactory;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Spring Boot Stater
|
||||
*/
|
||||
package me.zhengjie.mybatis;
|
||||
|
|
@ -1,7 +1,22 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.repository;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import me.zhengjie.base.BaseDao;
|
||||
import me.zhengjie.base.mybatis.BaseDao;
|
||||
import me.zhengjie.domain.ColumnInfo;
|
||||
import me.zhengjie.repository.jpa.ColumnInfoRepository;
|
||||
import me.zhengjie.repository.mp.ColumnInfoService;
|
||||
|
|
@ -10,13 +25,15 @@ import org.springframework.stereotype.Component;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JPA 与Mybatis 适配DAO
|
||||
*
|
||||
* @author liaojinlong
|
||||
* @since 2020/6/28 14:59
|
||||
*/
|
||||
@Component
|
||||
public class ColumnInfoDao extends BaseDao<ColumnInfoService, ColumnInfoRepository, ColumnInfo, Long> {
|
||||
|
||||
public ColumnInfoDao(ColumnInfoService baseService, ColumnInfoRepository jpaRepository ) {
|
||||
public ColumnInfoDao(ColumnInfoService baseService, ColumnInfoRepository jpaRepository) {
|
||||
super(baseService, jpaRepository);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.repository.mp;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,21 @@
|
|||
/*
|
||||
* Copyright 2019-2020
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package me.zhengjie.repository.mp;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import me.zhengjie.base.mybatis.ServiceImpl;
|
||||
import me.zhengjie.domain.ColumnInfo;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package me.zhengjie;
|
|||
import io.swagger.annotations.Api;
|
||||
import me.zhengjie.annotation.rest.AnonymousGetMapping;
|
||||
import me.zhengjie.utils.SpringContextHolder;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
|
|
@ -39,6 +40,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
@Api(hidden = true)
|
||||
@SpringBootApplication
|
||||
@EnableTransactionManagement
|
||||
@MapperScan("me.zhengjie.repository.mp")
|
||||
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
|
||||
public class AppRun {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package me.zhengjie.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@MapperScan("me.zhengjie.repository.mp")
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"groups": [
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties",
|
||||
"name": "mybatis-plus",
|
||||
"type": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties"
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties",
|
||||
"name": "mybatis-plus.configuration",
|
||||
"sourceMethod": "getConfiguration()",
|
||||
"type": "com.baomidou.mybatisplus.core.MybatisConfiguration"
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties",
|
||||
"name": "mybatis-plus.global-config",
|
||||
"sourceMethod": "getGlobalConfig()",
|
||||
"type": "com.baomidou.mybatisplus.core.config.GlobalConfig"
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig",
|
||||
"name": "mybatis-plus.global-config.db-config",
|
||||
"sourceMethod": "getDbConfig()",
|
||||
"type": "com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig",
|
||||
"name": "mybatis-plus.global-config.identifier-generator",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "请使用@Bean的方式注入至Spring容器."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig",
|
||||
"name": "mybatis-plus.global-config.meta-object-handler",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "3.0开始废除此属性,请使用@Bean的方式注入至Spring容器."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig",
|
||||
"name": "mybatis-plus.global-config.sql-injector",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "3.0开始废除此属性,请使用@Bean的方式注入至Spring容器."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig",
|
||||
"name": "mybatis-plus.global-config.db-config.key-generator",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "3.0开始废除此属性,请使用@Bean的方式注入至Spring容器."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties",
|
||||
"name": "mybatis-plus.default-scripting-language-driver",
|
||||
"defaultValue": "com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver",
|
||||
"type": "java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "如果修改了该值,你会至少失去几乎所有 mp 提供的功能."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.MybatisConfiguration",
|
||||
"name": "mybatis-plus.configuration.default-scripting-language",
|
||||
"defaultValue": "com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver",
|
||||
"type": "java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"reason": "设置无效."
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceType": "com.baomidou.mybatisplus.core.MybatisConfiguration",
|
||||
"name": "mybatis-plus.configuration.default-enum-type-handler",
|
||||
"defaultValue": "org.apache.ibatis.type.EnumTypeHandler",
|
||||
"description": "A default TypeHandler class for Enum.",
|
||||
"type": "java.lang.Class<? extends org.apache.ibatis.type.TypeHandler>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Auto Configure
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
me.zhengjie.mybatis.autoconfig.SafetyEncryptProcessor
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
me.zhengjie.mybatis.autoconfig.MybatisPlusAutoConfiguration
|
||||
Loading…
Reference in New Issue