本文主要介绍如何使用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&characterEncoding=UTF-8&autoReconnect=false&useSSL=false&failOverReadOnly=true&loadBalanceStrategy=random&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实例。
相关推荐
Spring Boot是流行的Java开发框架,提供了便捷的方式来配置和使用数据库,包括实现数据库读写分离。 1. **MySQL主从复制原理**: 主从复制的核心是binlog(二进制日志),主节点的所有更改操作都会记录在binlog中...
在SpringBoot中实现MySQL的读写分离,我们需要依赖于Spring Data JPA或MyBatis等持久层框架,并结合配置中心(如Apache ZooKeeper或Consul)来动态管理读写数据库的地址。以下是具体步骤: 1. **设置主从复制**:在...
在"spring aop 读写分离"的场景下,我们主要关注如何利用Spring AOP实现数据库的读操作与写操作的分离,以提高系统性能和并发能力。以下将详细讲解这个主题。 首先,我们需要理解读写分离的基本概念。在高并发的Web...
在Spring框架中,实现数据库的读写分离是提高系统性能的一种常见策略,它可以将读操作和写操作分散到不同的数据源上,减轻主数据库的压力。本文将详细介绍如何利用Spring配置动态数据源来实现这一功能。 首先,我们...
在IT行业中,数据库的读写分离是一种常见的优化策略,它能有效地提高系统性能,尤其是在高并发环境下。SpringMVC作为Java Web开发中的一个强大框架,能够很好地支持这种架构设计。以下将详细介绍如何在SpringMVC项目...
本篇文章将深入探讨如何利用Spring和MyBatis实现读写分离,以及如何进行多数据源管理与数据库记录合并。 首先,读写分离是数据库架构设计中的一个关键策略。它通过将高并发的读操作和相对较少的写操作分配到不同的...
标题中的“Spring读写分离”指的是在基于Spring框架的应用中实现数据库的读写操作分离,以提高系统的性能和可扩展性。通常,读写分离是通过设置主从数据库集群来实现的,主库负责写操作,从库负责读操作,以此来分散...
数据库读写分离是一种常见的优化策略,它通过将读操作与写操作分配到不同的数据库服务器,以提高系统的并发处理能力和响应速度。在Web应用中,Spring和MyBatis框架结合可以很好地实现这一方案。以下是实现这一方案的...
基于mybatis,springboot开箱即用的读写分离插件 此插件由以下2部分组成 datasource:读写数据源的代理,支持一写多读,用户只需实现 ...
在现代的高并发Web应用中,数据库的读写分离是一种常见的优化策略,它可以提升系统的响应速度和可用性。本项目“Springboot-mybatis读写分离”正是基于SpringBoot和Mybatis框架实现这一功能的示例代码,适用于个人的...
这种方式简化了事务处理的代码,使得事务逻辑与业务逻辑分离,提高了代码的可读性和可维护性。同时,Spring的事务管理还支持多种数据访问技术,如JDBC、MyBatis等,可以方便地在不同的数据源间切换。
在Action类中,使用Spring的@Transactional注解标记需要进行事务控制的方法。 4. **Hibernate 事务管理** Hibernate的事务管理通常是由容器(如Spring)或者JTA(Java Transaction API)来完成的。在Spring中,...
在IT行业中,数据库的读写分离是提升系统性能和可扩展性的重要技术手段,尤其是在高并发的场景下。本教程将带你深入理解如何在SpringBoot项目中结合Mybatis和MySQL实现读写分离,并且探讨事务管理。以下是相关知识点...
在现代企业级应用开发中,数据库读写分离是一种常见的优化策略,它有助于提高系统的性能和可扩展性。本文将深入探讨如何使用AOP(面向切面编程)在Spring Boot框架中实现这一目标。 首先,我们需要理解什么是AOP。...
在企业级应用开发中,常常需要处理多个数据库的情况,例如主从数据库、读写分离等,因此掌握跨库访问的测试方法至关重要。 首先,Spring-test的核心组件是`SpringJUnit4ClassRunner`或`SpringRunner`(在JUnit5中),...
在本项目中,我们主要探讨如何使用SpringBoot与AOP(面向切面编程)来实现一个简单的数据库读写分离方案,同时利用阿里巴巴的Druid数据源。数据库读写分离是提高系统性能和可扩展性的重要手段,它将读操作和写操作...
这通常在需要处理来自不同数据源的数据或者实现数据库读写分离的场景下应用。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射,而Spring则是一个强大的企业级应用框架,提供了AOP(面向切面...
读写分离是数据库架构中的重要策略,它将读操作与写操作分配到不同的服务器,通常主库负责写操作,从库处理读操作,从而减轻主库压力,提高系统的并发能力。MyBatis多数据源配置主要涉及以下几个方面: 1. **数据源...
Spring还具备解决数据库读写分离的能力,在应用层面上通过Spring的特性来实现数据库读写分离,以提高应用性能和稳定性。 最后,Spring框架还支持Bean Validation规范,允许开发者在方法级别上进行验证,提高了数据...