Commit 997656eb by fengshuonan

强力集成atomikos,完美解决多数据源事务

parent 4594a330
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
<dependency> <dependency>
<groupId>cn.stylefeng.roses</groupId> <groupId>cn.stylefeng.roses</groupId>
<artifactId>kernel-core</artifactId> <artifactId>kernel-core</artifactId>
<version>1.1.3</version> <version>1.2.0</version>
</dependency> </dependency>
<dependency> <dependency>
...@@ -154,6 +154,13 @@ ...@@ -154,6 +154,13 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- 多数据源事务管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<optional>true</optional>
</dependency>
<!--需要分布式session的话需要放开注释--> <!--需要分布式session的话需要放开注释-->
<!--<dependency>--> <!--<dependency>-->
<!--<groupId>org.springframework.session</groupId>--> <!--<groupId>org.springframework.session</groupId>-->
......
/* 这个sql用在测试多数据源的时候,可以不用运行 */
DROP DATABASE IF EXISTS guns_test_db;
CREATE DATABASE IF NOT EXISTS guns_test_db DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE guns_test_db;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`USER_ID` bigint(20) NOT NULL COMMENT '主键id',
`AVATAR` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '头像',
`ACCOUNT` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '账号',
`PASSWORD` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码',
`SALT` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'md5密码盐',
`NAME` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '名字',
`BIRTHDAY` datetime(0) DEFAULT NULL COMMENT '生日',
`SEX` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '性别(字典)',
`EMAIL` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '电子邮件',
`PHONE` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '电话',
`ROLE_ID` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '角色id(多个逗号隔开)',
`DEPT_ID` bigint(20) DEFAULT NULL COMMENT '部门id(多个逗号隔开)',
`STATUS` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '状态(字典)',
`CREATE_TIME` datetime(0) DEFAULT NULL COMMENT '创建时间',
`CREATE_USER` bigint(20) DEFAULT NULL COMMENT '创建人',
`UPDATE_TIME` datetime(0) DEFAULT NULL COMMENT '更新时间',
`UPDATE_USER` bigint(20) DEFAULT NULL COMMENT '更新人',
`VERSION` int(11) DEFAULT NULL COMMENT '乐观锁',
PRIMARY KEY (`USER_ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '管理员表' ROW_FORMAT = Dynamic;
\ No newline at end of file
...@@ -13,25 +13,21 @@ ...@@ -13,25 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package cn.stylefeng.guns.config.datasource; package cn.stylefeng.guns.config.datasource.multi;
import cn.stylefeng.roses.core.config.properties.DruidProperties; import cn.stylefeng.roses.core.config.properties.DruidProperties;
import cn.stylefeng.roses.core.config.properties.MutiDataSourceProperties;
import cn.stylefeng.roses.core.datascope.DataScopeInterceptor;
import cn.stylefeng.roses.core.mutidatasource.DynamicDataSource;
import cn.stylefeng.roses.core.mutidatasource.aop.MultiSourceExAop; import cn.stylefeng.roses.core.mutidatasource.aop.MultiSourceExAop;
import com.alibaba.druid.pool.DruidDataSource; import cn.stylefeng.roses.core.util.ToolUtil;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.context.annotation.Primary;
import java.sql.SQLException; import javax.sql.DataSource;
import java.util.HashMap;
/** /**
* 多数据源配置<br/> * 多数据源配置<br/>
...@@ -43,14 +39,14 @@ import java.util.HashMap; ...@@ -43,14 +39,14 @@ import java.util.HashMap;
*/ */
@Configuration @Configuration
@ConditionalOnProperty(prefix = "guns.muti-datasource", name = "open", havingValue = "true") @ConditionalOnProperty(prefix = "guns.muti-datasource", name = "open", havingValue = "true")
@EnableTransactionManagement(order = 2, proxyTargetClass = true) @MapperScan(basePackages = {"cn.stylefeng.guns.modular.*.mapper"}, sqlSessionTemplateRef = "gunsSqlSessionTemplate")
@MapperScan(basePackages = {"cn.stylefeng.guns.modular.*.mapper", "cn.stylefeng.guns.multi.mapper"})
public class MultiDataSourceConfig { public class MultiDataSourceConfig {
/** /**
* druid配置 * 默认主数据源配置
*/ */
@Bean @Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource") @ConfigurationProperties(prefix = "spring.datasource")
public DruidProperties druidProperties() { public DruidProperties druidProperties() {
return new DruidProperties(); return new DruidProperties();
...@@ -61,84 +57,49 @@ public class MultiDataSourceConfig { ...@@ -61,84 +57,49 @@ public class MultiDataSourceConfig {
*/ */
@Bean @Bean
@ConfigurationProperties(prefix = "guns.muti-datasource") @ConfigurationProperties(prefix = "guns.muti-datasource")
public MutiDataSourceProperties mutiDataSourceProperties() { public DruidProperties mutiDataSourceProperties() {
return new MutiDataSourceProperties(); return new DruidProperties();
} }
/** /**
* 多数据源切换的aop * 主数据源实例
*/ */
@Primary
@Bean @Bean
public MultiSourceExAop multiSourceExAop() { public DataSource dataSourcePrimary(@Qualifier("druidProperties") DruidProperties druidProperties) {
return new MultiSourceExAop(); if (ToolUtil.isOneEmpty(druidProperties, druidProperties.getDataSourceName())) {
} throw new IllegalArgumentException("初始化OptionalSqlSessionTemplate错误!请设置spring.datasource.data-source-name属性的值!");
/**
* guns的数据源
*/
private DruidDataSource dataSource(DruidProperties druidProperties) {
DruidDataSource dataSource = new DruidDataSource();
druidProperties.config(dataSource);
return dataSource;
} }
return createDataSource(druidProperties.getDataSourceName(), druidProperties);
/**
* 多数据源,第二个数据源
*/
private DruidDataSource bizDataSource(DruidProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) {
DruidDataSource dataSource = new DruidDataSource();
druidProperties.config(dataSource);
mutiDataSourceProperties.config(dataSource);
return dataSource;
} }
/** /**
* 多数据源连接池配置 * 第二个数据源实例
*/ */
@Bean @Bean
public DynamicDataSource mutiDataSource(DruidProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) { public DataSource dataSourceBusiness(@Qualifier("mutiDataSourceProperties") DruidProperties mutiDataSourceProperties) {
if (ToolUtil.isOneEmpty(mutiDataSourceProperties, mutiDataSourceProperties.getDataSourceName())) {
DruidDataSource dataSourceGuns = dataSource(druidProperties); throw new IllegalArgumentException("初始化OptionalSqlSessionTemplate错误!请设置spring.muti-datasource.data-source-name属性的值!");
DruidDataSource bizDataSource = bizDataSource(druidProperties, mutiDataSourceProperties);
try {
dataSourceGuns.init();
bizDataSource.init();
} catch (SQLException sql) {
sql.printStackTrace();
}
DynamicDataSource dynamicDataSource = new DynamicDataSource();
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(mutiDataSourceProperties.getDataSourceNames()[0], dataSourceGuns);
hashMap.put(mutiDataSourceProperties.getDataSourceNames()[1], bizDataSource);
dynamicDataSource.setTargetDataSources(hashMap);
dynamicDataSource.setDefaultTargetDataSource(dataSourceGuns);
return dynamicDataSource;
} }
return createDataSource(mutiDataSourceProperties.getDataSourceName(), mutiDataSourceProperties);
/**
* mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
} }
/** /**
* 数据范围mybatis插件 * 多数据源切换的aop
*/ */
@Bean @Bean
public DataScopeInterceptor dataScopeInterceptor() { public MultiSourceExAop multiSourceExAop() {
return new DataScopeInterceptor(); return new MultiSourceExAop();
} }
/** /**
* 乐观锁mybatis插件 * 数据源创建模板
*/ */
@Bean private static DataSource createDataSource(String dataSourceName, DruidProperties druidProperties) {
public OptimisticLockerInterceptor optimisticLockerInterceptor() { AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
return new OptimisticLockerInterceptor(); atomikosDataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
atomikosDataSourceBean.setUniqueResourceName(dataSourceName);
atomikosDataSourceBean.setXaProperties(druidProperties.createProperties());
return atomikosDataSourceBean;
} }
} }
/**
* Copyright 2018-2020 stylefeng & fengshuonan (sn93@qq.com)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.config.datasource.multi;
import cn.stylefeng.roses.core.config.properties.DruidProperties;
import cn.stylefeng.roses.core.datascope.DataScopeInterceptor;
import cn.stylefeng.roses.core.mutidatasource.mybatis.OptionalSqlSessionTemplate;
import cn.stylefeng.roses.core.util.ToolUtil;
import cn.stylefeng.roses.kernel.model.exception.ServiceException;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置<br/>
* <p>
* 注:由于引入多数据源,所以让spring事务的aop要在多数据源切换aop的后面
*
* @author stylefeng
* @Date 2017/5/20 21:58
*/
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "guns.muti-datasource", name = "open", havingValue = "true")
public class MultiSqlSessionFactoryConfig {
/**
* xml文件的位置,改包了注意修改哦
*/
public static final String MAPPING_XML_CLASSPATH = "classpath:cn/stylefeng/guns/modular/**/mapping/*.xml";
/**
* 主sqlSessionFactory
*/
@Primary
@Bean
public SqlSessionFactory sqlSessionFactoryPrimary(@Qualifier("dataSourcePrimary") DataSource dataSource) {
return createSqlSessionFactory(dataSource);
}
/**
* 第二个sqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactoryBusiness(@Qualifier("dataSourceBusiness") DataSource dataSource) {
return createSqlSessionFactory(dataSource);
}
/**
* 多数据源sqlSessionTemplate切换模板
*/
@Bean(name = "gunsSqlSessionTemplate")
public OptionalSqlSessionTemplate gunsSqlSessionTemplate(@Qualifier("sqlSessionFactoryPrimary") SqlSessionFactory sqlSessionFactoryPrimary,
@Qualifier("sqlSessionFactoryBusiness") SqlSessionFactory sqlSessionFactoryBusiness,
@Qualifier("druidProperties") DruidProperties druidProperties,
@Qualifier("mutiDataSourceProperties") DruidProperties mutiDataSourceProperties) {
if (ToolUtil.isOneEmpty(druidProperties, druidProperties.getDataSourceName())) {
throw new IllegalArgumentException("初始化OptionalSqlSessionTemplate错误!请设置spring.datasource.data-source-name属性的值!");
}
if (ToolUtil.isOneEmpty(mutiDataSourceProperties, mutiDataSourceProperties.getDataSourceName())) {
throw new IllegalArgumentException("初始化OptionalSqlSessionTemplate错误!请设置spring.muti-datasource.data-source-name属性的值!");
}
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
sqlSessionFactoryMap.put(druidProperties.getDataSourceName(), sqlSessionFactoryPrimary);
sqlSessionFactoryMap.put(mutiDataSourceProperties.getDataSourceName(), sqlSessionFactoryBusiness);
return new OptionalSqlSessionTemplate(sqlSessionFactoryPrimary, sqlSessionFactoryMap);
}
/**
* mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 数据范围mybatis插件
*/
@Bean
public DataScopeInterceptor dataScopeInterceptor() {
return new DataScopeInterceptor();
}
/**
* 乐观锁mybatis插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 创建数据源
*/
private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) {
try {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPING_XML_CLASSPATH));
bean.setVfs(SpringBootVFS.class);
bean.setPlugins(new Interceptor[]{
paginationInterceptor(),
dataScopeInterceptor(),
optimisticLockerInterceptor()
});
return bean.getObject();
} catch (Exception e) {
log.error("初始化SqlSessionFactory错误!", e);
throw new ServiceException(500, "初始化SqlSessionFactory错误!");
}
}
}
spring: spring:
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT url: jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT
username: root username: root
password: root password: root
filters: wall,mergeStat filters: wall,mergeStat
data-source-name: gunsdb
#多数据源情况的配置 #多数据源情况的配置
guns: guns:
muti-datasource: muti-datasource:
open: false open: false
url: jdbc:mysql://127.0.0.1:3306/guns_test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/guns_test_db?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT
username: root username: root
password: root password: root
dataSourceNames: data-source-name: otherdb
- dataSourceGuns
- dataSourceBiz
\ No newline at end of file
spring: spring:
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT url: jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT
username: root username: root
password: root password: root
filters: wall,mergeStat filters: wall,mergeStat
data-source-name: gunsdb
#多数据源情况的配置 #多数据源情况的配置
guns: guns:
muti-datasource: muti-datasource:
open: false open: false
url: jdbc:mysql://127.0.0.1:3306/guns_test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/guns_test_db?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT
username: root username: root
password: root password: root
dataSourceNames: data-source-name: otherdb
- dataSourceGuns
- dataSourceBiz
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment