-
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
-
将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务
管理。
-
无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
-
就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
<bean id=“transactionManager”
class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”>
===========================================================================
- 什么是声明式事务控制?
-
Spring 的声明式事务顾名思义就是采用声明的方式来处理事务
-
这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。
Spring 声明式事务控制底层就是AOP
只要简单的加个注解(或者是xml配置)就可以实现事务控制,不需要事务控制的时候只需要去掉相应的注解即可。
- sql语句准备
CREATE DATABASE /!32312 IF NOT EXISTS/spring_db
/*!40100 DEFAULT CHARACTER SET utf8 */;
USE spring_db
;
DROP TABLE IF EXISTS account
;
CREATE TABLE account
(
id
INT(11) NOT NULL AUTO_INCREMENT,
name
VARCHAR(50) DEFAULT NULL,
money
DOUBLE DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO account
(id
,name
,money
) VALUES
(1,‘三更’,100),
(2,‘草堂’,100);
- 创建实体类
- com.sangeng.domain.Account.java
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Account {
private Integer id;
private String name;
private Double money;
}
- 数据持久层接口和对应xml文件
- com.sangeng.dao.AccountDao
public interface AccountDao {
public void updateMoney(@Param(“id”) Integer id, @Param(“updatemoney”) Double updatemoney);
}
resourecs/com/sangeng/dao.AccountDao.xml
<?xml version="1.0" encoding="UTF-8" ?>update account set money = money + #{updatemoney} where id = #{id}
- 业务层接口和实现类
-
com.sangeng.service.AccountService
-
com.sangeng.service.impl.AccounttServiceImpl
public interface AccountService {
/**
-
转账
-
@param outId 转出账户的id
-
@param inId 转入账户的id
-
@param money 转账金额
*/
public void transfer(Integer outId,Integer inId,Double money);
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(Integer outId, Integer inId, Double money) {
//增加
accountDao.updateMoney(inId,money);
//减少
accountDao.updateMoney(outId,-money);
}
}
- spring整合mybatis
<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”
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">
<context:component-scan base-package=“com.sangeng”></context:component-scan>
<context:property-placeholder location=“classpath:jdbc.properties”></context:property-placeholder>
- mybatis-config.xml
- 测试类
- test.java.com.sangeng.AccountTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = “classpath:applicationContext.xml”)
public class AccountTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer(){
accountService.transfer(1,2,new Double(10));
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(Integer outId, Integer inId, Double money) {
//增加
accountDao.updateMoney(inId,money);
int i = 1/0; //手动制造错误
//减少
accountDao.updateMoney(outId,-money);
}
}
如果我们在业务层实现类出现错误,那么就会造成转入钱增加,但是因为执行不到后续代码,会造成转出的钱没有减少,解决方法就是在业务层实现类增加事务。
4.2.1、导入依赖
因为声明式事务底层是通过AOP实现的,所以最好把AOP相关依赖都加上。
org.aspectj
aspectjweaver
1.9.6
4.2.2、配置事务管理器和事务注解驱动
在spring的配置文件中添加如下配置:
<tx:annotation-driven transaction-manager=“txManager”/>
4.2.2、添加@Transactional注解
在需要进行事务控制的方法或者类上添加@Transactional
注解就可以实现事务控制。
-
类上:如果加在类上,这个类的所有方法都会受事务控制
-
方法上:如果加在方法上,就是那一个方法受事务控制
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional
@Override
public void transfer(Integer outId, Integer inId, Double money) {
//增加
accountDao.updateMoney(inId,money);
int i = 1/0; //手动制造错误
//减少
accountDao.updateMoney(outId,-money);
}
}
注意:一般@Transactional
注解都是加在 service 层
4.3.1、导入依赖
因为声明式事务底层是通过AOP实现的,所以最好把AOP相关依赖都加上。
org.aspectj
aspectjweaver
1.9.6
4.3.2、配置事务管理器
4.3.3、配置事务切面
- 导入tx命名空间
<tx:advice transaction-manager=“txManager” id=“txAdvice”>
tx:attributes
<tx:method name=“transfer”/>
</tx:attributes>
</tx:advice>
aop:config
<aop:pointcut id=“pt” expression=“execution(* com.sangeng.service….(…))”></aop:pointcut>
<aop:advisor advice-ref=“txAdvice” pointcut-ref=“pt”></aop:advisor>
</aop:config>
4.4.1、tx:advice
-
名称:
tx:advice
-
类型:标签
-
归属:beans标签
-
作用:专用于声明事务通知
-
格式:
<tx:advice id=“txAdvice” transaction-manager=“txManager”>
</tx:advice>
-
基本属性:
-
id
:用于配置aop时指定通知器的id -
transaction-manager
:指定事务管理器bean
4.4.2、tx:attributes
-
名称:
tx:attributes
-
类型:标签
-
归属:tx:advice标签
-
作用:定义通知属性
-
格式:
<tx:advice id=“txAdvice” transaction-manager=“txManager”>
tx:attributes
</tx:attributes>
</tx:advice>
- 基本属性
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
:
- 无
4.4.3、tx:method
-
名称:
tx:method
-
类型:标签
-
归属:tx:attribute标签
-
作用:设置具体的事务属性
-
格式:
tx:attributes
<tx:method name="*" read-only=“false” />
<tx:method name=“get*” read-only=“true” />
</tx:attributes>
- 说明:
通常事务属性会配置多个,包含1个读写的全事务属性,1个只读的查询类事务属性
- ·
tx:method
属性
4.4.4、aop:advice与aop:advisor区别
-
aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系
-
aop:advisor配置的通知类必须实现通知接口
4.5.1、事务传播行为propagation
当事务方法嵌套调用时,需要控制是否开启新事务,可以使用事务传播行为来控制。
测试案例:
@Service
public class TestServiceImpl {
@Autowired
AccountService accountService;
@Transactional
public void test(){
accountService.transfer(1,2,10);
accountService.log();
}
}
public class AccountServiceImpl implements AccountService {
//…省略其他不相关代码
@Transactional
public void log() {
System.out.println(“打印日志”);
int i = 1/0;
}
}
例如上述代码,我们转账方法 transfer 加上了事务控制注解,日志方法 log 也加上了事务控制注解,那如果我们转账方法成功运行,但是在日志方法出现错误,那么不光回滚日志方法,也会回滚转账方法。这就是事务方法嵌套使用时,我们需要用事务传播行为来控制是否需要开启新事务。
- 事务传播行为
propagation
| 属性值 | 行为 |
| — | — |
| REQUIRED(必须要有)默认值 | 外层方法有事务,内层方法就加入。外层没有,内层就新建 |
| REQUIRES_NEW(必须要有新事务) | 外层方法有事务,内层方法新建。外层没有,内层也新建 |
| SUPPORTS(支持有) | 外层方法有事务,内层方法就加入。外层没有,内层就也没有 |
| NOT_SUPPORTED(支持没有) | 外层方法有事务,内层方法没有。外层没有,内层也没有 |
| MANDATORY(强制要求外层有) | 外层方法有事务,内层方法加入。外层没有。内层就报错 |
| NEVER(绝不允许有) | 外层方法有事务,内层方法就报错。外层没有。内层就也没有 |
例如:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void transfer(Integer outId, Integer inId, Double money) {
//增加
accoutDao.updateMoney(inId,money);
//减少
accoutDao.updateMoney(outId,-money);
}
4.5.2、隔离级别isolation
Isolation.DEFAULT 使用数据库默认隔离级别
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)
public void transfer(Integer outId, Integer inId, Double money) {
//增加
accoutDao.updateMoney(inId,money);
//减少
accoutDao.updateMoney(outId,-money);
}
4.5.3、只读readOnly
如果事务中的操作都是读操作,没涉及到对数据的写操作可以设置readOnly为true。这样可以提高效率。
@Transactional(readOnly = true)
public void log() {
System.out.println(“打印日志”);
int i = 1/0;
}