虽然我们在用Spring的事务管理,但总体感觉使用的时候未知(未搞懂)的概念还是挺多的,心里总是没有底,在使用的过程中也比较容易用错误的方式来实现。
关于Spring声明式事务,具体可以参考下面这篇文章:
目前我们使用的是第五种方式,即全注解方式,这种方式使用比较方便,在spring的配置文件中声明:
...... <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
在Service层中需要加入事务的地方加入:
@Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } ...... }
我们使用@Transactional的时候,都是使用其默认值,但其中的行为如下:
传播属性
@Transactional注解中,默认的传播属性可以在源码中看到,属于PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,这是最常见的选择。
Propagation propagation() default Propagation.REQUIRED;
- PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
此外,在Spring中额外提供了另外一个事务管理方式,在EJB中并未对应,只在Spring的事务管理中存在:
PROPAGATION_NESTED:理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的,而Nested事务的好处是他有一个savepoint。
ServiceA { //事务属性配置为 PROPAGATION_REQUIRED void methodA() { try { //savepoint ServiceB.methodB(); //PROPAGATION_NESTED 级别 } catch (SomeException) { // 执行其他业务, 如 ServiceC.methodC(); } } }
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。但是这个事务并没有在EJB标准中定义。
问题:我们当前的所有查询其实都使用的默认传播属性REQUIRED,其实是不合理的,这会造成性能的略微下降;
隔离级别
事务的隔离级别属于数据库的支持,通常分为下面几类,由隔离级别从低到高:
1. ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应
2. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED: 保证一个事务不能读到另一个并行事务已修改但未提交的数据。数据提交后才能被读取。避免了脏数据。该级别适应于大多数系统。大多数主流数据库默认的级别。
4. ISOLATION_REPEATABLE_READ:它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。避免了脏读,不可重复读。但是可能出现幻像读。但是也带来更多的性能损失。
5. ISOLATION_SERIALIZABLE 事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。这是花费最高代价但是最可靠的事务隔离级别。
我们当前的数据库隔离级别,如果没有设置,就为Isolation.DEFAULT,定义显示为使用底层数据源的默认隔离级别。除非DBA有额外设置,对于MySQL InnoDB引擎默认是可重复读的(REPEATABLE READ)
/** * Use the default isolation level of the underlying datastore. * All other levels correspond to the JDBC isolation levels. * @see java.sql.Connection */ int ISOLATION_DEFAULT = -1;
READ-ONLY
对于@Transactional注解中的read-only属性,之前感觉我们的理解都不算太透彻,可以参考下面这篇文章:
其概念为:从这一点设置的时间点开始(时间点a)到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见!(查询中不会出现别人在时间点a之后提交的数据);
对于只读查询,可以指定事务类型为readonly,即只读事务,据同事讲,这个时候可以在其中操纵数据库,但是并不会commit到数据源中。
关于只读事务和无事务
那么问题来了,什么时候使用只读事务,什么时候将事务的传播属性改为SUPPORT?
由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。
但是如果连事务都不启动,从理论上讲性能应该更佳,参考其中的一篇blog来讲述这个问题:
- 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
- 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。 【注意是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务】
多数据源
如果多个数据源之间不存在分布式事务的话(即此时不会跨数据源对某个服务执行增删改等操作,查询无所谓),数据源之间可以采用直接在spring的配置文件中配置多个数据源,然后分别进行事务的配置。
在单数据源时,如果事务配置类型为SUPPORTS且没有嵌套在事务级别中,无论成功执行与否,方法都不会报错,只是无法进行数据库的增删改操作。
如果没有配置事务(服务上没有定义@Transactional注解),则会执行每一步就提交,异常不会影响之前已经提交的修改。
如果设置成REQUIRED,出现异常会导致整体事务的回退,如果不出现异常,一起提交。
比如我们有两个数据源,一个为master,一个为slave,这时就需要声明将两个transactionManager纳入事务管理:
<tx:annotation-driven transaction-manager="transactionManager"/> <tx:annotation-driven transaction-manager="transactionManagerSlave"/>
在其中定义两套dataSource, transactionManager, sqlSessionFactory,最重要的是两个MapperScannerConfigurer,通过这个能够正确区分出mapper包使用的事务:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.api.example.pub.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
这就要求我们要将不同数据源之间使用的Mapper接口,实体类型,以及mybatis mapper文件隔离,以数据源区分。
但此时使用注解声明式事务还测试,还是不能保证正确的事务已经被加上,这是因为当存在多个TransactionManager时,Spring貌似默认使用第一个定义的(加载的)事务管理器,其不知道到底该使用哪个事务管理器。
@Transactional注解加入了一个value属性,用来手动来指定具体使用的事务管理器,可以参考:http://stackoverflow.com/questions/3333133/multiple-transaction-managers-with-transactional-annotation
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { /** * A qualifier value for the specified transaction. * <p>May be used to determine the target transaction manager, * matching the qualifier value (or the bean name) of a specific * {@link org.springframework.transaction.PlatformTransactionManager} * bean definition. */ String value() default "";
加入该属性,就可以正常使用@Transactional对应的事务管理器了,需要注意的是,这个我们所说的多个数据源管理并没有涉及到分布式事务,如果需要分布式事务的话这种配置方式肯定是不支持的。
相关推荐
Spring事务管理的目的是确保数据的一致性和完整性,尤其是在多操作、多资源的环境中。本Demo将深入探讨Spring如何实现事务的管理。 首先,Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。 ...
### Spring事务管理详解 #### 一、事务管理基础概念 在深入探讨Spring事务管理之前,我们需要先理解什么是事务。事务可以被定义为一系列的操作集合,这些操作作为一个整体被提交或回滚。简单来说,事务就是一个不...
本资源包提供了进行Spring事务管理开发所需的所有关键库,包括框架基础、核心组件、AOP(面向切面编程)支持、日志处理、编译工具以及与数据库交互的相关jar包。下面将对这些知识点进行详细解释: 1. **Spring框架*...
Spring 框架是Java开发中...理解并熟练掌握Spring事务管理,对于提升应用程序的稳定性和可靠性至关重要。在实际开发中,结合声明式事务管理、事务传播行为、隔离级别和回滚规则,可以有效地确保数据的完整性和一致性。
Spring事务管理.pdf 1.资料 2.本地事务与分布式事务 3.编程式模型 4.宣告式模型
Synchronized锁在Spring事务管理下,导致线程不安全。
本篇将深入探讨Spring事务管理的核心概念、工作原理以及如何使用`spring-tx-3.2.0.RELEASE.jar`这个jar包。 首先,我们需要理解什么是事务。在数据库系统中,事务是一组操作,这些操作被视为一个整体,要么全部完成...
标题“Spring事务管理失效原因汇总”指出了本文的核心内容是分析在使用Spring框架进行事务管理时可能遇到的问题及其原因。描述部分进一步说明了事务失效的后果往往不明显,容易在测试环节被忽略,但在生产环境中出现...
本篇文章将深入探讨Spring事务管理的五种方法,旨在帮助开发者更好地理解和运用这一核心特性。 首先,我们来了解什么是事务。在数据库操作中,事务是一组逻辑操作,这些操作要么全部成功,要么全部失败,确保数据的...
本文将详细介绍Spring事务管理的四种方式:编程式事务管理、声明式事务管理、PlatformTransactionManager接口以及TransactionTemplate。 1. **编程式事务管理**:这是一种手动控制事务的方式,通过在代码中调用`...
### Spring事务管理详解 #### 一、Spring事务管理的重要性及必要性 在现代软件开发中,事务管理是一项至关重要的技术,特别是在涉及数据库操作时。事务能够确保一系列操作要么全部成功,要么全部失败,这对于保持...
Spring事务管理是Spring框架的核心特性之一,它提供了一种强大且灵活的方式来管理应用程序中的事务边界。在企业级Java应用中,事务处理是确保数据一致性、完整性和可靠性的关键部分。本篇文章将深入探讨Spring的事务...
总的来说,Spring事务管理提供了一种灵活、强大的方式来处理应用程序中的事务,无论是在简单还是复杂的事务场景下,都能有效保证数据的一致性和完整性。通过声明式事务管理,开发者可以将关注点从事务细节中解脱出来...
Spring事务管理是Spring框架的核心特性之一,它提供了一种在Java应用中管理和协调数据库事务的标准方式。对于有Java基础的开发者来说,理解并掌握Spring事务管理至关重要,因为这有助于确保数据的一致性和完整性,...
当出现像描述中那样的问题——SQL语句执行出错但事务未回滚时,我们需要深入理解Spring事务管理的配置和机制。以下是一些关键知识点: 1. **Spring事务管理类型**: - **编程式事务管理**:通过`...
spring事务管理几种方式代码实例:涉及编程式事务,声明式事务之拦截器代理方式、AOP切面通知方式、AspectJ注解方式,通过不同方式实例代码展现,总结spring事务管理的一般规律,从宏观上加深理解spring事务管理特性...
在思维导图"Spring Transaction.twd"中,可能包含了Spring事务管理的各个概念和它们之间的关系,如事务的ACID属性(原子性、一致性、隔离性和持久性),事务管理器,以及声明式和编程式事务管理的实现方式。...
### Spring事务管理的方法 #### 一、引言 在企业级应用开发中,事务管理是一项至关重要的技术。Spring框架作为Java领域中一个非常流行的轻量级框架,为开发者提供了多种方式来实现事务管理,其中主要分为编程式...