Springboot 整合druid+mybatis+jta分布式事务+多数据源aop注解动态切换 (一篇到位)(1)

结语

小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。

我们选择的这个行业就一直要持续的学习,又很吃青春饭。

虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。

送给每一位想学习Java小伙伴,用来提升自己。

在这里插入图片描述

本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

}

然后是创建 DataSourceNames.java,用于简单数据源命名:

/**

  • @Author : JCccc

  • @CreateTime : 2019/8/28

  • @Description :

**/

public interface DataSourceNames {

String ONE = “ONE”;

String TWO = “TWO”;

}

ps:其实这些都是我之前aop切换数据源的时候敲的,大概8月份的时候,这次我相当于在这个基础上着重解决事务问题

然后是将自定义注解作为切点,进行aop方式动态切换逻辑补全,创建DynamicDataSourceAspect.java:

import com.test.jtadbsource.dbConfig.DataSourceContextHolder;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :

**/

@Aspect

@Component

public class DynamicDataSourceAspect {

protected Logger logger = LoggerFactory.getLogger(getClass());

/**

  • 切点: 所有配置 DataSource 注解的方法

*/

@Pointcut(“@annotation(com.test.jtadbsource.dbAop.DataSource)”)

public void dataSourcePointCut() {}

@Around(“dataSourcePointCut()”)

public Object around(ProceedingJoinPoint point) throws Throwable {

DataSource ds = null;

MethodSignature signature = (MethodSignature) point.getSignature();

Method method = signature.getMethod();

//获取自定义注解

ds = method.getAnnotation(DataSource.class);

if (ds == null) {

//如果监测到自定义注解不存在,那么默认切换到数据源 mydbone

DataSourceContextHolder.setDataSourceKey(DataSourceNames.ONE);

logger.info("set default datasource is " + DataSourceNames.ONE);

} else {

//自定义存在,则按照注解的值去切换数据源

DataSourceContextHolder.setDataSourceKey(ds.value());

logger.info("set datasource is " + ds.value());

}

return point.proceed();

}

@After(value = “dataSourcePointCut()”)

public void afterSwitchDS(JoinPoint point) {

DataSourceContextHolder.clearDataSourceKey();

logger.info(“clean datasource”);

}

}

上面用到的DataSourceContextHolder.java:

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :

**/

public class DataSourceContextHolder {

private static final ThreadLocal contextHolder = new ThreadLocal<>();

// 设置数据源名

public static void setDataSourceKey(String dbName) {

contextHolder.set(dbName);

}

// 获取数据源名

public static String getDataSourceKey() {

return (contextHolder.get());

}

// 清除数据源名

public static void clearDataSourceKey() {

contextHolder.remove();

}

}

ok,到这里,基本的动态切换边框的东西都完毕了,接下来是比较核心的:

1. DataSourceFactory.java  :

用于 不同的数据源DataSource的信息配置,使用DruidXADataSource创建,支持jta事务;

将不同数据源DataSource分别都关联上对应的AtomikosDataSourceBean,这样事务能提取到JTA事务管理器;

重写数据源会话工厂,为每个数据源单独配置一个。

配置重写的sqlSessionTemplate,将实际使用的不同数据源的sqlsession和spring的事务机制关联起来。

import com.alibaba.druid.pool.xa.DruidXADataSource;

import com.test.jtadbsource.dbAop.DataSourceNames;

import org.apache.ibatis.logging.stdout.StdOutImpl;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :多数据源配置

**/

@Configuration

@MapperScan(basePackages = DataSourceFactory.BASE_PACKAGES, sqlSessionTemplateRef = “sqlSessionTemplate”)

public class DataSourceFactory {

static final String BASE_PACKAGES = “com.test.jtadbsource.mapper”;

private static final String MAPPER_LOCATION = “classpath:mybatis/mapper/*.xml”;

/***

  • 创建 DruidXADataSource mydbone 用@ConfigurationProperties 自动配置属性

*/

@Bean

@ConfigurationProperties(“spring.datasource.druid.mydbone”)

public DataSource druidDataSourceOne() {

return new DruidXADataSource();

}

/***

  • 创建 DruidXADataSource mydbtwo

*/

@Bean

@ConfigurationProperties(“spring.datasource.druid.mydbtwo”)

public DataSource druidDataSourceTwo() {

return new DruidXADataSource();

}

/**

  • 创建支持 XA 事务的 Atomikos 数据源 mydbone

*/

@Bean

public DataSource dataSourceOne(DataSource druidDataSourceOne) {

AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();

sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceOne);

// 必须为数据源指定唯一标识

sourceBean.setPoolSize(5);

sourceBean.setTestQuery(“SELECT 1”);

sourceBean.setUniqueResourceName(“mydbone”);

return sourceBean;

}

/**

  • 创建支持 XA 事务的 Atomikos 数据源 mydbtwo

*/

