在实际项目开发中数据库操作一般都是并发执行的,即有多个事务并发执行,并发执行就可能遇到问题,目前常见的问题如下:
- 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
- 脏读:一个事务看到了另一个事务未提交的更新数据;
- 不可重复读:在同一事务中,多次读取同一数据却返回不同的结果;也就是有其他事务更改了这些数据;
- 幻读:一个事务在执行过程中读取到了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样
为了解决这些并发问题,需要通过数据库隔离级别来解决,在标准SQL规范中定义了四种隔离级别:
未提交读(Read Uncommitted):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
提交读(Read Committed):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读;
可重复读(Repeatable Read):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
序列化(Serializable):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读
事务类型
数据库事务类型有本地事务和分布式事务
- 本地事务:就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上;
- 分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的)
Java事务类型有JDBC事务和JTA事务:
- JDBC事务:就是数据库事务类型中的本地事务,通过Connection对象的控制来管理事务;
- JTA事务:JTA指Java事务API(Java Transaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务。
按是否通过编程实现事务有声明式事务和编程式事务;
声明式事务: 通过注解或XML配置文件指定事务信息;
编程式事务:通过编写代码实现事务
Spring框架最核心功能之一就是事务管理,而且提供一致的事务管理抽象,
- 提供一致的编程式事务管理API,不管使用Spring JDBC框架还是集成第三方框架使用该API进行事务编程;
- 无侵入式的声明式事务支持。
Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManager,
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
getTransaction():返回一个已经激活的事务或创建一个新 的事务(根据给定的TransactionDefinition类型参数定义的事务属性),返回的是TransactionStatus对象代表了当前事 务的状态,其中该方法抛出TransactionException(未检查异常)表示事务由于某种原因失败。
commit():用于**提交**TransactionStatus参数代表的事务
rollback():用于**回滚**TransactionStatus参数代表的事务
内置事务管理器
Spring提供了许多内置事务管理器实现:
- DataSourceTransactionManager:数据源事务管理器,提供对单个javax.sql.DataSource事务管理,
- HibernateTransactionManager:提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;
- JtaTransactionManager:提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
- WebSphereUowTransactionManager:Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
- WebLogicJtaTransactionManager:Spring提供的用于对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。
还提供对如JMS事务管理的管理器等
Spring提供一致的事务抽象如图所示
spring配置事务管理器
一、声明对本地事务的支持:
a)JDBC及iBATIS、MyBatis框架事务管理器
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"/>
</bean>
b)Jpa事务管理器
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--通过entityManagerFactory属性指定需要事务管理的javax.persistence.EntityManagerFactory对象。-->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
……
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
为entityManagerFactory对象指定jpaDialect属性,该属性所对应的对象指定了如何获取连接对象、开启事务、关闭事务等事务管理相关的行为。
c)Hibernate事务管理器
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--通过sessionFactory属性指定需要事务管理的org.hibernate.SessionFactory对象。-->
Spring支持两种事务管理方式:
编程式事务管理 ,声明式事务管理。
(1),编程式事务
编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。
Spring框架提供一致的事务抽象,因此对于JDBC还是JTA事务都是采用相同的API进行编程。
在Spring中将采用一致的事务抽象进行控制和一致的异常控制,即面向 PlatformTransactionManager接口编程来控制事务。
Spring中的事务分为物理事务和逻辑事务;
- 物理事务:就是底层数据库提供的事务支持,如JDBC或JTA提供的事务;
- 逻辑事务:是Spring管理的事务,不同于物理事务,逻辑事务提供更丰富的控制,Spring中如果没特别强调一般就是逻辑事务;
逻辑事务两种方案:低和高
低:
使用工具类:使用工具类获取连接(会话)和释放连接(会话),DataSourceUtils 类来获取和释放具有逻辑事务功能的连接,第三方ORM框架类似的工具类,对Hibernate提供了SessionFactoryUtils工具类
高:
使用Spring提供的模板类,如JdbcTemplate、HibernateTemplate和JpaTemplate模板类等,而这些模板类内部其实是使用了低级别解决方案中的工具类来管理连接或会话;
Spring提供两种编程式事务支持:
(1),直接使用PlatformTransactionManager实现
(2),使用TransactionTemplate模板类,用于支持逻辑事务管理。
采用编程式事务推荐使用TransactionTemplate模板类和高级别解决方案。
使用TransactionTemplate
TransactionTemplate模板类用于简化事务管理,事务管理由模板类定义,而具体操作需要通过回调接口指定,通过调用模板类的的execute方法来自动享受事务管理,参数类型为 TransactionCallback或TransactionCallbackWithoutResult
回调接口:
- TransactionCallback:
- TransactionCallbackWithoutResult:继承TransactionCallback接口
txManager = ctx.getBean(PlatformTransactionManager.class);
dataSource = ctx.getBean(DataSource.class);
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute(CREATE_TABLE_SQL);
//创建事务模板类,其中构造器参数为PlatformTransactionManager实现
TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
/*
或
TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);
*/
//通过其相应方法设置事务定义,如事务隔离级别、传播行为等,此处未指定传播行为, 其默认为PROPAGATION_REQUIRED;
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//通过execute方法执行需要事务管理的回调。
//使用不带返回的回调实现TransactionCallbackWithoutResult
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//表示向数据库中的test表中插入数据;
jdbcTemplate.update(INSERT_SQL, "test");
}
}
);
抛出Exception类型的异常且需要回滚时,需要捕获异常并通过调用status对象的setRollbackOnly()方法告知事务管理器当前事务需要回滚
try {
//业务操作
....
} catch (Exception e) { //可使用具体业务异常代替
status.setRollbackOnly();
}
实际运用: Service实现中需要Spring事务管理的部分应该使用TransactionTemplate模板类来包装执行
★注意:
Q:Spring事务全部在Service层定义,为什么会在Service层定义,而不是Dao层定义呢?
A:因为在服务层可能牵扯到业务逻辑,而每个业务逻辑可能调用多个Dao层方法,为保证这些操作的原子性,必须在Service层定义事务。
事务属性
通过TransactionDefinition接口实现定义,主要有事务隔离级别、事务传播行为、事务超时时间、事务是否只读。默认实现DefaultTransactionDefinition
- 事务超时:设置事务的超时时间,单位为秒,默认为-1表示使用底层事务的超时时间
DataSourceTransactionManager. setDefaultTimeout(10)。 - 事务只读:将事务标识为只读,只读事务不修改任何数据
(2),声明式事务
声明式事务支持方式属于无侵入式,对业务逻辑实现无影响。
Spring配置:
1、XML命名空间定义,定义用于事务支持的tx命名空间和AOP支持的aop命名空间
2、业务实现配置,
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
3、事务相关配置:
<!--
<tx:advice>:事务通知定义,用于指定事务属性"transaction-manager"属性指定事务管理器,
并通过< tx:attributes >指定具体需要拦截的方法;
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- save* :拦截以save开头的方法 -->
<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<!-- * :拦截其他所有方法 ; read-only=”true”表示事务只读; -->
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* cn..chapter9.service..*.*(..))"/>
<!-- <aop:advisor>:Advisor定义,其中切入点为serviceMethod,通知为txAdvice。 -->
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
声明式事务管理同样是通过AOP代理方式实现(cglib或者原生jdk代理)
<tx:advice>:id用于指定此通知的名字,
<tx:method>:用于定义事务属性即相关联的方法名
rollback-for:需要触发回滚的异常定义,以“,”分割;
no-rollback-for:不被触发进行回滚的 Exception(s);以“,”分割;
多事务语义配置:
<!-- 声明式事务最佳配置-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
<!-- 只需修改切入点表达式来拦截我们的业务实现从而对其应用事务属性就可以了 pointcut-ref - expression -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
@Transactional实现事务管理
Spring提供基于@Transactional注解方式来实现,但需要Java 5+,
Spring提供的< tx:annotation-driven/>
用于开启对注解事务管理的支持,
Spring使用@Transaction来指定事务属性,可以在接口、类或方法上指定
1、业务逻辑代码
2、XML命名空间定义,定义用于事务支持的tx命名空间和AOP支持的aop命名空间:
3、业务实现Bean配置
4、事务相关配置:< tx:annotation-driven transaction-manager="txManager"/>
@Transaction元数据说明:
value:指定事务管理器名字,默认使用< tx:annotation-driven/>
指定的事务管理器,用于支持多事务管理器环境;
rollbackForClassname:指定一组异常类名字,其含义与< tx:method>
中的rollback-for属性语义完全一样;
★注:
- 如果在接口、实现类或方法上优先级顺序为 :方法>实现类>接口;
- 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制是没问题,因为其使用基于接口的代理;而使用使用CGLIB代理机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”;
- 默认只对RuntimeException异常回滚;
- 在public可见度的方法的@Transactional 注解才是有效的。
Spring声明式事务实现其实就是Spring AOP+线程绑定实现,利用AOP实现开启和关闭事务,利用线程绑定(ThreadLocal)实现跨越多个方法实现事务传播