【Spring】12 - Spring 中的事务控制
2020-10-26 本文已影响0人
itlu
1. Spring
事务控制我们要明确的
-
第一:
JavaEE
体系进行分层开发,事务处理位于业务层,Spring
提供了分层设计业务层
的事务处理解决方案。 -
第二:
spring
框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar
中。 -
第三:
spring
的事务控制都是基于AOP
的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现
。
2. Spring 中事务控制的 API 介绍
2.1 PlatformTransactionManager
接口
- 此接口是
spring
的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
-
我们在开发中都是使用它的实现类。
-
真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Spring JDBC
或Batis
进行持久化数据时使用。org.springframework.orm.hibernate5.HibernateTransactionManager
使用
Hibernate
版本进行持久化数据时使用。
2.2 TransactionDefinition
- 它是事务的定义信息对象,里面有如下方法:
2.2.1 事务的隔离级别
事务的隔离级别2.2.2 事务的传播行为
-
REQUIRED
:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)。 -
SUPPORTS
:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)。 -
MANDATORY
:使用当前的事务,如果当前没有事务,就抛出异常。 -
REQUERS_NEW
:新建事务,如果当前在事务中,把当前事务挂起。 -
NOT_SUPPORTED
:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 -
NEVER
:以非事务方式运行,如果当前存在事务,抛出异常。 -
NESTED:
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED
类似的操作。
2.2.3 超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
2.2.4 是否是只读事务
建议查询时设置为只读。
2.3 TransactionStatus
- 此接口提供的是事务具体的运行状态,方法介绍如下图:
3. Spring 中的事务控制环境搭建
3.1 创建项目并导入依赖
-
创建一个不使用骨架的
Maven
项目 。 -
导入依赖 :
Spring
的 事务控制需要AOP
的支持
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- Spring整合Junit单元测试jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 检查切入点表达式的jar spring 的事务控制需要AOP的支持 -->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
<scope>runtime</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
3.2 继续使用 Spring_JdbcTemplate
中的代码
-
demo
地址 : Spring 中的 JdbcTemplate
3.3 创建业务层接口及实现类
-
AccountService
业务层接口:
public interface AccountService {
/**
* 根据id查询账户信息
*
* @param id
* @return
*/
Account findById(Integer id);
/**
* 根据name查询账户信息
*
* @param name
* @return
*/
Account findByName(String name);
/**
* 更新账户信息
*
* @param account
*/
void update(Account account);
/**
* 提供 transfer zhuanZhang 的方法
* @param sourceName
* @param targetName
* @param money
*/
void transfer(String sourceName , String targetName , Float money);
}
-
AccountServiceImpl
业务层实现类 :
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
/**
* 提供 setter 方法进行 为其提供注入
*
* @param accountDao
*/
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account findById(Integer id) {
return accountDao.findById(id);
}
public Account findByName(String name) {
return accountDao.findByName(name);
}
public void update(Account account) {
accountDao.update(account);
}
public void transfer(String sourceName, String targetName, Float money) {
Account source = accountDao.findByName(sourceName);
Account target = accountDao.findByName(targetName);
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
accountDao.update(source);
accountDao.update(target);
}
}
3.4 配置 bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"/>
</bean>
<bean id="accountService" class="com.lyp.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean id="accountDao" class="com.lyp.dao.impl.AccountDaoImpl">
<!--<property name="jt" ref="jdbcTemplate"/>-->
<property name="dataSource" ref="datasource"/>
</bean>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
3.5 编写测试类进行测试 :
- 使用
Spring
整合 Junit 的方式 ,这里需要注意我们使用的Junit
版本需要是4.12
之后的 。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class SpringTxTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById() {
Account account = accountService.findById(1);
System.out.println(account);
}
@Test
public void testTransfer() {
accountService.transfer("aaa","bbb",100.f);
}
}
4. 使用基于 xml
的方式配置Spring
的声明式事务控制
- 导入事务和
aop
的约束 :
<?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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
4.1 Spring
中基于 xml
的声明式事务控制的步骤
- 配置事务管理器 :
<!-- 1. 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
- 配置事务的通知(
tx:advice标签
) : 此时需要导入事务和aop
的约束。使用tx:advice标签 配置事务通知。 属性如下:
id
: 为事务通知起一个唯一的标识。transaction-manager
:给事务提供一个事务管理器的引用。
<!-- 2. 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
</tx:advice>
- 配置
aop
:
<!-- 3. 配置aop -->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
</aop:config>
- 建立通知和切入点表达式的对应关系: 使用
aop:advice
标签建立与事务通知的关系。
<!-- 3. 配置aop -->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
<!-- 4. 建立切入点表达式与通知之间的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
- 配置事务的属性:是在事务的通知
tx:advice
标签内部进行配置:使用tx:attributes
和tx:method
标签
isolation
: 用于指定事务的隔离级别。 默认的是DEFAULT
,表示使用的是数据库的默认隔离级别。propagation
:用于指定事务的传播行为,默认值是REQUIRED
,表示一定会有事务。增删改选择它,查询应选择SUPPORTS
。read-only
:用于指定事务是否只读,只有查询方法才设置为true,默认值是false,表示读写。timeout
:用于指定事务的超时时间。默认值是 -1 表示永不超时如果指定数值,其单位是秒。rollback-for
: 用于指定一个异常当产生异常时,事务回滚,产生其他异常时不会滚,没有默认值,表示任何异常都回滚no-rollback-for
:用于指定一个异常当产生该异常的时候,事务不回滚,产生其他异常的时候事务回滚,没有默认值,表示任何异常都回滚。
<!-- 5. 配置事务的属性 -->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
-
Spring
中基于xml
的声明式事务控制代码如下 :
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 1. 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!-- 2. 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 5. 配置事务的属性 -->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 3. 配置aop -->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
<!-- 4. 建立切入点表达式与通知之间的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
- demo地址: spring中基于xml的声明式事务控制
5. Spring
基于注解的声明式事务控制
-
在上面的项目的基础上进行修改。
-
主要修改
bean.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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lyp"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"/>
</bean>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 1. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!-- 开启spring对注解事务的支持 -->
<tx:annotation-driven/>
</beans>
- 只需在业务层实现类上面加上注解
@Transactional
即可实现注解配置事务的控制。
6. Spring
基于纯注解
的声明式事务控制
- 在上面的项目的基础上进行修改;
- 创建一个配置类
SpringConfiguration
用于代替bean.xml
的作用:
@Configuration
@ComponentScan("com.lyp")
public class SpringConfiguration {
}
- 创建一个
JdbcConfig
类用于配置JdbcTemplate
和DataSource
对象 :
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
- 创建一个事务管理器配置类 :
public class TransactionManagerConfig {
@Bean
public PlatformTransactionManager createTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
- 创建
jdbcConfig.properties
配置文件 :
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
- 修改主配置文件内容 :
@Configuration
@ComponentScan("com.lyp")
@Import(value = {JdbcConfig.class, TransactionManagerConfig.class})
@PropertySource("JdbcConfig.properties")
@EnableTransactionManagement
public class SpringConfiguration {
}
- 修改测试文件中的代码并进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringTxTest {....}
- demo地址: spring基于纯注解的事务控制配置。