Spring(六)声明式事务
在数据访问层,我们经常会使用到事务操作,事务操作都是开启事务,commit和rollback,AOP的本质就是将重复的代码从业务逻辑层独立出来,从而降低代码之间的耦合度。
Spring的确实现了一套事务处理(基于AOP)。
既然涉及到了事务,那么肯定就需要MyBatis,而Spring又是一个容器,所以可以将MyBatis中的核心对象交给Spring来管理。
事务的方式有两种:
- 编码式事务:纯手动、使用事务模板;
- 声明式事务:本篇文章所介绍的。
若想了解编码式事务中的纯手动事务和事务模板,就可以不必往下看了,本篇文章没有涉及。
1.注解配置
jar包
既需要Spring的,也需要MyBatis和JDBC的。
与事务相关的需要spring-tx和aspectjweaver。
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aop</artifactid>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupid>javax.annotation</groupid>
<artifactid>javax.annotation-api</artifactid>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aspects</artifactid>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-test</artifactid>
<version>5.3.3</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupid>org.mybatis</groupid>
<artifactid>mybatis</artifactid>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupid>org.mybatis</groupid>
<artifactid>mybatis-spring</artifactid>
<version>2.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
<version>8.0.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-jdbc</artifactid>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-tx</artifactid>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupid>org.aspectj</groupid>
<artifactid>aspectjweaver</artifactid>
<version>1.9.5</version>
</dependency>
</dependencies>
配置文件
首先需要准备好数据库及表。
jdbc.properties :
driver = com.mysql.jdbc.Driver
url = jdbc:mysql:///mybatisdb?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
usr = root
password = root
applicationContext.xml :
<!--?xml version="1.0" encoding="UTF-8"?-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 涉及到mybatis,需要数据源-->
<!-- 涉及到参数,需要引入属性文件-->
<context:property-placeholder location="classpath:jdbc.properties">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}">
<property name="url" value="${url}">
<property name="username" value="${usr}">
<property name="password" value="${password}">
</property></property></property></property></bean>
<!-- 配置SqlSessionFactory-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">
</property></bean>
<!-- 扫描mapper-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dh.mapper">
</property></bean>
<!-- 开启扫描注解-->
<context:component-scan base-package="com.dh.service">
<!-- 添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</property></bean>
<!-- 开启注解事务管理-->
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven></context:component-scan></context:property-placeholder></beans>
ProductsMapper.xml :
<!--?xml version="1.0" encoding="UTF-8" ?-->
<mapper namespace="com.dh.mapper.ProductsMapper">
<select id="selectById" parametertype="int" resulttype="com.dh.pojos.Products">
select * from products where pid = #{pid};
</select>
<update id="updateById" parametertype="com.dh.pojos.Products">
update products set pname = #{pname} where pid = #{pid};
</update>
</mapper>
ProductsMapper接口 :
package com.dh.mapper;
import com.dh.pojos.Products;
public interface ProductsMapper {
public Products selectById(int pid);
public int updateById(Products products);
}
ProductsService :
package com.dh.service;
import com.dh.mapper.ProductsMapper;
import com.dh.pojos.Products;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductsService {
@Autowired
private ProductsMapper mapper;
@Transactional
public void TransactionTest(){
Products product = mapper.selectById(1);
product.setPname("111");
int i = 1/0;
mapper.updateById(product);
}
}
测试:
import com.dh.service.ProductsService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest01 {
@Autowired
private ProductsService productsService;
@Test
public void test01(){
productsService.TransactionTest();
}
}
结果:发生了异常,事务回滚,没有更新成功。
总结:在配置好mybatis相关内容后,只需要在xml中配置好事务管理器,以及开启注解扫描,然后在所需要开启事务的方法上加上@Transactional注解即可。
2.xml配置
只需要将加了注释的那一行的内容换成下列内容即可,然后去掉方法上的@Transactional注解。
<!-- 添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</property></bean>
<!-- <tx:annotation-driven transaction-manager="transactionManager"/>-->
<!-- 事务通知-->
<tx:advice transaction-manager="transactionManager" id="interceptor">
<!-- 还可以设置事务的其它属性,如隔离级别-->
<tx:attributes>
<tx:method name="*" read-only="false">
</tx:method></tx:attributes>
</tx:advice>
<!-- 切点信息-->
<aop:config>
<!-- 指定切点-->
<aop:pointcut id="pointCut" expression="execution(* com.dh.service.*.*(..))">
<!-- 切面信息-->
<aop:advisor advice-ref="interceptor" pointcut-ref="pointCut">
</aop:advisor></aop:pointcut></aop:config>
3.事务的传播行为
事务具有隔离级别和传播行为。
在mysql中已经提过了事务的隔离级别,在此处不做过多的赘述。
以下为事务支持的传播行为。