spring声明式事务管理
在实际开发中,操作数据库时都会涉及到事务管理问题,为此Spring提供了专门用于事务处理的API。Spring的事务管理简化了传统的事务管理流程,并且在一定程度上减少了开发者的工作量。
一.事务管理的核心接口
1.Platform TransactionManager
PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。该接口中提供了三个事务操作的方法,具体如下:
- TransactionStatus getTransaction(TransactionDefinition definition);
- 用于获取事务以及状态信息
- void commit(TransactionStatus status);
- 用于提交事务
- void rollback(TransactionStatus status);
用于回滚事务
PlatformTransactionManager接口只是代表事务管理的接口,并不知道底层是如何管理事务的,具体如何管理事务则由它的实现类来完成。该接口常见的几个实现类如下:
- org.springframework.jdbc.datasource.DataSourceTransactionManager
- 用于配置JDBC数据源的事务管理器
- org.springframework.orm.hibernate4.HibernateTransactionManager
- 用于配置Hibernate的事务管理器
- org.springframework.transaction.jta.JtaTransactionManager
- 用于配置全局事务管理器
- tips:当底层采用不同的持久层技术时,系统只需使用不同的PlatformTransactionManager实现类即可。
2.TransactionDefinition
TransactionDefinition****接口是事务定义(描述)的对象,该对象中定义了事务基本属性,并提供了获取事务基本属性的方法,具体如下:
- String getName( ); 获取事务对象名称
- int getIsolationLevel( ); 获取事务的隔离级别
- int getPropagationBehavior( ); 获取事务的传播行为
- int getTimeout( ); 获取事务的超时时间
- boolean isReadOnly( ); 获取事务是否只读
二.事务的基本事务属性
**传播行为(propagation behavior):**当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。Spring定义了七种传播行为(Page113:表4-4)
**隔离级别(isolation level):**定义了一个事务可能受其他并发事务的影响程度。多个事务并发运行,经常会操作相同的数据来完成各自的任务,可能会出现脏读,不可重复读和幻读的问题。隔离级别有四种( Page113:表4-5 )
**是否只读(isReadOnly):**如果一个方法内都是对数据库的select操作,那么可以设置方法事务为只读,数据库也会对该事务进行特定的优化。只读事务内不能有insert、update、delete的操作
**事务超时(timeout):**事务可能设计对后端数据库的锁定,所以长时间的事务运行会不必要的占用数据库资源,设置事务超时时间可以及时释放资源
3.TransactionStatus
TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息。该接口中包含6个方法,具体如下:
- void flush(); 刷新事务
- boolean hasSavepoint(); 获取是否存在保存点
- boolean isCompleted(); 获取事务是否完成
- boolean isNewTransaction(); 获取是否为新事务
- boolean isRollbackOnly(); 获取事务是否回滚
- void setRollbackOnly(); 设置事务回滚
四.事务ACID 的 4 个重要特性:
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
其中,事务隔离又分为 4 种不同的级别,包括:
未提交读(Read uncommitted),最低的隔离级别,允许“脏读”(dirty reads),事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。
提交读(read committed),一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
可重复读(repeatable read),一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
串行化(Serializable),最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。
(此处引用链接 原文链接:https://blog.csdn.net/h295928126/article/details/125530184)
五.实例
1.创建所需要的数据库
2.引入maven配置依赖和xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入mysql配置文件-->
<context:property-placeholder location="classpath*:database.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="jt" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置JdbcTemplate和PhoneDao之间的依赖关系-->
<bean id="phoneDao" class="org.example.dao.impl.PhoneDaoImpl">
<property name="jdbcTemplate" ref="jt"/>
</bean>
<!-- 配置PhoneDao和PhoneService之间的依赖关系-->
<bean id="phoneService" class="org.example.service.impl.PhoneServiceImpl">
<property name="phoneDao" ref="phoneDao"/>
</bean>
<!-- 配置PhoneService和PhoneController之间的依赖关系-->
<bean id="phoneController" class="org.example.controller.PhoneController">
<property name="phoneSerice" ref="phoneService"></property>
</bean>
</beans>
3.创建实体类
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
4.创建dao接口和impl
package org.example.dao;
public interface AccountDao {
void inMoney(String name,Double money);
void outMOney(String name,Double money);
}
package org.example.dao.impl;
import org.example.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jt;
@Override
public void inMoney(String name,Double money) {
jt.update("update account set money = money + ? where name = ? ",money,name);
}
@Override
public void outMOney(String name,Double money) {
jt.update("update account set money =money - ? where name = ? ",money,name);
}
}
5.创建service接口和impl
package org.example.service;
public interface AccountService {
void transfer(String in,String out,Double money);
}
package org.example.service.impl;
import org.example.dao.AccountDao;
import org.example.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional
public void transfer(String in, String out, Double money) {
accountDao.outMOney(out,money);
// int i = 1/0;
accountDao.inMoney(in,money);
}
}
6.创建测试类
package org.example.service.impl;
import org.example.config.SpringConfiguration;
import org.example.service.AccountService;
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;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class AccountServiceImplTest {
@Autowired
private AccountService accountService;
@Test
public void transfer() {
accountService.transfer("张三","李四",200.00);
}
}
7.实验结果
当我们将int i=1/0;加入serviceImpl
运行结果