spring 多数据源事务问题
来源:互联网 发布:淘宝售假次数 编辑:程序博客网 时间:2024/05/21 16:59
spring整合mybatis,2个数据源,使用DynamicDataSource+aop,在方法调用之前根据方法上的注解来切换数据源,
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:property-placeholder location="classpath:db.properties"/> <!--<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">--> <!--<property name="config">--> <!--<bean class="com.google.code.kaptcha.util.Config">--> <!--<constructor-arg>--> <!--<props>--> <!--<prop key="kaptcha.border">yes</prop>--> <!--<prop key="kaptcha.border.color">105,179,90</prop>--> <!--<prop key="kaptcha.textproducer.font.color">blue</prop>--> <!--<prop key="kaptcha.image.width">250</prop>--> <!--<prop key="kaptcha.image.height">90</prop>--> <!--<prop key="kaptcha.textproducer.font.size">80</prop>--> <!--<prop key="kaptcha.session.key">code</prop>--> <!--<prop key="kaptcha.textproducer.char.length">4</prop>--> <!--<prop key="kaptcha.textproducer.font.names">瀹嬩綋,妤蜂綋,寰蒋闆呴粦</prop>--> <!--</props>--> <!--</constructor-arg>--> <!--</bean>--> <!--</property>--> <!--</bean>--> <bean id="BasicDataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="url" value="${dbcp2.url}"/> <property name="driverClassName" value="${dbcp2.driverClassName}"/> <property name="username" value="${dbcp2.username}"/> <property name="password" value="${dbcp2.password}"/> <property name="initialSize" value="${dbcp2.initialSize}"/> <property name="timeBetweenEvictionRunsMillis" value="${dbcp2.timeBetweenEvictionRunsMillis}"/> <property name="poolPreparedStatements" value="${dbcp2.poolPreparedStatements}"/> <property name="maxOpenPreparedStatements" value="${dbcp2.maxOpenPreparedStatements}"/> <property name="removeAbandonedTimeout" value="${dbcp2.removeAbandonedTimeout}"/> <property name="testOnBorrow" value="${dbcp2.testOnBorrow}"/> <property name="testOnReturn" value="${dbcp2.testOnReturn}"/> <property name="testWhileIdle" value="${dbcp2.testWhileIdle}"/> </bean> <bean id="BasicDataSource2" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="url" value="${dbcp2.url}"/> <property name="driverClassName" value="${dbcp2.driverClassName}"/> <property name="username" value="${dbcp2.username}"/> <property name="password" value="${dbcp2.password}"/> <property name="initialSize" value="${dbcp2.initialSize}"/> <property name="timeBetweenEvictionRunsMillis" value="${dbcp2.timeBetweenEvictionRunsMillis}"/> <property name="poolPreparedStatements" value="${dbcp2.poolPreparedStatements}"/> <property name="maxOpenPreparedStatements" value="${dbcp2.maxOpenPreparedStatements}"/> <property name="removeAbandonedTimeout" value="${dbcp2.removeAbandonedTimeout}"/> <property name="testOnBorrow" value="${dbcp2.testOnBorrow}"/> <property name="testOnReturn" value="${dbcp2.testOnReturn}"/> <property name="testWhileIdle" value="${dbcp2.testWhileIdle}"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> <bean name="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <property name="basePackage" value="com.lutai.admin.*.dao"/> </bean> <bean id="sqlTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--<!– 浣跨敤annotation瀹氫箟浜嬪姟 –>--> <!--<aop:aspectj-autoproxy expose-proxy="true"/>--> <!--<bean id="test" class="com.zheyue.authserver.controller.TestBean"></bean>--> <!--<bean id="a" class="com.zheyue.authserver.controller.AspectTest"></bean>--> <tx:annotation-driven transaction-manager="sqlTransactionManager" order="2"/> <bean id="userService" class="com.zheyue.authserver.service.impl.UserServiceImpl"></bean> <!-- 配置多数据源映射关系 --> <bean id="dataSource" class="com.zheyue.authserver.datasource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="dataSource1" value-ref="BasicDataSource"></entry> <entry key="dataSource2" value-ref="BasicDataSource2"></entry> </map> </property> <!-- 默认目标数据源为你主库数据源 --> <property name="defaultTargetDataSource" ref="BasicDataSource"/> </bean> <bean id="exchange" class="com.zheyue.authserver.datasource.DataSourceExchange"></bean> <aop:config expose-proxy="true"> <aop:pointcut id="pointcut" expression="execution(* *.save*(..))"/> <aop:advisor advice-ref="exchange" pointcut-ref="pointcut" order="1"/> </aop:config></beans>自定义注解
package com.zheyue.authserver.datasource;import java.lang.annotation.*;/** * Created by fd on 2016/10/18. */@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataSource { String name() default DataSource.master; public static String master = "BasicDataSource"; public static String slave = "BasicDataSource2";}aop拦截
package com.zheyue.authserver.datasource;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;/** * Created by fd on 2016/10/18. */public class DataSourceExchange implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(invocation.getMethod().getAnnotation(DataSource.class)); System.out.println(invocation.getMethod().getDeclaredAnnotation(DataSource.class)); String dataSourceName = invocation.getMethod().getAnnotation(DataSource.class).name(); DataSourceHolder.setDataSource(dataSourceName); System.out.println("使用数据源"+dataSourceName);// try {// invocation.proceed();// } catch (Exception e) {// e.printStackTrace();// } finally {//// } invocation.proceed(); return null; }}threadlocal
package com.zheyue.authserver.datasource;/** * Created by fd on 2016/10/18. */public class DataSourceHolder { public static final ThreadLocal<String> dataSources = new ThreadLocal<String>(); public static void setDataSource(String dataSourceName) { dataSources.set(dataSourceName); } public static String getDataSource() { return (String)dataSources.get(); } public static void clear() { dataSources.remove(); }}动态数据源
package com.zheyue.authserver.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * Created by fd on 2016/10/18. */public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); }}业务
package com.zheyue.authserver.service.impl;import com.zheyue.authserver.datasource.DataSource;import com.zheyue.authserver.service.UserService;import org.springframework.aop.framework.AopContext;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;/** * Created by fd on 2016/10/18. */public class UserServiceImpl implements UserService { public void save() { System.out.println("开始执行sava()方法"); try { UserService userService = (UserService) AopContext.currentProxy(); userService.save2(); } catch (Exception e) { e.printStackTrace(); } finally { } } public void save2() { System.out.println("开始执行sava2()方法"); }}
package com.zheyue.authserver.service;import com.zheyue.authserver.datasource.DataSource;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;/** * Created by fd on 2016/10/18. */public interface UserService { @DataSource(name = "BasicDataSource") @Transactional(propagation = Propagation.REQUIRED) void save(); @DataSource(name = "BasicDataSource2") @Transactional(propagation = Propagation.MANDATORY) void save2();}执行
public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); UserService userService = (UserService)applicationContext.getBean("userService"); try { userService.save(); } catch (Exception e) { e.printStackTrace(); } finally { }}
新建事务会从datasource获取一个连接connection,存在ConnectionHolder中,
事务的创建之后会把datasource作为key,connectionHolder作为value放进map中,然后将map放进名为resources的threadlocal中,也就是绑定当前线程了,
然后当你使用诸如propagation = Propagation.MANDATORY传播级别时,会取出当前线程绑定的connectionHolder,然后使用。
这就出现了一个问题
上面的动态数据源切换,对于某一个线程来说,新建一个事务是没有问题的,但是你这个线程需要使用2个数据源,如果重用事务,它使用的数据源就是之前的,
所以切换数据源也没有用了。。。
所以
1.一个线程就不要使用多个数据源了。。
2.或者不要重用事务,设置好隔离级别。
所以需要使用分布式事务???
0 0
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- Spring 多数据源事务配置问题
- spring 多数据源事务问题
- spring 多数据源事务
- Spring 多数据源事务配置
- spring 多数据源注解事务使用
- spring 多数据源一致性事务方案
- Spring 多数据源 事务配置
- Spring事务结合多数据源切换
- 问题:spring +atomikos 多数据源,分布式事务:只支持单阶段事务提交(不支持嵌套事务)?
- spring+hibernate多数据源+动态切换 事务 lazyload一应俱全
- 桌面快捷方式
- python+Django+postgresql 的web开发实例--杰西笔记
- 二叉树的链式结构的非递归遍历
- 命令的执行顺序 && || | () { }
- Jenkins编译失败Minimum supported Gradle version is 2.14.1. Current version is 2.8.
- spring 多数据源事务问题
- turn 搭建
- echo、read、tee、标准输入0、输出1、错误输出2、重定向>、追加重定向>>
- JavaScript中常用的转义字符
- (快速入门7)使用本地识别包
- 数据库的一些常识
- Adapter
- NOIP模拟题 2016.10.18 [二分答案] [从上到下的树形DP] [链表翻转]
- 各行业大数据分析报告