Spring
Spring中的事务操作
事务的概念
- 什么是事务:事务是数据库操作中最基本的操作单元。事务一般包含一组多个操作,要么所有的操作都成功,事务成功提交执行。要么但凡有一个操作失败,整个事务操作失败回滚。
- 事务的四个特性:ACID,也即原子性,一致性,隔离性,持久性
事务操作环境的搭建
在entity包中创建User类(包含userId和account属性即可),在Dao层创建UserDao的操作数据库方法,分别为addMoney和reduceMoney,在service层中声明UserService,其中创建转账方法,同时调用Dao层的两个方法,作为事务的两个操作。这样事务就创建成功了。
Spring事务管理介绍
- 事务是添加到JavaEE三层架构中的Service层中的(也即业务逻辑层)
- 在Spring中进行事务管理操作,有两种方式:即编程式事务管理和声明式事务管理
编程式事务管理即在业务逻辑层对事务操作使用try-catch包围,手动的对事务成功和失败进行提交或者回滚
声明式事务管理为使用注解或者配置文件来将事务提交给Spring,由Spring来管理事务的提交和回滚 - 声明式事务管理
- 基于注解方式
- 基于XML配置文件方式
- 注意,在使用声明式事务管理时,底层也是用到了AOP的原理。即将整个事务的操作作为切入点,切面由Spring来配置处理,完成事务的提交和回滚
- Spring事务管理的API
- PaltformTransactionManager,提供一个接口代表事务管理器,根据不同的框架及不同的实现方法提供不同的实现类
- DatasourceTransactionManager,使用datasource来实现数据库连接及操作的实现类
注解声明式事务管理
- 在Spring配置文件中配置事务管理器
<bean id="dataSource" class="...">
<property name="driverClassName" value=""></property>
<property name="username" value=""></property>
<property name="password" value=""></property>
<property name="url" value=""></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
-
在spring配置文件中开启事务注解
- 引入tx名称空间
xmlns:tx="http://www.springframework.org/schema/tx"
- 开启事务注解
<tx:annotation-dirven transactionManager="transactionManager"></tx:annotation-driven>
- 引入tx名称空间
-
在Service类(或者Service类中的方法)上面添加事务注解
- @Transactional注解,表示事务注解,这个注解可以添加到类上面也可以添加到方法上面
- 如果将该注解添加到类上面,表示类中的所有方法都会被添加事务
- 如果将该注解添加到方法上面,表示为这个方法添加事务
声明式事务管理的参数配置
在service类或其中的方法上面添加注解@Transactional,可以配置事务的相关参数,具体参数如下:
propagation
表示事务传播行为,指的是:多个事务方法之间进行调用时,这个过程中事务是如何进行管理的
class ServiceA(){
@Transactional
public void methodA(){
//doSomething
methodB();
//doSomething
}
}
class ServiceB(){
@Transactional
public void methodB(){
//doSomething
}
}
- REQUIRED:如果有事务正在运行,那么当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行。 即如果methodA有事务,那么methodB就在这个事务内运行,他的操作异常会导致整个methodA事务回滚;否则methodB创建新的事务运行。
- REQUIRED_NEW:当前的方法必须启动新的事务,并在它自己的事务内运行,如果有事务正在运行,那么应该将它挂起。 无论methodA有没有事务,methodB都需要启动新的事务并在它的事务内执行操作,它内部的操作异常会导致自己的事务回滚,而根据methodA中的具体操作来决定methodA的事务是否回滚(如是否捕获异常等)。
- SUPPORTS:如果有事务正在运行,当前的方法就在这个事务内运行,否则它不可以运行在事务中。 即如果methodA运行在一个事务中,那么methodB也应该运行在这个事务中。如果methodA中没有事务,那么methodB不可以创建新的事务并在其中运行。
- NOT_SUPPORTS:当前的方法不应该出现在事务中,如果有正在运行的事务,应该将该事务挂起
- MANDATORY:当前的方法必须运行在事务内部,如果当前没有正在运行的事务,就抛出异常
- NEVER:当前方法不允许在事务内运行,如果当前有正在运行的事务,那么将抛出异常
- NESTED:如果有事务正在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务并在其自己的事务内运行。 即如果A正在运行在一个事务中,那么会开启一个嵌套事务,并且methodB应该在这个嵌套事务内运行。在嵌套事务开启时,会取得外部methodA事务的savepoint,如果method出现异常,会回滚到外部的savepoint处。
isolation
- 事务的隔离性决定着多个事务操作之间在执行时不会产生影响,因此如果不考虑隔离性会产生很多问题——脏读、不可重复读、幻读
- 脏读:一个事务读到另一个事务没有提交的(修改)数据,读到的是错误数据
- 不可重复读:一个事务读取库表中的一条数据,多次读取的结果不同(如多次读取之间有其他事务操作提交了该条数据)
- 幻读:一个事务读到另一个事务没有提交的(新增)数据,读到的是不存在的数据
- 通过设置事务隔离级别,可以解决这些问题,如:
@Transactional(propagation=Propagation.REQUIRED , isolation=Isolation.REPEATABLE_READ)
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ_UNCOMMITED | X | X | X |
READ_COMMITED | √ | X | X |
REPEATABLE_READ | √ | √ | X |
SERIALIZABLE | √ | √ | √ |
XML的声明式事务管理
- 在Spring配置文件中进行配置
- 配置事务管理器
- 配置通知
- 配置切入点和切面
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置通知 -->
<tx:advice id="txadvice">
<tx:attributes>
<tx:attribute>
<tx:method name="methodA" propagation="REQUIRED"/>
<tx:attribute>
<tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<aop:pointcut id="pt" expression="">
<aop:advisor advice-ref="exadvice" piont-ref="pt">
</aop:config>
完全注解声明式事务管理
与IOC、AOP、Template等相同,创建配置类并添加相应的注解即可。在配置类中还可以使用@Bean注解创建指定所需配置对象
@Configuration
@ComponentScan(basePackage="")
@EnableAspectJAutoProxy(proxyTargetClass=true)
@EnableTransactionManagement //开启事务
public class TxConfig{
@Bean
DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到ioc容器中根据类型找到dataSource
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
Spring新特性
待补充