@Bean

public DataSource dataSourceTwo(DataSource druidDataSourceTwo) {

AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();

sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceTwo);

sourceBean.setPoolSize(5);

sourceBean.setTestQuery(“SELECT 1”);

sourceBean.setUniqueResourceName(“mydbtwo”);

return sourceBean;

}

/**

  • @param dataSourceOne 数据源 mydbone

  • @return 数据源 mydbone 的会话工厂

*/

@Bean

public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne)

throws Exception {

return createSqlSessionFactory(dataSourceOne);

}

/**

  • @param dataSourceTwo 数据源 mydbtwo

  • @return 数据源 mydbtwo 的会话工厂

*/

@Bean

public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo)

throws Exception {

return createSqlSessionFactory(dataSourceTwo);

}

/***

  • sqlSessionTemplate 与 Spring 事务管理一起使用,以确保使用的实际 SqlSession 是与当前 Spring 事务关联的,

  • 此外它还管理会话生命周期,包括根据 Spring 事务配置根据需要关闭,提交或回滚会话

  • @param sqlSessionFactoryOne 数据源 mydbone

  • @param sqlSessionFactoryTwo 数据源 mydbtwo

*/

@Bean

public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne, SqlSessionFactory sqlSessionFactoryTwo) {

Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();

sqlSessionFactoryMap.put(DataSourceNames.ONE, sqlSessionFactoryOne);

sqlSessionFactoryMap.put(DataSourceNames.TWO, sqlSessionFactoryTwo);

CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(sqlSessionFactoryOne);

customSqlSessionTemplate.setTargetSqlSessionFactories(sqlSessionFactoryMap);

return customSqlSessionTemplate;

}

/***

  • 自定义会话工厂

  • @param dataSource 数据源

  • @return :自定义的会话工厂

*/

private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

factoryBean.setDataSource(dataSource);

org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();

//配置驼峰命名

configuration.setMapUnderscoreToCamelCase(true);

//配置sql日志

configuration.setLogImpl(StdOutImpl.class);

factoryBean.setConfiguration(configuration);

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

//配置读取mapper.xml路径

factoryBean.setMapperLocations(resolver.getResources(MAPPER_LOCATION));

return factoryBean.getObject();

}

}

上面用到的自定义CustomSqlSessionTemplate (重写SqlSessionTemplate):

import static java.lang.reflect.Proxy.newProxyInstance;

import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;

import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;

import static org.mybatis.spring.SqlSessionUtils.getSqlSession;

import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.sql.Connection;

import java.util.List;

import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;

import org.apache.ibatis.executor.BatchResult;

import org.apache.ibatis.session.Configuration;

import org.apache.ibatis.session.ExecutorType;

import org.apache.ibatis.session.ResultHandler;

import org.apache.ibatis.session.RowBounds;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.MyBatisExceptionTranslator;

import org.mybatis.spring.SqlSessionTemplate;

import org.springframework.dao.support.PersistenceExceptionTranslator;

import org.springframework.util.Assert;

