spring 事务管理
---------------------------------------------------------------------------------------------------------------------------------------
Spring的事务管理(底层是AOP):
一、概述:Spring事务管理高层抽象主要包括3个接口
1、PlatformTransactionManager 事务管理器
2、TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
3、TransactionStatus 事务具体运行状态
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
2、TransactionDefinition 事务定义信息:
1、隔离级别:
1、DEFAULT 使用后端数据库默认的隔离级别(spring中的的选择项)
2、READ_UNCOMMITED(最差) 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
3、READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生
(默认MYSQl和Order数据库使用3和4的隔离级别)
4、REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。
5、SERIALIZABLE (最严格的,性能 差) 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
2、传播行为:
1、PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个----------》Spring默认使用
2、PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
3、PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
4、PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
5、PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
6、PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
7、PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
2、Spring的事务控制(Spring的声明式事务是通过AOP实现的IPO):
1、编程式事务控制:性能最好,最灵活,非常麻烦,在实际应用中很少使用,通过TransactionTemplate手动管理事务(高级工程师必备)
2、声明式事务控制:
1、原始型事务控制:
2、基于Tx/AOP:(多数使用):
1、基于注解
2、基于XML配置文件(代码侵入性最小)
基于Spring事务案例:
dao层:
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void inmoney(String name, Integer money) {
String sql = "update t_user set money=money+? where name=?";
jdbcTemplate.update(sql, money, name);
}
@Override
public void outmoney(String name, Integer money) {
String sql = "update t_user set money=money-? where name=?";
jdbcTemplate.update(sql, money, name);
}
}
service层:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void zhuangzhang() {
userDao.outmoney("aa",10);
int i=1/0;
userDao.inmoney("bb",10);
}
}
测试类:
@Component
public class TestAdd {
@Test
public void say(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");
UserService userService=applicationContext.getBean(UserService.class);
userService.zhuangzhang();
}
}
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">
<!--Spring自动代理-->
<context:annotation-config></context:annotation-config>
<!--开启Spring扫描-->
<context:component-scan base-package="com.zltz"></context:component-scan>
<!--加载properties数据库信息文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--获取dataSource数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.username}"></property>
</bean>
<!--添加dataSource数据源给对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--定义事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--AOP相关信息 tx(增强)-->
<tx:advice id="interceptor" transaction-manager="transactionManager">
<tx:attributes>
<!--事务的定义信息-->
<tx:method name="zhuangzhang" propagation="REQUIRED"/>
<tx:method>标签属性设置:
0、name="zhuangzhang*": 进行事务控制的切入点的方法(对那个方法增强),如果有*号,比如说这个,就是在切入点中只要以zhuangzhang方法名开头的都进行事务控制。
1、propagation="REQUIRED":传播行为
2、isolation="DEFAULT": 隔离级别
3、read-only="false": 是否只读
4、rollback-for="Exception":默认就是Exception出现任何异常都回滚
5、timeout="-1": 超时时间
注意:此处的超时时间的设置-1为永不超时,面试时问,如果超时时间为30那么多少时间超时?答:永不超时,所以超时时间属性不管设置多少都是永不超时,默认写-1。
</tx:attributes>
</tx:advice>
<aop:config>
<!--切入点配置-->
<aop:pointcut id="pois" expression="execution(* com.zltz.service..*(..))"></aop:pointcut>
<aop:advisor advice-ref="interceptor" pointcut-ref="pois"></aop:advisor>
</aop:config>
</beans>
使用注解配置:
xml中写入tx注解扫描:
<tx:annotation-driven></tx:annotation-driven>
在需要事务控制的方法上加注解:
@Transactional注解
注意:在处理以上程序案例时以上代码没有错误,能跑通(毕竟比较简单),但是出错后数据库数据依旧修改,
起初怀疑是切面没有切入成功,但是数据库第一条SQL能修改数据第二条位执行说明已经切入了,
就怀疑是Spring事务没有回滚,后来发现Spring事务正常回滚了,仔细研究了几个小时发现错出在了数据库上(还是基础不牢固),
我用的数据库管理工具数SQLyong,在建表时有时候默认的引擎是MyIsAM模式。
这种模式下不支持事务,得修改为InnoDB模式才可以,真是大意失荆州啊(也是学的不够扎实造成的),
希望大家不要这样的犯错误,耽误了宝贵的学习时间。