Spring中@Transactional与读写分离

标签: spring transactional 分离 | 发表时间:2018-10-23 16:58 | 作者:QING____
出处:http://www.iteye.com

    本文主要介绍如何使用Spring @Transactional基于JDBC Replication协议便捷的实现数据库的读写分离。 项目环境准备:

    1)Spring 4.x + 环境

    2)mysql connector-j 5.1.38+

    3)tomcat-jdbc-pool连接池

    4)spring @Transaction使用与JDBC Replcation协议。请参考 【replication协议】

 

    核心特性:

    1)所有操作默认请求从库,包括write、read,且无事务开启。

    2)如果期望请求主库,必须使用@Transactional或者使用编程式事务transactionManager(template)等开启事务。

    3)代码分层符合规范,合理设计事务开启的时机和范围。我们尽量将事务开启控制在manager或者dao层。

 

 

一、Spring配置(摘要)

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"
default-autowire="byName">

    <bean id="commonDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.ReplicationDriver"></property>
        <property name="url" value="jdbc:mysql:replication://127.0.0.1:3306,127.0.0.1:4306,127.0.0.1:5306/mydb?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=false&amp;useSSL=false&amp;failOverReadOnly=true&amp;loadBalanceStrategy=random&amp;readFormMasterNoSlaves=true"></property>
        <property name="username" value="test"></property>
        <property name="password" value="test"></property>
        <property name="maxTotal" value="12"></property>
        <property name="maxIdle" value="2"></property>
        <property name="minIdle" value="2"></property>
        <property name="maxWaitMillis" value="30000"></property>
        <property name="defaultAutoCommit" value="true"></property>
        <property name="defaultReadOnly" value="true"></property><!-- 必须为false,否则@transactional中的readOnly将无法正常工作 -->
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="commonDataSource" />
        <property name="configLocation" value="classpath:sqlmap-config.xml"></property>
        <!-- <property name="dataSource" ref="dataSource" /> -->
    </bean>

    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="commonDataSource"/>
    </bean>

    <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager" />
        <property name="isolationLevelName" value="ISOLATION_READ_COMMITTED"/>
        <property name="timeout" value="30"/>
    </bean>

    <!-- core api,必须为prototype -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

    <!-- 非常重要,否则@transactional将无法生效 -->
    <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
</beans>
 

 

 

 

    2、TestDao

 

@Component
public class TestDaoImpl extends BaseDao implements TestDao {

    @Override
    public TestDO get(int id) {
        return this.sqlSessionTemplate.selectOne("TestMapper.get",id);
    }

    @Override
    @Transactional(readOnly = false)
    public void update(TestDO test) {
        this.sqlSessionTemplate.update("TestMapper.update",test);
    }

}
 

 

    4、TestManager

 

@Component
public class TestManagerImpl implements TestManager {

    @Autowired
    private TestDao testDao;

    @Autowired
    private Test2Dao test2Dao

    @Override
    public TestDO get(int id) {
        return this.testDao.get(id);
    }
 
    @Override
    @Transactional(readOnly = false)
    public void update(TestDO test) {
        this.testDao.update(test);
        this.test2Dao.insert(test);//事务中执行
    }

}
 

 

二、@Transactional使用

    @Transactional注解配合Spring事务管理器,作用等同于“在方法级别手动开启事务”、“aspectj基于XML在方法级别切入拦截”。

    @Transactional参数列表:

    1)readOnly:限定connection级别read/write特性,默认为false,表示此连接支持读写。如果为true,表示此连接只能进行read操作(如果写操作将会抛出底层错误)。

    2)timeout:如果开启了事务,则事务超时的时长。超时则回滚。默认为-1,永不超时。

    3)propagation:传播级别,默认为REQUIRED,表示read/write操作都需要在事务中执行。如果不希望此操作主动开启新事务,事务由上下文决定,那么可以将此值设置为SUPPORTS,比如一个纯粹的read操作。

 

    @Transactional工作原理:

    1)默认请求将会访问从库,此特性由DataSourcePool中“ defaultReadOnly=true”决定。此时,在不使用@Transactional时和@Transactinal(readOnly=true)时在读写分离效果是一致的。

    2)使用@Transactional时,在进入修饰方法之前,默认开启事务并从DataSourcePool中获取一个connection(然后设置connection的readOnly和autoCommit参数值)。如果当前请求中,为只读操作,则不使用@Transactional。

    不使用@Transactional  <等价于> @Transactional(readOnly=true,propagation=Propagation.SUPPORTS),即不开始事务。但是@Transactional仍然触发获取连接。

    3)如果操作是write或者期望此操作访问主库,则必须显示声明@Transactional。(readOnly参数保持默认,或者设置为false,对于事务传播级别,按需)。

    4)无论如何,最终connection级别的readOnly参数值才是决定replication协议选择“读”、“写”库的最终依据。

 

 

三、@Transactional原理 

     当Spring Bean调用@transactional注释的方法时,将被拦截器拦截且通过反射机制方式执行(注释最终都是由拦截器驱动,参见TransactionInterceptor),且开启事务,如果readOnly为false时将会强制设置autocommit=false,在方法调用结束后,事务被自动提交。

 

    1)Spring基于反射机制拦截@Transactionnal,开启事务(SpringManagedTransaction),并根据@Transactional设定事务的属性,并将此事务TransactionInfo绑定在当前线程。(TransactionInteceptor)

    2)根据DataSource创建链接Connection,并将此链接绑定在当前线程(ConnectionHolder)。(DataSourceTransactionManager.doBegin())

    3)每个sqlSessionTemplate实例内部都有个代理实例sqlSessionProxy,即通过sqlSessionTemplate执行的方法均由此代理实例执行;在执行操作之前,首先获取sqlSession实例,并将sqlSession绑定在当前线程(SqlSessionHolder,ThreadLocal)。

    4)ibatis中使用此sqlSession执行数据库操作,sqlSession执行操作所使用的链接是从ConnectionHolder获取的。(DefaultSqlSession,SpringManagedTransaction)

    5)如果@Transactional方法中有多个dao层方法调用,则继续循环3)~5),此过程中,所有的方法均公用一个sqlSession实例。

    6)和1)对应,将TransactionInfo从当前线程解绑,并提交事务。

    7)和3)对应,将SqlSessionHolder从当前线程解绑,并关闭sqlSession。

    8)与2)对应,将ConnectionHolder从当前线程中解绑,并将Connection释放到连接池中。

 

    每调用一个@Transactional方法,都会按照上述过程执行;即如果你一个方法中调用了多个@Transactional方法,这意味着它们在不同的事务中执行、使用不同的sqlSession实例、可能使用不同的Connection。

 

    对于使用transactionTemplate方式手动开启事务的,过程稍微有些不同,在内部类doTransaction方法调用之前,将由spring创建事务、准备connection等与上述保持一致,并在方法执行后提交事务(如果抛出异常在rollback);doInTransaction中所调用的方法上的@Transactional将被忽略,所有的dao层方法均使用同一个sqlSession和Connection实例。

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring transactional 分离] 推荐:

Spring中@Transactional与读写分离

- - 编程语言 - ITeye博客
    本文主要介绍如何使用Spring @Transactional基于JDBC Replication协议便捷的实现数据库的读写分离.     1)Spring 4.x + 环境.     3)tomcat-jdbc-pool连接池.     4)spring @Transaction使用与JDBC Replcation协议.

深入Spring Boot:排查@Transactional引起的NullPointerException

- - 开源软件 - ITeye博客
这个demo来说明怎么排查一个. @Transactional引起的. 定位 NullPointerException 的代码. Demo是一个简单的spring事务例子,提供了下面一个. @Transactional来声明事务:. selectStudentById和. mvn spring-boot:run 或者把工程导入IDE里启动,抛出来的异常信息是:.

在使用spring mvc时,我使用了@Service这样的注解, 发现使用注解@Transactional声明的事务不起作用

- - CSDN博客Web前端推荐文章
在使用spring mvc时,我使用了@Service这样的注解, 发现使用注解@Transactional声明的事务不起作用. component-scan和事务所在的上下文不一样,component-scan所在的配置是由servlet加载的,事务所在的配置文件是由Listener加载的. 安装下面的配置,在应用启动时,不让spring扫描到@Service注解的类.

spring +mybatis读写分离

- - 编程语言 - ITeye博客
一、配置定义数据库连接属性. .     .     .     .     .         . .

spring实现数据库读写分离

- - 行业应用 - ITeye博客
现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库. Master库负责数据更新和 实时数据查询,Slave库当然负责非实时数据查询. 因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据 通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.

使用Spring实现读写分离

- - 编程语言 - ITeye博客
 1. 使用 背景. 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,. 其中一个是主库,负责写入数据,我们称之为:写库;. 其它都是从库,负责读取数据,我们称之为:读库;. 解决读写分离的方案有两种:应用层解决和中间件解决. 2.1.  应用层解决:.

在应用层通过spring解决数据库读写分离

- - CSDN博客推荐文章
如何配置mysql数据库的主从. 单机配置mysql主从: http://my.oschina.net/god/blog/496. 常见的解决数据库读写分离有两种方案. http://neoremind.net/2011/06/spring实现数据库读写分离. 目前的一些解决方案需要在程序中手动指定数据源,比较麻烦,后边我会通过AOP思想来解决这个问题.

Spring 配置多数据源实现数据库读写分离

- - 企业架构 - ITeye博客
现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库. Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询. 因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.

Spring + JPA实现数据库读写分离

- - 深入一点,你会更加快乐
    本文展示了如何在Spring环境中使用JPA实现dataSource的读写分离(本文没有使用JTA事务),这个东西看起来简单,其实实现起来比较蹩脚,与JDBC有很大区别.     1)使用Spring中的AbstractRoutingDataSource,辅助程序在运行时选择合适的dataSource.

前后端分离,spring boot跨域问题

- - 编程语言 - ITeye博客
1995年,同源政策由 Netscape 公司引入浏览器. 目前,所有浏览器都实行这个政策. 最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源". 所谓"同源"指的是"三个相同". 协议相同/域名相同/端口相同. 一句话:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域.