在这里不细说什么是事务了,想明白什么是事务的,可以查阅相关资料,直接进入主题。
Spring中事务管理
Spring事务管理高层抽象主要包括3个接口:
- PlatformTransactionManager 事务管理器
- TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
隔离设置:isolation="DEFAULT"(默认),其他配置选项可查看下方 “事务隔离级别”
传播设置:propagation="REQUIRED"(默认),其他配置选项可查看下方 “事务的传播行为”
超时设置:timeout=-1 (默认) ,代表事务永不超时 直到提交。在实际开发中可以根据情况进行实时的调整,一般为30s
只读设置:read-only=false (默认)代表当前事务可读可写。 =true 时代表当前事务只可读
- TransactionStatus 事务具体运行状态
三个接口中事务管理器PlatformTransactionManager:
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现(红色字体标记较重要)
org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager
使用Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager
使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager
当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager
使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
三个接口中事务定义信息TransactionDefinition:
事务隔离级别(红色字体标记较重要):
DEFAULT 使用后端数据库默认的隔离级别(spring中的的选择项)
READ_UNCOMMITED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生
REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。
SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。
事务的传播行为(不是JDBC事务管理,用来解决实际开发的问题.)传播行为:解决业务层之间的调用的事务的关系)
指的是对当前事务的支持,默认是REQUIRED
事务传播行为类型 说明
PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个 例如: A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务 例如: A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常 例如:A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务 例如:A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务 例如: A,B 非事务的方式运行,A有事务,就会挂起当前的事务.
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常 例如:A,B 非事务的方式运行,A有事务,就会抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行 基于SavePoint技术. 例如: A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.
Spring事务支持两种事务管理
1.编程时事务管理
在实际应用中很少使用
通过TransactionTemplate手动管理事务
2.使用XML配置文件声明式事务
开发中推荐使用(代码侵入性最小)
Spring的声明式事务是通过AOP实现的
下面通过转账案例演示spring中如何管理事务:
直接上代码了。
UserdaoI.java:
public interface UserDaoI {
void inMoney(String name,int money);
void outMoney(String name,int money);
}
UserDaoImpl.java:
@Repository
public class UserDaoImpl implements UserDaoI{
@Autowired
private JdbcTemplate jdbcTemlate;
@Override
public void inMoney(String name, int money) {
// TODO Auto-generated method stub
String sql="update t_user set money=money+? where name=?";
Object[] objects=new Object[]{money,name};
jdbcTemlate.update(sql, objects);
}
@Override
public void outMoney(String name, int money) {
// TODO Auto-generated method stub
String sql="update t_user set money=money-? where name=?";
Object[] objects=new Object[]{money,name};
jdbcTemlate.update(sql, objects);
}
}
UserService.java:
@Service
public class UserService {
@Autowired
private UserDaoI userDao=new UserDaoImpl();
public void transfer(){
userDao.inMoney("张三", 10);
int i=1/0;
userDao.outMoney("李四", 10);
}
}
beans.xml:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入第三方 properties配置 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<context:annotation-config/>
<context:component-scan base-package="com.baidu.service,com.baidu.dao"></context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- ${key} 可以读取properties文件中配置 key对应value -->
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器
id:不建议修改,就叫transactionManager
如果要进行注解的事务,那么事务管理器必须名字是transactionManager
-->
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入连接池 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 增强(事务)的属性的配置 -->
<tx:attributes>
<!--
isolation:DEFAULT :事务的隔离级别.
propagation :事务的传播行为.
read-only :false.不是只读
timeout :-1
no-rollback-for :发生哪些异常不回滚
rollback-for :发生哪些异常回滚事务
-->
<tx:method name="transfer" propagation="REQUIRED"/><!-- name:测试方法中方法名称-->
</tx:attributes>
</tx:advice>
<!-- AOP相关配置 -->
<!-- aop配置定义切面和切点的信息 -->
<aop:config>
<!-- 定义切点:哪些类的哪些方法应用增强 -->
<aop:pointcut expression="expression=execution(* com.baidu.service..*(..))" id="poi1"/>
<!-- 定义切面: -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="poi1"/>
</aop:config>
</beans>
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/laok
jdbc.username=root
jdbc.password=root
测试方法:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class TestTrans {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.transfer();
}
}
可以看到,运行结果报错,而且数据库的值也没有变,说明操作事务成功。
在管理多方法的事务时,在beans.xml配置文件中可以这样配置,
在配置事务增强部分可以这样:
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" read-only="true" isolation="DEFAULT" timeout="-1"/>
<tx:method name="get*"/>
<tx:method name="set*"/>
<tx:method name="find*"/>
<tx:method name="update*"/>
</tx:attributes>
</tx:advice>
get*、set*、find*、update*等等,每个名词后面的*代表了以get、set等等名词开头的所有相关方法, 统一进行事务处理,但要注意其他属性配置,列表展示等功能要只读,其他修改功能要可读可写
使用注解配置声明事务:
beans.xml:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入第三方 properties配置 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<context:annotation-config/>
<context:component-scan base-package="com.baidu.service,com.baidu.dao"></context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- ${key} 可以读取properties文件中配置 key对应value -->
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器
id:不建议修改,就叫transactionManager
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解的事务配置 -->
<tx:annotation-driven/>
</bean>
可以对某个方法进行事务管理:
UserService.java:
@Service
public class UserService {
@Autowired
private UserDaoI userDao=new UserDaoImpl();
@Transactional(propagation=Propagation.REQUIRED,readOnly=false,timeout=-1,isolation=Isolation.DEFAULT)
public void transfer(){
userDao.inMoney("张三", 10);
int i=1/0;
userDao.outMoney("李四", 10);
}
}
可以对该类中所有方法进行事务管理:
UserService.java:
@Service
@Transactional//如果将注解放置到类级别,那么代表所有的方法都要加事务的
public class UserService {
@Autowired
private UserDaoI userDao=new UserDaoImpl();
@Transactional(propagation=Propagation.REQUIRED,readOnly=false,timeout=-1,isolation=Isolation.DEFAULT)
public void transfer(){
userDao.inMoney("张三", 10);
int i=1/0;
userDao.outMoney("李四", 10);
}
}