| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- package jnpf.database.config;
- import com.alibaba.druid.pool.DruidDataSource;
- import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
- import com.baomidou.dynamic.datasource.annotation.DS;
- import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
- import com.baomidou.dynamic.datasource.enums.DdConstants;
- import com.baomidou.dynamic.datasource.processor.DsProcessor;
- import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
- import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
- import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
- import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
- import com.baomidou.mybatisplus.core.MybatisConfiguration;
- import com.baomidou.mybatisplus.core.config.GlobalConfig;
- import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
- import com.baomidou.mybatisplus.core.injector.ISqlInjector;
- import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
- import com.baomidou.mybatisplus.core.toolkit.StringPool;
- import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
- import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
- import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
- import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
- import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
- import com.github.pagehelper.PageInterceptor;
- import jnpf.base.entity.SuperBaseEntity;
- import jnpf.config.ApplicationStartErrorCheck;
- import jnpf.config.ConfigValueUtil;
- import jnpf.constant.GlobalConst;
- import jnpf.database.model.entity.DbLinkEntity;
- import jnpf.database.plugins.*;
- import jnpf.database.source.DbBase;
- import jnpf.database.source.impl.DbOracle;
- import jnpf.database.util.ConnUtil;
- import jnpf.database.util.DataSourceUtil;
- import jnpf.database.util.DbTypeUtil;
- import jnpf.database.util.DynamicDataSourceUtil;
- import jnpf.exception.DataException;
- import jnpf.util.ClassUtil;
- import jnpf.util.TenantHolder;
- import lombok.extern.slf4j.Slf4j;
- import net.sf.jsqlparser.expression.Expression;
- import net.sf.jsqlparser.expression.LongValue;
- import net.sf.jsqlparser.expression.NullValue;
- import net.sf.jsqlparser.expression.StringValue;
- import org.apache.ibatis.builder.MapperBuilderAssistant;
- import org.apache.ibatis.logging.slf4j.Slf4jImpl;
- import org.apache.ibatis.mapping.MappedStatement;
- import org.apache.ibatis.plugin.Interceptor;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.apache.ibatis.type.JdbcType;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.aop.Advisor;
- import org.springframework.aop.Pointcut;
- import org.springframework.aop.aspectj.AspectJExpressionPointcut;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
- import org.springframework.context.annotation.*;
- import org.springframework.core.io.Resource;
- import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
- import org.springframework.core.io.support.ResourcePatternResolver;
- import javax.sql.DataSource;
- import java.io.IOException;
- import java.lang.reflect.Modifier;
- import java.net.URISyntaxException;
- import java.sql.SQLException;
- import java.util.*;
- /**
- * MybatisPlus配置类
- *
- * @author JNPF开发平台组
- * @version V3.1.0
- * @copyright 引迈信息技术有限公司
- * @date 2021/3/16 8:53
- */
- @Slf4j
- @Configuration
- @ComponentScan("jnpf")
- @DependsOn({"tenantDataSourceUtil", "threadPoolExecutorUtil"})
- @MapperScan(basePackages = {"jnpf.*.mapper", "jnpf.mapper", "com.xxl.job.admin.dao"})
- public class MybatisPlusConfig {
- /**
- * 对接数据库的实体层
- */
- static final String ALIASES_PACKAGE = "jnpf.*.entity;com.xxl.job.admin.core.model";
- @Autowired
- private DataSourceUtil dataSourceUtil;
- @Autowired
- private ConfigValueUtil configValueUtil;
- @Primary
- @Bean(name = "dataSourceSystem")
- public DataSource dataSourceOne(DynamicDataSourceProperties properties, List<DynamicDataSourceProvider> providers) throws SQLException, IOException, URISyntaxException {
- DataSource dataSource = dynamicDataSource(properties, providers);
- initDynamicDataSource(dataSource, properties);
- return dataSource;
- }
- @Bean(name = "sqlSessionFactorySystem")
- public SqlSessionFactory sqlSessionFactoryOne(@Qualifier("dataSourceSystem") DataSource dataSource, @Autowired(required = false) ISqlInjector sqlInjector) throws Exception {
- return createSqlSessionFactory(dataSource, sqlInjector);
- }
- /**
- * 服务中查询其他服务的表数据, 未引用Mapper无法初始化MybatisPlus的TableInfo对象, 无法判断逻辑删除情况, 初始化MybatisPlus所有Entity对象
- * 微服务的情况才进行扫描
- * @param sqlSessionFactory
- * @return
- */
- @Bean
- @ConditionalOnClass(name = "org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient")
- public Object scanAllEntity(SqlSessionFactory sqlSessionFactory){
- ApplicationStartErrorCheck.getApplicationInitThreadPool().execute(()->{
- Set<Class<?>> classes = ClassUtil.scanCandidateComponents("jnpf", c->
- !Modifier.isAbstract(c.getModifiers()) && SuperBaseEntity.SuperTBaseEntity.class.isAssignableFrom(c)
- );
- for (Class<?> aClass : classes) {
- MapperBuilderAssistant builderAssistant = new MapperBuilderAssistant(sqlSessionFactory.getConfiguration(), "resource");
- builderAssistant.setCurrentNamespace(aClass.getName());
- TableInfoHelper.initTableInfo(builderAssistant, aClass);
- }
- });
- return null;
- }
- public MybatisPlusInterceptor mybatisPlusInterceptor(){
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- try{
- //判断是否多租户
- if (configValueUtil.isMultiTenancy()) {
- interceptor.addInnerInterceptor(myTenantLineInnerInterceptor());
- interceptor.addInnerInterceptor(mySchemaInnerInterceptor());
- }
- //开启逻辑删除插件功能
- if(configValueUtil.isEnableLogicDelete()) {
- interceptor.addInnerInterceptor(myLogicDeleteInnerInterceptor());
- }
- // 新版本分页必须指定数据库,否则分页不生效
- // 不指定会动态生效 多数据源不能指定数据库类型
- interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
- //乐观锁
- interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
- }catch (Exception e){
- e.printStackTrace();
- }
- return interceptor;
- }
- @Bean("myLogicDeleteInnerInterceptor")
- @ConditionalOnProperty(prefix = "config", name = "EnableLogicDelete", havingValue = "true")
- public MyLogicDeleteInnerInterceptor myLogicDeleteInnerInterceptor(){
- MyLogicDeleteInnerInterceptor myLogicDeleteInnerInterceptor = new MyLogicDeleteInnerInterceptor();
- myLogicDeleteInnerInterceptor.setLogicDeleteHandler(new LogicDeleteHandler() {
- @Override
- public Expression getNotDeletedValue() {
- Object defValue = GlobalConst.LOGIC_NO_DELETE_VALUE;
- if(defValue == null){
- return new NullValue();
- } else{
- return new LongValue(defValue.toString());
- }
- }
- @Override
- public String getLogicDeleteColumn() {
- return configValueUtil.getLogicDeleteColumn();
- }
- });
- return myLogicDeleteInnerInterceptor;
- }
- @Bean("myTenantLineInnerInterceptor")
- @ConditionalOnProperty(prefix = "config", name = "MultiTenancy", havingValue = "true")
- public TenantLineInnerInterceptor myTenantLineInnerInterceptor(){
- TenantLineInnerInterceptor tenantLineInnerInterceptor = new MyTenantLineInnerInterceptor();
- tenantLineInnerInterceptor.setTenantLineHandler(new TenantLineHandler() {
- @Override
- public Expression getTenantId() {
- return new StringValue(TenantHolder.getDatasourceName());
- }
- @Override
- public String getTenantIdColumn() {
- return configValueUtil.getMultiTenantColumn();
- }
- @Override
- public boolean ignoreTable(String tableName) {
- return configValueUtil.getMultiTenantIgnoreTable().contains(tableName.toLowerCase());
- }
- });
- return tenantLineInnerInterceptor;
- }
- @Bean("mySchemaInnerInterceptor")
- @ConditionalOnProperty(prefix = "config", name = "MultiTenancy", havingValue = "true")
- public DynamicTableNameInnerInterceptor mySchemaInnerInterceptor() throws Exception {
- DbLinkEntity dbLinkEntity = dataSourceUtil.init();
- DbBase dbBase = DbTypeUtil.getDb(dbLinkEntity);
- DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new MySchemaInnerInterceptor();
- HashMap<String, TableNameHandler> map = new HashMap<>(150) ;
- // null空库保护
- List<String> tableNames = new ArrayList<>() ;
- // JdbcUtil.queryCustomMods(SqlComEnum.TABLES.getPrepSqlDto(dbLinkEntity, null), DbTableFieldModel.class)
- // .forEach(dbTableModel-> tableNames.add(dbTableModel.getTable().toLowerCase()));
- //将当前连接库的所有表保存, 在列表中的表才进行切库, 所有表名转小写, 后续比对转小写
- DbBase.dynamicAllTableName = tableNames;
- dynamicTableNameInnerInterceptor.setTableNameHandler(dbBase.getDynamicTableNameHandler());
- return dynamicTableNameInnerInterceptor;
- }
- @Bean("myTenantMasterSlaveInterceptor")
- @ConditionalOnProperty(prefix = "config", name = "MultiTenancy", havingValue = "true")
- public MyTenantMasterSlaveAutoRoutingPlugin myTenantMasterSlaveAutoRoutingPlugin(DataSource dataSource) {
- return new MyTenantMasterSlaveAutoRoutingPlugin(dataSource);
- }
- public MyDefaultMasterSlaveAutoRoutingPlugin myDefaultMasterSlaveAutoRoutingPlugin(DataSource dataSource) {
- return new MyDefaultMasterSlaveAutoRoutingPlugin(dataSource);
- }
- protected DataSource dynamicDataSource(DynamicDataSourceProperties properties, List<DynamicDataSourceProvider> providers) {
- // 动态路由数据源(关键)
- DynamicRoutingDataSource dataSource = new MyDynamicRoutingDataSource(providers);
- dataSource.setPrimary(properties.getPrimary());
- dataSource.setStrict(properties.getStrict());
- dataSource.setStrategy(properties.getStrategy());
- dataSource.setP6spy(properties.getP6spy());
- dataSource.setSeata(properties.getSeata());
- //创建失败不等待
- // properties.getDruid().setBreakAfterAcquireFailure(false);
- // properties.getDruid().setMaxWait(1000);
- return dataSource;
- }
- private void initDynamicDataSource(@Qualifier("dataSourceSystem") DataSource dataSource1, DynamicDataSourceProperties properties) throws DataException, SQLException, IOException, URISyntaxException {
- DynamicRoutingDataSource dataSource = (DynamicRoutingDataSource) dataSource1;
- //若未配置多数据源, 从主配置复制数据库配置填充多数据源
- boolean isPresentPrimary = properties.getDatasource().entrySet().stream().anyMatch(ds->
- ds.getKey().equals(properties.getPrimary()) || ds.getKey().startsWith(properties.getPrimary()+"_") || properties.getPrimary().equals(ds.getValue().getPoolName())
- );
- DynamicDataSourceUtil.dynamicDataSourceProperties = properties;
- if(!isPresentPrimary){
- // null多租户空库保护
- String url = ConnUtil.getUrl(dataSourceUtil, configValueUtil.isMultiTenancy() ? null : dataSourceUtil.getDbName());
- DataSourceProperty dataSourceProperty = DynamicDataSourceUtil.createDataSourceProperty(dataSourceUtil, url);
- dataSourceProperty.getDruid().setBreakAfterAcquireFailure(false);
- dataSourceProperty.setLazy(false);
- properties.getDatasource().put(properties.getPrimary(), dataSourceProperty);
- }
- }
- @Bean
- public Advisor myDynamicDatasourceGeneratorAdvisor(DsProcessor dsProcessor) {
- DynamicGeneratorInterceptor interceptor = new DynamicGeneratorInterceptor(true, dsProcessor);
- return new DynamicDataSourceAnnotationAdvisor(interceptor, DS.class){
- private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
- {
- pointcut.setExpression("within(jnpf.database.plugins.DynamicSourceGeneratorInterface+) && @target(com.baomidou.dynamic.datasource.annotation.DS)");
- }
- @Override
- public Pointcut getPointcut() {
- return pointcut;
- }
- };
- }
- protected DataSource druidDataSource() throws Exception{
- DbBase dbBase = DbTypeUtil.getDb(dataSourceUtil);
- String userName = dataSourceUtil.getUserName();
- String password = dataSourceUtil.getPassword();
- String driver = dbBase.getDriver();
- String url = "";
- if (configValueUtil.isMultiTenancy()) {
- url = ConnUtil.getUrl(dataSourceUtil, null);
- }else {
- url = ConnUtil.getUrl(dataSourceUtil);
- }
- DruidDataSource dataSource = new DruidDataSource();
- if(dbBase.getClass() == DbOracle.class){
- // Oracle特殊创建数据源方式
- // String logonUer = "Default";
- String logonUer = "SYSDBA";
- // String logonUer = "SYSOPER";
- Properties properties = DbOracle.setConnProp(logonUer, userName, password);
- dataSource.setConnectProperties(properties);
- }else {
- dataSource.setUsername(userName);
- dataSource.setPassword(password);
- }
- dataSource.setUrl(url);
- dataSource.setDriverClassName(driver);
- return dataSource;
- }
- public Resource[] resolveMapperLocations() {
- ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
- List<String> mapperLocations = new ArrayList<>();
- mapperLocations.add("classpath*:mapper/*.xml");
- mapperLocations.add("classpath*:mapper/*/*.xml");
- mapperLocations.add("classpath*:mapper/*/*/*.xml");
- mapperLocations.add("classpath*:mybatis-mapper/*.xml");
- List<Resource> resources = new ArrayList<Resource>();
- for (String mapperLocation : mapperLocations) {
- try {
- Resource[] mappers = resourceResolver.getResources(mapperLocation);
- resources.addAll(Arrays.asList(mappers));
- } catch (IOException e) {
- // ignore
- }
- }
- return resources.toArray(new Resource[0]);
- }
- public SqlSessionFactory createSqlSessionFactory(DataSource dataSource, ISqlInjector sqlInjector) throws Exception {
- MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
- bean.setDataSource(dataSource);
- //全局配置
- GlobalConfig globalConfig = new GlobalConfig();
- //配置填充器
- globalConfig.setMetaObjectHandler(new MybatisPlusMetaObjectHandler());
- bean.setGlobalConfig(globalConfig);
- if(configValueUtil.isEnableLogicDelete()) {
- globalConfig.setDbConfig(new GlobalConfig.DbConfig());
- globalConfig.getDbConfig().setLogicDeleteField("deleteMark");
- globalConfig.getDbConfig().setLogicDeleteValue(GlobalConst.LOGIC_DELETE_VALUE.toString());
- globalConfig.getDbConfig().setLogicNotDeleteValue(String.valueOf(GlobalConst.LOGIC_NO_DELETE_VALUE));
- }
- sqlInjector = new MyDefaultSqlInjector(sqlInjector, configValueUtil);
- globalConfig.setSqlInjector(sqlInjector);
- List<Interceptor> mybatisPlugins = new ArrayList<>();
- mybatisPlugins.add(new ResultSetInterceptor());
- mybatisPlugins.add(new MyDynamicDataSourceAutoRollbackInterceptor());
- mybatisPlugins.add(pageHelper());
- if(configValueUtil.isMultiTenancy()) {
- mybatisPlugins.add(myTenantMasterSlaveAutoRoutingPlugin(dataSource));
- }
- // 配置从库添加读写分离插件
- if(DynamicDataSourceUtil.dynamicDataSourceProperties.getDatasource().keySet().stream().anyMatch(k -> k.startsWith(DdConstants.SLAVE))){
- mybatisPlugins.add(myDefaultMasterSlaveAutoRoutingPlugin(dataSource));
- }
- bean.setVfs(SpringBootVFS.class);
- bean.setTypeAliasesPackage(ALIASES_PACKAGE);
- bean.setMapperLocations(resolveMapperLocations());
- bean.setConfiguration(configuration(dataSource));
- bean.setPlugins(mybatisPlugins.toArray(new Interceptor[mybatisPlugins.size()]));
- return bean.getObject();
- }
- public PageInterceptor pageHelper() {
- PageInterceptor pageHelper = new PageInterceptor();
- // 配置PageHelper参数
- Properties properties = new Properties();
- properties.setProperty("dialectAlias", "kingbase8=com.github.pagehelper.dialect.helper.MySqlDialect");
- properties.setProperty("autoRuntimeDialect", "true");
- properties.setProperty("offsetAsPageNum", "false");
- properties.setProperty("rowBoundsWithCount", "false");
- properties.setProperty("pageSizeZero", "true");
- properties.setProperty("reasonable", "false");
- properties.setProperty("supportMethodsArguments", "false");
- properties.setProperty("returnPageInfo", "none");
- pageHelper.setProperties(properties);
- return pageHelper;
- }
- public MybatisConfiguration configuration(DataSource dataSource){
- MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(){
- @Override
- public void addMappedStatement(MappedStatement ms) {
- // 避免Mybatis多线程初始化问题
- synchronized (ALIASES_PACKAGE) {
- super.addMappedStatement(ms);
- }
- }
- };
- mybatisConfiguration.setMapUnderscoreToCamelCase(false);
- mybatisConfiguration.setCacheEnabled(false);
- mybatisConfiguration.setCallSettersOnNulls(true);
- mybatisConfiguration.addInterceptor(mybatisPlusInterceptor());
- mybatisConfiguration.setLogImpl(Slf4jImpl.class);
- mybatisConfiguration.setJdbcTypeForNull(JdbcType.NULL);
- return mybatisConfiguration;
- }
- @Bean
- public IKeyGenerator keyGenerator() {
- return new H2KeyGenerator();
- }
- /**
- * 数据权限插件
- *
- * @return DataScopeInterceptor
- */
- // @Bean
- // @ConditionalOnMissingBean
- // public DataScopeInterceptor dataScopeInterceptor(DataSource dataSource) {
- // return new DataScopeInterceptor(dataSource);
- // }
- }
|