Spring配置事务
事务
事务特性:ACID
- 原子性:整体 【原子性是指事务包含的所有操作要么全部成功,要么全部失败】
- 一致性:数据【一个事务执行之前和执行之后都必须处于一致性状态】
- 隔离性:并发【对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。】
- 持久性:结果 【持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的】
隔离问题
- 脏读:一个事务读到另一个事务未提交的内容【读取未提交内容】
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。 - 不可重复读:一个事务读到另一个事务已提交的内容(insert)【读取提交内容】
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。 - 虚读(幻读):一个事务读到另一个事务已提交的内容(update)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。 - Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
隔离级别–解决问题
- read uncommittd,读未提交。存在3个问题。
- read committed,读已提交。解决:脏读。存在2个问题。
- repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
- serializable,串行化。单事务。没有问题。
spring-tx.jar中的接口
- PlatformTransactionManager:
平台事务管理器,spring要管理事务,必须使用事务管理器,进行事务配置时,必须配置事务管理器 - TransactionDefinition:
事务详情(事务定义、事务属性),spring用于确定事务具体详情,
例如:隔离级别(isolation)、是否只读、超时时间 、传播行为(propagation)等
进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。 - TransactionStatus:
事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
spring底层根据状态进行相应操作。
事务的传播行为
- 传播行为:在两个业务之间如何共享事务
传播行为 描述 PROPAGATION_REQUIRED required , 必须 【默认值】 支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将创建一个新的事务。 PROPAGATION_SUPPORTSsupports ,支持 支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将以非事务执行。 PROPAGATION_MANDATORY mandatory ,强制 支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将抛异常。 PROPAGATION_REQUIRES_NEW requires_new ,必须新的 如果A有事务,将A的事务挂起,B创建一个新的事务 如果A没有事务,B创建一个新的事务 PROPAGATION_NOT_SUPPORTED not_supported ,不支持 如果A有事务,将A的事务挂起,B将以非事务执行 如果A没有事务,B将以非事务执行 PROPAGATION_NEVER never,从不 如果A有事务,B将抛异常,如果A没有事务,B将以非事务执行 PROPAGATION_NESTED nested ,嵌套 A和B底层采用保存点机制,形成嵌套事务。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
applicationContext
<?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">
<!--引入数据库连接池配置文件-->
<context:property-placeholder location="com/hlk/conf/druid.properties"/>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置dao-->
<bean id="userDao" class="com.hlk.dao.UserDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置service-->
<bean id="userService" class="com.hlk.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="tranfer" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--Aop配置事务-->
<aop:config>
<!--execution(* com.hlk.service.*.*(..))配置到service下的所有切入点-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hlk.service.*.*(..))"></aop:advisor>
</aop:config>
</beans>
UserService
public class UserService {
private UserDao userDao;
/**
* 方便spring注入
*
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void tranfer(String from, String to, double money) {
userDao.out(from, money);
int i = 10 / 0;
userDao.in(to, money);
}
}
UserDao
public class UserDao extends JdbcDaoSupport {
public void in(String name, double money) {
String sql = "update account set money = money + ? where username = ?";
getJdbcTemplate().update(sql, money, name);
}
public void out(String name, double money) {
String sql = "update account set money = money - ? where username = ?";
getJdbcTemplate().update(sql, money, name);
}
}
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("com/hlk/conf/applicationContext2.xml");
UserService service = (UserService) context.getBean("userService");
service.tranfer("jack","rose",100);
}