public class CustomSqlSessionTemplate extends SqlSessionTemplate {

private final SqlSessionFactory sqlSessionFactory;

private final ExecutorType executorType;

private final SqlSession sqlSessionProxy;

private final PersistenceExceptionTranslator exceptionTranslator;

private Map<Object, SqlSessionFactory> targetSqlSessionFactories;

private SqlSessionFactory defaultTargetSqlSessionFactory;

/**

  • 通过Map传入

  • @param targetSqlSessionFactories

*/

public void setTargetSqlSessionFactories(Map<Object, SqlSessionFactory> targetSqlSessionFactories) {

this.targetSqlSessionFactories = targetSqlSessionFactories;

}

public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {

this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {

this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {

this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()

.getEnvironment().getDataSource(), true));

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,

PersistenceExceptionTranslator exceptionTranslator) {

super(sqlSessionFactory, executorType, exceptionTranslator);

this.sqlSessionFactory = sqlSessionFactory;

this.executorType = executorType;

this.exceptionTranslator = exceptionTranslator;

this.sqlSessionProxy = (SqlSession) newProxyInstance(

SqlSessionFactory.class.getClassLoader(),

new Class[] { SqlSession.class },

new SqlSessionInterceptor());

this.defaultTargetSqlSessionFactory = sqlSessionFactory;

}

//通过DataSourceContextHolder获取当前的会话工厂

@Override

public SqlSessionFactory getSqlSessionFactory() {

String dataSourceKey = DataSourceContextHolder.getDataSourceKey();

SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactories.get(dataSourceKey);

if (targetSqlSessionFactory != null) {

return targetSqlSessionFactory;

} else if (defaultTargetSqlSessionFactory != null) {

return defaultTargetSqlSessionFactory;

} else {

Assert.notNull(targetSqlSessionFactories, “Property ‘targetSqlSessionFactories’ or ‘defaultTargetSqlSessionFactory’ are required”);

Assert.notNull(defaultTargetSqlSessionFactory, “Property ‘defaultTargetSqlSessionFactory’ or ‘targetSqlSessionFactories’ are required”);

}

return this.sqlSessionFactory;

}

@Override

public Configuration getConfiguration() {

return this.getSqlSessionFactory().getConfiguration();

}

public ExecutorType getExecutorType() {

return this.executorType;

}

public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {

return this.exceptionTranslator;

}

/**

  • {@inheritDoc}

*/

public T selectOne(String statement) {

return this.sqlSessionProxy. selectOne(statement);

}

/**

  • {@inheritDoc}

*/

public T selectOne(String statement, Object parameter) {

return this.sqlSessionProxy. selectOne(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, String mapKey) {

return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {

return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {

return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement) {

return this.sqlSessionProxy. selectList(statement);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement, Object parameter) {

return this.sqlSessionProxy. selectList(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement, Object parameter, RowBounds rowBounds) {

return this.sqlSessionProxy. selectList(statement, parameter, rowBounds);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, ResultHandler handler) {

this.sqlSessionProxy.select(statement, handler);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, Object parameter, ResultHandler handler) {

this.sqlSessionProxy.select(statement, parameter, handler);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {

this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);

}

/**

  • {@inheritDoc}

*/

public int insert(String statement) {

return this.sqlSessionProxy.insert(statement);

}

/**

  • {@inheritDoc}

*/

public int insert(String statement, Object parameter) {

return this.sqlSessionProxy.insert(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public int update(String statement) {

return this.sqlSessionProxy.update(statement);

}

/**

  • {@inheritDoc}

*/

public int update(String statement, Object parameter) {

return this.sqlSessionProxy.update(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public int delete(String statement) {

return this.sqlSessionProxy.delete(statement);

}

/**

  • {@inheritDoc}

*/

public int delete(String statement, Object parameter) {

return this.sqlSessionProxy.delete(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public T getMapper(Class type) {

return getConfiguration().getMapper(type, this);

}

/**

  • {@inheritDoc}

*/

public void commit() {

throw new UnsupportedOperationException(“Manual commit is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void commit(boolean force) {

throw new UnsupportedOperationException(“Manual commit is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void rollback() {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void rollback(boolean force) {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void close() {

throw new UnsupportedOperationException(“Manual close is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void clearCache() {

this.sqlSessionProxy.clearCache();

}

/**

  • {@inheritDoc}

*/

public Connection getConnection() {

return this.sqlSessionProxy.getConnection();

}

/**

  • {@inheritDoc}

  • @since 1.0.2

*/

public List flushStatements() {

return this.sqlSessionProxy.flushStatements();

}

/**

  • Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring’s Transaction Manager It also

  • unwraps exceptions thrown by {@code Method#invoke(Object, Object…)} to pass a {@code PersistenceException} to

  • the {@code PersistenceExceptionTranslator}.

*/

private class SqlSessionInterceptor implements InvocationHandler {

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

final SqlSession sqlSession = getSqlSession(

CustomSqlSessionTemplate.this.getSqlSessionFactory(),

CustomSqlSessionTemplate.this.executorType,

CustomSqlSessionTemplate.this.exceptionTranslator);

try {

Object result = method.invoke(sqlSession, args);

if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {

sqlSession.commit(true);

}

return result;

} catch (Throwable t) {

Throwable unwrapped = unwrapThrowable(t);

if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {

Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator

.translateExceptionIfPossible((PersistenceException) unwrapped);

if (translated != null) {

unwrapped = translated;

}

}

throw unwrapped;

} finally {

closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());

}

}

}

}

然后是xat分布式事务管理器,XATransactionManagerConfig.java:

import com.atomikos.icatch.jta.UserTransactionImp;

import com.atomikos.icatch.jta.UserTransactionManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.transaction.PlatformTransactionManager;

import org.springframework.transaction.annotation.EnableTransactionManagement;

import org.springframework.transaction.jta.JtaTransactionManager;

import javax.transaction.TransactionManager;

import javax.transaction.UserTransaction;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :JTA 事务配置

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
架构面试专题及架构学习笔记导图.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

}

}

}

}

然后是xat分布式事务管理器,XATransactionManagerConfig.java:

import com.atomikos.icatch.jta.UserTransactionImp;

import com.atomikos.icatch.jta.UserTransactionManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.transaction.PlatformTransactionManager;

import org.springframework.transaction.annotation.EnableTransactionManagement;

import org.springframework.transaction.jta.JtaTransactionManager;

import javax.transaction.TransactionManager;

import javax.transaction.UserTransaction;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :JTA 事务配置

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
[外链图片转存中…(img-Ex7blWpG-1715466606914)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值