, 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) {
diff --git a/eladmin-common/src/main/java/me/zhengjie/base/mybatis/ServiceImpl.java b/eladmin-common/src/main/java/me/zhengjie/base/mybatis/ServiceImpl.java
new file mode 100644
index 00000000..5398125e
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/base/mybatis/ServiceImpl.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * 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
+ *
+ * https://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.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, T> implements IService {
+
+ 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 currentModelClass() {
+ return (Class) 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 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 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 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 entityList, int batchSize) {
+ String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
+ return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
+ MapperMethod.ParamMap param = new MapperMethod.ParamMap<>();
+ param.put(Constants.ENTITY, entity);
+ sqlSession.update(sqlStatement, param);
+ });
+ }
+
+ @Override
+ public T getOne(Wrapper queryWrapper, boolean throwEx) {
+ if (throwEx) {
+ return baseMapper.selectOne(queryWrapper);
+ }
+ return SqlHelper.getObject(log, baseMapper.selectList(queryWrapper));
+ }
+
+ @Override
+ public Map getMap(Wrapper queryWrapper) {
+ return SqlHelper.getObject(log, baseMapper.selectMaps(queryWrapper));
+ }
+
+ @Override
+ public V getObj(Wrapper 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 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 泛型
+ * @return 操作结果
+ * @since 3.3.1
+ */
+ protected boolean executeBatch(Collection list, int batchSize, BiConsumer 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 泛型
+ * @return 操作结果
+ * @since 3.3.1
+ */
+ protected boolean executeBatch(Collection list, BiConsumer consumer) {
+ return executeBatch(list, DEFAULT_BATCH_SIZE, consumer);
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/AbstractSqlInjector.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/AbstractSqlInjector.java
new file mode 100644
index 00000000..65a0ea6f
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/AbstractSqlInjector.java
@@ -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 mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
+ if (!mapperRegistryCache.contains(className)) {
+ List 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);
+ }
+ }
+ }
+
+ /**
+ *
+ * 获取 注入的方法
+ *
+ *
+ * @param mapperClass 当前mapper
+ * @return 注入的方法集合
+ * @since 3.1.2 add mapperClass
+ */
+ public abstract List 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];
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/DefaultSqlInjector.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/DefaultSqlInjector.java
new file mode 100644
index 00000000..127d5ac2
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/DefaultSqlInjector.java
@@ -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 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());
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableFieldImp.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableFieldImp.java
new file mode 100644
index 00000000..98fff17f
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableFieldImp.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * 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
+ *
+ * https://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.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 = "";
+
+ /**
+ * 数据库字段值,
+ * 不需要配置该值的情况:
+ *
当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,
+ * (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase()
+ * 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,
+ * 数据库字段值.toUpperCase() == 实体属性名.toUpperCase()
+ */
+ @Override
+ public String value() {
+ return value;
+ }
+
+ /**
+ * 是否为数据库表字段
+ * 默认 true 存在,false 不存在
+ */
+ @Override
+ public boolean exist() {
+ return exist;
+ }
+
+ /**
+ * 字段 where 实体查询比较条件
+ * 默认 `=` 等值
+ */
+ @Override
+ public String condition() {
+ return condition;
+ }
+
+ /**
+ * 字段 update set 部分注入, 该注解优于 el 注解使用
+ *
+ * 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
+ * 输出 SQL 为:update 表 set 字段=字段+1 where ...
+ *
+ * 例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(column) values (#{columnProperty})
+ * NOT_EMPTY: insert into table_a(column) values (#{columnProperty})
+ *
+ * @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 column=#{columnProperty}
+ * NOT_EMPTY: update table_a set column=#{columnProperty}
+ *
+ * @since 3.1.2
+ */
+ @Override
+ public FieldStrategy updateStrategy() {
+ return updateStrategy;
+ }
+
+ /**
+ * 字段验证策略之 where: 表示该字段在拼接where条件时的策略
+ * IGNORED: 直接拼接 column=#{columnProperty}
+ * NOT_NULL: column=#{columnProperty}
+ * NOT_EMPTY: column=#{columnProperty}
+ *
+ * @since 3.1.2
+ */
+ @Override
+ public FieldStrategy whereStrategy() {
+ return whereStrategy;
+ }
+
+ /**
+ * 字段自动填充策略
+ */
+ @Override
+ public FieldFill fill() {
+ return fill;
+ }
+
+ /**
+ * 是否进行 select 查询
+ *
大字段可设置为 false 不加入 select 查询范围
+ */
+ @Override
+ public boolean select() {
+ return select;
+ }
+
+ /**
+ * 是否保持使用全局的 Format 的值
+ * 只生效于 既设置了全局的 Format 也设置了上面 {@link #value()} 的值
+ * 如果是 false , 全局的 Format 不生效
+ *
+ * @since 3.1.1
+ */
+ @Override
+ public boolean keepGlobalFormat() {
+ return keepGlobalFormat;
+ }
+
+ /**
+ * JDBC类型 (该默认值不代表会按照该值生效),
+ * 只生效与 mp 自动注入的 method,
+ * 建议配合 {@link TableName#autoResultMap()} 一起使用
+ *
+ * {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}
+ *
+ * @since 3.1.2
+ */
+ @Override
+ public JdbcType jdbcType() {
+ return jdbcType;
+ }
+
+ /**
+ * 类型处理器 (该默认值不代表会按照该值生效),
+ * 只生效与 mp 自动注入的 method,
+ * 建议配合 {@link TableName#autoResultMap()} 一起使用
+ *
+ * {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}
+ *
+ * @since 3.1.2
+ */
+ @Override
+ public Class extends TypeHandler> typeHandler() {
+ return typeHandler;
+ }
+
+ /**
+ * 指定小数点后保留的位数,
+ * 只生效与 mp 自动注入的 method,
+ * 建议配合 {@link TableName#autoResultMap()} 一起使用
+ *
+ * {@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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableIdImp.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableIdImp.java
new file mode 100644
index 00000000..79859ad6
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableIdImp.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * 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
+ *
+ * https://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.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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableNameImp.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableNameImp.java
new file mode 100644
index 00000000..6a52211e
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/annotation/impl/TableNameImp.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * 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
+ *
+ * https://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.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 的值
+ *
只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值
+ * 如果是 false , 全局的 tablePrefix 不生效
+ *
+ * @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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/ConfigurationCustomizer.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/ConfigurationCustomizer.java
new file mode 100644
index 00000000..1477dc4b
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/ConfigurationCustomizer.java
@@ -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);
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusAutoConfiguration.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusAutoConfiguration.java
new file mode 100644
index 00000000..07acb7e7
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusAutoConfiguration.java
@@ -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}.
+ *
+ * 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.
+ *
+ * copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}
+ *
+ * @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 configurationCustomizers;
+
+ private final List mybatisPlusPropertiesCustomizers;
+
+ private final ApplicationContext applicationContext;
+
+
+ public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,
+ ObjectProvider interceptorsProvider,
+ ObjectProvider typeHandlersProvider,
+ ObjectProvider languageDriversProvider,
+ ResourceLoader resourceLoader,
+ ObjectProvider databaseIdProvider,
+ ObjectProvider> configurationCustomizersProvider,
+ ObjectProvider> 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 泛型
+ */
+ private void getBeanThen(Class clazz, Consumer 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 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.");
+ }
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusProperties.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusProperties.java
new file mode 100644
index 00000000..4e0ec6dc
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusProperties.java
@@ -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+)
+ *
+ * 如果设置了这个,你会至少失去几乎所有 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];
+ }
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusPropertiesCustomizer.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusPropertiesCustomizer.java
new file mode 100644
index 00000000..f3d07ab4
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/MybatisPlusPropertiesCustomizer.java
@@ -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.
+ *
+ *
慎用
+ *
+ * @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);
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SafetyEncryptProcessor.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SafetyEncryptProcessor.java
new file mode 100644
index 00000000..d7e3cae8
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SafetyEncryptProcessor.java
@@ -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 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));
+ }
+ }
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SpringBootVFS.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SpringBootVFS.java
new file mode 100644
index 00000000..cd9de5d8
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/autoconfig/SpringBootVFS.java
@@ -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 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());
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/GlobalConfigUtils.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/GlobalConfigUtils.java
new file mode 100644
index 00000000..8d488098
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/GlobalConfigUtils.java
@@ -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
+ * FIXME 这可能是一个伪装成单例模式的原型模式,暂时不确定
+ */
+ 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 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 getMapperRegistryCache(Configuration configuration) {
+ return getGlobalConfig(configuration).getMapperRegistryCache();
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisConfiguration.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisConfiguration.java
new file mode 100644
index 00000000..1fbf971b
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisConfiguration.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * 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
+ *
+ * https://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.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
+ *
Caratacus 2016/9/25 replace mapperRegistry
+ * 适配
+ *
+ * @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 顺序:
+ * 1、加载XML中的SQL
+ * 2、加载sqlProvider中的SQL
+ * 3、xmlSql 与 sqlProvider不能包含相同的SQL
+ * 调整后的SQL优先级:xmlSql > sqlProvider > curdSql
+ */
+ @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 void addMapper(Class 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 getMapper(Class 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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperAnnotationBuilder.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperAnnotationBuilder.java
new file mode 100644
index 00000000..e07247a9
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperAnnotationBuilder.java
@@ -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.*;
+
+
+/**
+ * 继承
+ *
+ * 只重写了 {@link MapperAnnotationBuilder#parse} 和 #getReturnType
+ * 没有XML配置文件注入基础CRUD方法
+ *
+ *
+ * @author Caratacus
+ * @author liaojinlong
+ * @since 2020/6/29 18:13
+ */
+public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
+
+ private static final Set> SQL_ANNOTATION_TYPES = new HashSet<>();
+ private static final Set> 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 incompleteMethods = configuration.getIncompleteMethods();
+ synchronized (incompleteMethods) {
+ Iterator 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 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 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 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
+ 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> 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 resultMappings) {
+ for (Result result : results) {
+ List 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 resultMappings) {
+ for (Arg arg : args) {
+ List 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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperRegistry.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperRegistry.java
new file mode 100644
index 00000000..07f685cd
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisMapperRegistry.java
@@ -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, MybatisMapperProxyFactory>> knownMappers = new HashMap<>();
+ private final MybatisConfiguration config;
+
+ public MybatisMapperRegistry(MybatisConfiguration config) {
+ super(config);
+ this.config = config;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getMapper(Class type, SqlSession sqlSession) {
+ // TODO 这里换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
+ final MybatisMapperProxyFactory mapperProxyFactory = (MybatisMapperProxyFactory) 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 boolean hasMapper(Class type) {
+ return knownMappers.containsKey(type);
+ }
+
+ @Override
+ public void addMapper(Class 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> getMappers() {
+ return Collections.unmodifiableCollection(knownMappers.keySet());
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBean.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBean.java
new file mode 100644
index 00000000..d4415410
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBean.java
@@ -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() 加载自定义
+ * MybatisXmlConfigBuilder
+ * 移除 sqlSessionFactoryBuilder 属性,强制使用 `new MybatisSqlSessionFactoryBuilder()`
+ * 移除 environment 属性,强制使用 `MybatisSqlSessionFactoryBean.class.getSimpleName()`
+ *
+ * @author hubin
+ * @author liaojinlong
+ * @since 2020/6/29 18:13
+ */
+public class MybatisSqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
+
+ 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.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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}.
+ *
+ * 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}
+ *
+ * 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.
+ *
+ * It is strongly recommended to use the default {@code TransactionFactory}. 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.
+ *
+ * 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).
+ *
+ *
+ * @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> 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> 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> scanClasses(String packagePatterns, Class> assignableType) throws IOException {
+ Set> 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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBuilder.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBuilder.java
new file mode 100644
index 00000000..70e430b5
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/core/MybatisSqlSessionFactoryBuilder.java
@@ -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;
+ }
+}
diff --git a/eladmin-common/src/main/java/me/zhengjie/mybatis/package-info.java b/eladmin-common/src/main/java/me/zhengjie/mybatis/package-info.java
new file mode 100755
index 00000000..5a8d0649
--- /dev/null
+++ b/eladmin-common/src/main/java/me/zhengjie/mybatis/package-info.java
@@ -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;
diff --git a/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoDao.java b/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoDao.java
index 687aed9c..f2afb400 100644
--- a/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoDao.java
+++ b/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoDao.java
@@ -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 {
- public ColumnInfoDao(ColumnInfoService baseService, ColumnInfoRepository jpaRepository ) {
+ public ColumnInfoDao(ColumnInfoService baseService, ColumnInfoRepository jpaRepository) {
super(baseService, jpaRepository);
}
diff --git a/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoMapper.java b/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoMapper.java
index 3f489f9f..3682e91a 100644
--- a/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoMapper.java
+++ b/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoMapper.java
@@ -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;
diff --git a/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoService.java b/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoService.java
index 266b0a5b..b93623cc 100644
--- a/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoService.java
+++ b/eladmin-generator/src/main/java/me/zhengjie/repository/mp/ColumnInfoService.java
@@ -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;
diff --git a/eladmin-system/src/main/java/me/zhengjie/AppRun.java b/eladmin-system/src/main/java/me/zhengjie/AppRun.java
index ed244064..f39c425d 100644
--- a/eladmin-system/src/main/java/me/zhengjie/AppRun.java
+++ b/eladmin-system/src/main/java/me/zhengjie/AppRun.java
@@ -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 {
diff --git a/eladmin-system/src/main/java/me/zhengjie/config/MybatisPlusConfig.java b/eladmin-system/src/main/java/me/zhengjie/config/MybatisPlusConfig.java
deleted file mode 100644
index 225ed7f8..00000000
--- a/eladmin-system/src/main/java/me/zhengjie/config/MybatisPlusConfig.java
+++ /dev/null
@@ -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 {
-
-}
diff --git a/eladmin-system/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/eladmin-system/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100755
index 00000000..a920bccd
--- /dev/null
+++ b/eladmin-system/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -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>"
+ }
+ ]
+}
diff --git a/eladmin-system/src/main/resources/META-INF/spring.factories b/eladmin-system/src/main/resources/META-INF/spring.factories
new file mode 100755
index 00000000..8dfa5984
--- /dev/null
+++ b/eladmin-system/src/main/resources/META-INF/spring.factories
@@ -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
diff --git a/pom.xml b/pom.xml
index 54794b27..132b233c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -209,8 +209,8 @@
com.baomidou
- mybatis-plus-boot-starter
- 3.3.3.3-SNAPSHOT
+ mybatis-plus
+ 3.3.2