Spring 4+atomikos+JdbcTemplate+DynamicDataSource 分散式事務資料來源動態切換
一:依賴的jar包 Maven配置
二:配置
dataSource.properties
transactions.properties
三:AOP 註解方式資料來源動態切換
四:資料來源動態切換例項
五:分散式事務下的資料來源動態切換
@Transactional 透過資料庫的connection來建立事務的,為了保證資料來源能順利切換,要保證@DataSource優先於@Transactional執行。 實現辦法 在DataSourceAspect 切面上增加註解@Order(1). 此處需要了解切面的層次和執行順序等相關知識。 @Transactionl,@DataSource 在service層的同一個方法上。
點選(此處)摺疊或開啟
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>transactions</artifactId>
-
<version>4.0.4</version>
-
</dependency>
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>transactions-api</artifactId>
-
<version>4.0.4</version>
-
</dependency>
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>atomikos-util</artifactId>
-
<version>4.0.4</version>
-
</dependency>
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>transactions-jdbc-deprecated</artifactId>
-
<version>3.8.0</version>
-
</dependency>
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>transactions-jta</artifactId>
-
<version>4.0.4</version>
-
</dependency>
-
<dependency>
-
<groupId>com.atomikos</groupId>
-
<artifactId>transactions-jdbc</artifactId>
-
<version>4.0.4</version>
-
</dependency>
-
-
<dependency>
-
<groupId>cglib</groupId>
-
<artifactId>cglib-nodep</artifactId>
-
<version>3.2.5</version>
-
</dependency>
-
-
<dependency>
-
<groupId>javax.transaction</groupId>
-
<artifactId>jta</artifactId>
-
<version>1.1</version>
- </dependency>
dataSource.properties
點選(此處)摺疊或開啟
-
workDesk.jdbc.driverclass=com.mysql.jdbc.Driver
-
workDesk.jdbc.url=jdbc:mysql://10.243.3.18:3306/system?userUnicode=true&characterEncoding=UTF-8
-
workDesk.jdbc.username=root
-
workDesk.jdbc.password=$Fortune2015
-
workDesk.jdbc.poolsize.max=3
-
workDesk.jdbc.poolsize.min=3
-
workDesk.jdbc.poolsize.initial=2
-
workDesk.jdbc.idletime.max=25000
-
workDesk.jdbc.idleConnectionTestPeriod=18000
-
-
#-------workDesk jdbc--------
-
workDesk.read.jdbc.driverclass=com.mysql.jdbc.Driver
-
workDesk.read.jdbc.url=jdbc:mysql://112.74.53.213:3306/gmc?userUnicode=true&characterEncoding=UTF-8
-
workDesk.read.jdbc.username=root
-
workDesk.read.jdbc.password=Wanmide@123
-
workDesk.read.jdbc.poolsize.max=3
-
workDesk.read.jdbc.poolsize.min=3
-
workDesk.read.jdbc.poolsize.initial=2
-
workDesk.read.jdbc.idletime.max=25000
-
workDesk.read.jdbc.idleConnectionTestPeriod=18000
-
- jdbc.xaDataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
點選(此處)摺疊或開啟
-
# SAMPLE PROPERTIES FILE FOR THE TRANSACTION SERVICE
-
# THIS FILE ILLUSTRATES THE DIFFERENT SETTINGS FOR THE TRANSACTION MANAGER
-
# UNCOMMENT THE ASSIGNMENTS TO OVERRIDE DEFAULT VALUES;
-
-
# Required: factory implementation class of the transaction core.
-
# NOTE: there is no default for this, so it MUST be
-
#
-
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
-
-
-
# Set base name of file where messages are output
-
# (also known as the 'console file').
-
#
-
# com.atomikos.icatch.console_file_name = tm.out
-
-
# Size limit (in bytes) for the console file;
-
# negative means unlimited.
-
#
-
# com.atomikos.icatch.console_file_limit=-1
-
-
# For size-limited console files, this option
-
# specifies a number of rotating files to
-
# maintain.
-
#
-
# com.atomikos.icatch.console_file_count=1
-
-
# Set the number of log writes between checkpoints
-
#
-
# com.atomikos.icatch.checkpoint_interval=500
-
-
# Set output directory where console file and other files are to be put
-
# make sure this directory
-
#
-
# com.atomikos.icatch.output_dir = ./
-
-
# Set directory of log files; make sure this directory
-
#
-
com.atomikos.icatch.log_base_dir = ./
-
-
# Set base name of log file
-
# this name will be used as the first part of
-
# the system-generated log file name
-
#
-
# com.atomikos.icatch.log_base_name = tmlog
-
-
# Set the max number of active local transactions
-
# or -1 for unlimited.
-
#
-
# com.atomikos.icatch.max_actives = 50
-
-
# Set the default timeout (in milliseconds) for local transactions
-
#
-
# com.atomikos.icatch.default_jta_timeout = 10000
-
-
# Set the max timeout (in milliseconds) for local transactions
-
#
-
# com.atomikos.icatch.max_timeout = 300000
-
-
# The globally unique name of this transaction manager process
-
# override this value with a globally unique name
-
#
-
# com.atomikos.icatch.tm_unique_name = tm
-
- # Do we want to use parallel subtransactions? JTA
點選(此處)摺疊或開啟
-
/**
-
* Atomikos 資料來源A
-
* @return
-
*/
-
@Bean(name="dataSourceA",initMethod="init",destroyMethod="close")
-
public AtomikosDataSourceBean dataSourceA()
-
{
-
AtomikosDataSourceBean dataSourceA=new AtomikosDataSourceBean();
-
dataSourceA.setUniqueResourceName("dataSourceA");
-
dataSourceA.setXaDataSourceClassName(xaDataSourceClassName);
-
Properties xaProperties = new Properties();
-
xaProperties.put("user", user);
-
xaProperties.put("password", password);
-
xaProperties.put("url", jdbcUrl);
-
xaProperties.put("pinGlobalTxToPhysicalConnection", true);
-
dataSourceA.setXaProperties(xaProperties);
-
dataSourceA.setMaxPoolSize(maxPoolSize);
-
dataSourceA.setMinPoolSize(minPoolSize);
-
dataSourceA.setMaxIdleTime(maxIdleTime);
-
dataSourceA.setTestQuery("SELECT 1");
-
return dataSourceA;
-
}
-
-
-
/**
-
* Atomikos 資料來源A
-
* @return
-
*/
-
@Bean(name="dataSourceB",initMethod="init",destroyMethod="close")
-
public AtomikosDataSourceBean dataSourceB()
-
{
-
AtomikosDataSourceBean dataSourceA=new AtomikosDataSourceBean();
-
dataSourceA.setUniqueResourceName("dataSourceB");
-
dataSourceA.setXaDataSourceClassName(xaDataSourceClassName);
-
Properties xaProperties = new Properties();
-
xaProperties.put("user", readUser);
-
xaProperties.put("password", readPassword);
-
xaProperties.put("url", readJdbcUrl);
-
xaProperties.put("pinGlobalTxToPhysicalConnection", true);
-
dataSourceA.setXaProperties(xaProperties);
-
dataSourceA.setMaxPoolSize(readMaxPoolSize);
-
dataSourceA.setMinPoolSize(readMinPoolSize);
-
dataSourceA.setMaxIdleTime(readMaxIdleTime);
-
dataSourceA.setTestQuery("SELECT 1");
-
return dataSourceA;
- }
點選(此處)摺疊或開啟
-
@Configuration
-
public class DynamicTransactionManagerElConfig {
-
-
// @Autowired
-
// @Qualifier("platformTomcat")
-
// private DataSource platformTomcat;
-
//
-
// @Autowired
-
// @Qualifier("platformReadTomcat")
-
// private DataSource platformReadTomcat;
-
-
@Autowired
-
@Qualifier("dataSourceA")
-
private DataSource dataSourceA;
-
-
@Autowired
-
@Qualifier("dataSourceB")
-
private DataSource dataSourceB;
-
-
@Bean(name = "dataSource")
-
public DynamicDataSource dataSource() {
-
DynamicDataSource dataSource = new DynamicDataSource();
-
Map<Object, Object> targetDataSources = new HashMap<>();
-
targetDataSources.put("master", dataSourceA);
-
targetDataSources.put("slave", dataSourceB);
-
dataSource.setTargetDataSources(targetDataSources);
-
dataSource.setDefaultTargetDataSource(dataSourceA);
-
return dataSource;
-
}
-
-
@Bean(name = "jdbcTemplate")
-
public JdbcTemplate jdbcTemplate(DynamicDataSource dataSource) {
-
JdbcTemplate jdbcTemplate = new JdbcTemplate();
-
jdbcTemplate.setDataSource(dataSource);
-
return jdbcTemplate;
-
}
-
-
@Bean(name = "jdbcReadTemplate")
-
public JdbcTemplate jdbcReadTemplate(DynamicDataSource dataSource) {
-
JdbcTemplate jdbcReadTemplate = new JdbcTemplate();
-
jdbcReadTemplate.setDataSource(dataSource);
-
return jdbcReadTemplate;
-
}
-
-
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
-
public UserTransactionManager atomikosTransactionManager() {
-
UserTransactionManager atomikosTransactionManager = new UserTransactionManager();
-
atomikosTransactionManager.setForceShutdown(true);
-
return atomikosTransactionManager;
-
}
-
-
@Bean(name = "atomikosUserTransaction")
-
public UserTransactionImp atomikosUserTransaction() {
-
UserTransactionImp atomikosUserTransaction = new UserTransactionImp();
-
try {
-
atomikosUserTransaction.setTransactionTimeout(300);
-
}
-
catch (SystemException e) {
-
e.printStackTrace();
-
}
-
return atomikosUserTransaction;
-
}
-
-
// @Bean(name = "transactionManager")
-
// public DataSourceTransactionManager transactionManager(DynamicDataSource
-
// dataSource) {
-
// DataSourceTransactionManager transactionManager = new
-
// DataSourceTransactionManager();
-
// transactionManager.setDataSource(dataSource);
-
// return transactionManager;
-
// }
-
-
@Bean(name = "transactionManager")
-
public JtaTransactionManager transactionManager(UserTransactionManager atomikosTransactionManager,
-
UserTransactionImp atomikosUserTransaction) {
-
JtaTransactionManager transactionManager = new JtaTransactionManager();
-
transactionManager.setTransactionManager(atomikosTransactionManager);
-
transactionManager.setUserTransaction(atomikosUserTransaction);
-
transactionManager.setAllowCustomIsolationLevels(true);
-
return transactionManager;
-
}
-
- }
點選(此處)摺疊或開啟
-
@Retention(RetentionPolicy.RUNTIME)
-
@Target(ElementType.METHOD)
-
@Documented
-
public @interface DataSource {
-
String value();
- }
點選(此處)摺疊或開啟
-
public class DynamicDataSource extends AbstractRoutingDataSource {
-
-
@Override
-
protected Object determineCurrentLookupKey() {
-
return DataSourceContextHolder.getDataSource();
-
}
-
- }
點選(此處)摺疊或開啟
-
public class DataSourceContextHolder {
-
-
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
-
-
public static void setDataSource(String dataSource) {
-
contextHolder.set(dataSource);
-
}
-
-
public static String getDataSource() {
-
return contextHolder.get();
-
}
-
-
public static void removeDataSource() {
-
contextHolder.remove();
-
}
-
- }
點選(此處)摺疊或開啟
-
@Aspect
-
@Order(1)
-
@Component
-
public class DataSourceAspect {
-
-
@Pointcut("@annotation(com.gemdale.ghome.business.async.deal.center.demo.datasource.DataSource)")
-
public void dataSourcePointCut() {
-
};
-
-
@Before("dataSourcePointCut()")
-
public void before(JoinPoint joinPoint) {
-
-
System.out.println("=============dataSourcePointCut:before=============");
-
Object target = joinPoint.getTarget();
-
String method = joinPoint.getSignature().getName();
-
-
// Class[] classz = target.getClass().getInterfaces();
-
Class<?> classz = target.getClass();
-
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
-
-
try {
-
// Method m = classz[0].getMethod(method, parameterTypes);
-
Method m = classz.getMethod(method, parameterTypes);
-
if (null != m && m.isAnnotationPresent(DataSource.class)) {
-
DataSource dataSource = m.getAnnotation(DataSource.class);
-
DataSourceContextHolder.setDataSource(dataSource.value());
-
-
System.out.println("=============dataSource:" + dataSource.value());
-
}
-
}
-
catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
- }
點選(此處)摺疊或開啟
-
@Repository("gmcSmsInfoDaoImpl")
-
public class GmcSmsInfoDaoImpl extends BaseDaoSupport implements GmcSmsInfoDAO{
-
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addMaster(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("master")
-
public Integer addMaster(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
-
}
-
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addSlave(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("slave")
-
public Integer addSlave(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
-
}
-
-
- }
五:分散式事務下的資料來源動態切換
情況一:事務下唯一資料來源切換
點選(此處)摺疊或開啟
-
@Transactional(rollbackFor={Exception.class,RuntimeException.class})
-
@DataSource("master")
-
public GmcSmsInfo addMaster(GmcSmsInfo smsInfo) throws BusinessServiceException {
-
try {
-
smsInfo.setSmsId(gmcSmsInfoDaoImpl.save(smsInfo));
-
}
-
catch (FrameworkDAOException e) {
-
throw new BusinessServiceException(e);
-
}
-
return smsInfo;
- }
@Transactional 透過資料庫的connection來建立事務的,為了保證資料來源能順利切換,要保證@DataSource優先於@Transactional執行。 實現辦法 在DataSourceAspect 切面上增加註解@Order(1). 此處需要了解切面的層次和執行順序等相關知識。 @Transactionl,@DataSource 在service層的同一個方法上。
情況二:事務下多資料來源切換
service類
點選(此處)摺疊或開啟
-
@Autowired
-
private GmcSmsInfoDAO gmcSmsInfoDaoImpl;
-
-
@Autowired
-
@Qualifier("transactionManager")
-
private JtaTransactionManager transactionManager;
-
-
-
public void addMasterAndSlave(GmcSmsInfo smsInfo) throws BusinessServiceException {
-
UserTransaction userTransaction = transactionManager.getUserTransaction();
-
try {
-
userTransaction.begin();
-
gmcSmsInfoDaoImpl.addMaster(smsInfo);
-
smsInfo = new GmcSmsInfo();
-
-
smsInfo.setChannel("test2");
-
smsInfo.setContent("test2");
-
smsInfo.setStatus("001");
-
smsInfo.setCreateDate(Calendar.getInstance().getTime());
-
smsInfo.setMobile("88888888");
-
gmcSmsInfoDaoImpl.addSlave(smsInfo);
-
userTransaction.commit();
-
}
-
catch (Exception e) {
-
try {
-
userTransaction.rollback();
-
}
-
catch (IllegalStateException e1) {
-
e1.printStackTrace();
-
}
-
catch (SecurityException e1) {
-
e1.printStackTrace();
-
}
-
catch (SystemException e1) {
-
e1.printStackTrace();
-
}
-
throw new BusinessServiceException(e);
-
}
-
-
}
dao實現類
點選(此處)摺疊或開啟
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addMaster(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("master")
-
public Integer addMaster(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
-
}
-
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addSlave(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("slave")
-
public Integer addSlave(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
-
}
注意,此處採用了程式設計式來實現事務,註解式暫時還沒有好的解決方法,歡迎大家討論分享。 此處的@DataSource 放在了dao的實現層。
點選(此處)摺疊或開啟
-
@Autowired
-
private GmcSmsInfoDAO gmcSmsInfoDaoImpl;
-
-
@Autowired
-
@Qualifier("transactionManager")
-
private JtaTransactionManager transactionManager;
-
-
-
public void addMasterAndSlave(GmcSmsInfo smsInfo) throws BusinessServiceException {
-
UserTransaction userTransaction = transactionManager.getUserTransaction();
-
try {
-
userTransaction.begin();
-
gmcSmsInfoDaoImpl.addMaster(smsInfo);
-
smsInfo = new GmcSmsInfo();
-
-
smsInfo.setChannel("test2");
-
smsInfo.setContent("test2");
-
smsInfo.setStatus("001");
-
smsInfo.setCreateDate(Calendar.getInstance().getTime());
-
smsInfo.setMobile("88888888");
-
gmcSmsInfoDaoImpl.addSlave(smsInfo);
-
userTransaction.commit();
-
}
-
catch (Exception e) {
-
try {
-
userTransaction.rollback();
-
}
-
catch (IllegalStateException e1) {
-
e1.printStackTrace();
-
}
-
catch (SecurityException e1) {
-
e1.printStackTrace();
-
}
-
catch (SystemException e1) {
-
e1.printStackTrace();
-
}
-
throw new BusinessServiceException(e);
-
}
-
- }
點選(此處)摺疊或開啟
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addMaster(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("master")
-
public Integer addMaster(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
-
}
-
-
/* (non-Javadoc)
-
* @see com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfoDAO#addSlave(com.gemdale.ghome.business.async.deal.center.demo.GmcSmsInfo)
-
*/
-
@Override
-
@DataSource("slave")
-
public Integer addSlave(GmcSmsInfo smsInfo) throws FrameworkDAOException {
-
return save(smsInfo);
- }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28624388/viewspace-2137095/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- spring-data-redis 動態切換資料來源SpringRedis
- mybatis 多資料來源動態切換MyBatis
- 30個類手寫Spring核心原理之動態資料來源切換Spring
- @Transactional開啟事務導致AbstractRoutingDataSource動態資料來源無法切換的解決方案
- 30個類手寫Spring核心原理之動態資料來源切換(8)Spring
- Spring-Boot 多資料來源配置+動態資料來源切換+多資料來源事物配置實現主從資料庫儲存分離Springboot資料庫
- AbstractRoutingDataSource 實現動態資料來源切換原理簡單分析
- 基於AOP的動態資料來源切換(附原始碼)原始碼
- 專案要實現多資料來源動態切換,咋搞?
- spring-boot-route(十)多資料來源切換Springboot
- Spring 下,關於動態資料來源的事務問題的探討Spring
- SpringBoot 這麼實現動態資料來源切換,就很絲滑!Spring Boot
- siebel切換資料來源【轉】
- springboot+ mybatisplus+druid 實現多資料來源+分散式事務Spring BootMyBatisUI分散式
- spring多資料來源下 事務不生效Spring
- Spring Boot整合quartz實現定時任務並支援切換任務資料來源Spring Bootquartz
- 註解切換雙資料來源
- 分散式事務之Spring事務與JMS事務(二)分散式Spring
- 分散式事務~從seata例項來學習分散式事務分散式
- Spring Data JPA系列4——Spring宣告式數事務處理與多資料來源支援Spring
- spring框架中多資料來源建立載入並且實現動態切換的配置例項程式碼Spring框架
- 分散式鎖和spring事務管理分散式Spring
- 來了!阿里開源分散式事務解決方案Fescar阿里分散式
- 來了!阿里開源分散式事務解決方案 Fescar阿里分散式
- Springboot資料庫事務處理——Spring宣告式事務Spring Boot資料庫
- 使用Spring Boot實現分散式事務Spring Boot分散式
- 分散式事務之資料庫事務與JDBC事務實現(一)分散式資料庫JDBC
- Spring 註解動態資料來源設計實踐Spring
- 分散式事務(一)—分散式事務的概念分散式
- 實現多資料來源事務
- 關於Spring+Mybatis事務管理中資料來源的思考SpringMyBatis
- 多資料來源與動態資料來源的權衡
- 資料庫分散式事務的實現原理!資料庫分散式
- 阿里分散式事務框架GTS開源啦!阿里分散式框架
- 分散式事務(3)---RocketMQ實現分散式事務原理分散式MQ
- 分散式事務和分散式hash分散式
- 「分散式技術專題」事務型、分析型資料資源隔離機制分散式
- 聊聊如何利用apollo與druid整合實現資料來源動態熱切UI
- Springboot通過AOP整合多資料來源,分析@Transaction切換資料來源不生效問題Spring Boot