java实训笔记_Spring的事务

1 前言

上一篇写了Spring的AOP
这一篇写一下Spring的事务

2 事务回顾

首先先回顾以下数据库中事务的相关概念

2.1 事务的概念

事务指逻辑上的一组操作,这组操作要么全部成功,要么全部失败。

2.2 事务的特性

事务有四个特性:原子性、一致性、隔离性、持久性

  • 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
  • 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。
  • 隔离性(Isolation):多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durability):指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。

四个特性首字母合在一起就是 ACID,事务的四个特性。

2.3 事务的隔离问题

如果事务不考虑隔离性,可能会引发如下问题:

2.3.1 脏读

脏读:指一个事务读取了另外一个事务未提交的数据。
例如A有100元,B有100元,此时A向B转100元,在未完成的时候,A又向C转了100元,系统读取了转账前的数值,判定可以转,完成两笔交易以后,A就会是-100元,正常情况下银行卡不应该出现负数金额,所以就是出问题了。
tips:信用卡可以出现负数金额,见参考链接4

2.3.2 不可重复读

不可重复读:指在一个事务内,读取表中的某一行数据,多次读取结果不同。
不可重复读和脏读的区别是:脏读是读取了前一事务未提交的数据,不可重复读是读取了前一事务已提交的数据。

2.3.3 虚读(幻读)

虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一样。

2.4 隔离级别

MySQL数据库定义了四种隔离级别:

  1. 串行化(Serializable):可避免脏读、不可重复读、虚读情况的发生。
  2. 可重复读(Repeatable):可避免脏读、不可重复读情况的发生。(MySQL默认级别)
  3. 读已提交(Read committed):可避免脏读情况发生。
  4. 读未提交(Read uncommitted):最低级别,以上情况均无法保证。

3 Spring中的事务

Spring中可以通过XML实现事务,也可以通过注解实现事务,首先介绍两个通用的东西,然后介绍具体实现。
PROPAGATION和隔离级别可以在下图的类中找到,spring-tx需要在pom.xml中导入。
在这里插入图片描述

3.1 事务配置

Spring中有七种PROPAGATION值,PROPAGATION的值指明了对方法要怎么使用事务

  1. PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。(默认)
  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务的方式执行。
  3. PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务

3.2 隔离级别

隔离级别有五个值

  1. ISOLATION_DEFAULT:使用数据库默认的隔离级别
  2. ISOLATION_READ_UNCOMMITTED:读未提交
  3. ISOLATION_READ_COMMITTED:读已提交
  4. ISOLATION_REPEATABLE:可重复读
  5. ISOLATION_SERIALIZABLE:串行化

3.3 导入依赖、连接数据库

不管基于什么实现,都需要导入spring事务相关的依赖,在pom.xml导入下列依赖,放在<dependencies></dependencies>标签中

		<!-- Spring 事务 -->
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>5.2.7.RELEASE</version>
		</dependency>
		<!-- MySQl connector -->
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.40</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.2.7.RELEASE</version>
		</dependency>

在springmvc的配置文件中连接数据库,使用的是mysql5,mysql8需要修改连接语句和使用的驱动。

<!-- Spring事务 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<!-- 驱动类 -->
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
	<!-- 连接数据库 -->
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm"></property>
	<!-- 用户名 -->
	<property name="user" value="root"></property>
	<!-- 密码 -->
	<property name="password" value=""></property>
</bean>

3.4 基于XML实现的事务

3.4.1 无事务实现

数据库操作类AccountDaoImpl

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import edu.spring.dao.AccountDao;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
	@Override
	public void set1() {
		this.getJdbcTemplate().update("update account set name = 'aa' where id = 1");
	}
	@Override
	public void set2() {
		this.getJdbcTemplate().update("update account set name = 'ba' where id = 2");
	}
}

服务类AccountServiceImpl

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import edu.spring.dao.AccountDao;
import edu.spring.service.AccountService;
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class AccountServiceImpl  implements AccountService{
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void changeNames() {
		accountDao.set1();
//		int i=1/0;
		accountDao.set2();	
		System.out.println("ok");
	}
}

在spring配置文件中引入aop、tx

<?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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

</beans>

在sping配置文件中注册

<!-- 数据库操作 -->
<bean id="accountDao" class="edu.spring.dao.impl.AccountDaoImpl"> 
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 对象 -->
<bean id="accountService" class="edu.spring.service.impl.AccountServiceImpl">
	<property name="accountDao" ref="accountDao"></property>
</bean>

使用getBean获取并调用其方法,注意bean的名称。

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("tranConfig.xml");
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.changeNames();

3.4.2 加入事务

<!-- 开启/注册事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 自定义事务通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
		<tx:method name="changeNames" propagation="REQUIRED" isolation="DEFAULT"/>
	</tx:attributes>
</tx:advice>
<!-- 事务的aop -->
<aop:config>
	<aop:advisor advice-ref="txAdvice" pointcut="execution(* edu.spring.service.*.*(..))"/>
</aop:config>

要注意:

  1. id引用是否填写正确
  2. 方法名是否和类中定义的相同
  3. propagation和isolation的值是否符合需求

开启后可以将accountServiceImpl中注释的除零错误取消,看看是否是以事务的状态执行。
之所以用到aop是因为事务是使用aop实现的。

3.5 基于注解实现的事务

在spring的配置文件中引入tx

<?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: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-4.3.xsd">

</beans>

还是之前的类和数据库,需要在AccountServiceImpl中加入注解,在类名的上一行

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import edu.spring.dao.AccountDao;
import edu.spring.service.AccountService;
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class AccountServiceImpl  implements AccountService{
	private AccountDao accountDao;	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void changeNames() {
		accountDao.set1();
//		int i=1/0;
		accountDao.set2();
		System.out.println("ok");
	}
}

在配置文件中注册并加入事务、注解。

<!-- Spring事务 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm"></property>
	<property name="user" value="root"></property>
	<property name="password" value=""></property>
</bean>
<bean id="accountDao" class="edu.spring.dao.impl.AccountDaoImpl"> 
	<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="edu.spring.service.impl.AccountServiceImpl">
	<property name="accountDao" ref="accountDao"></property>
</bean>

<!-- 开启/注册事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解 -->
<tx:annotation-driven/>

4 结语

1.csdn写博客的时候,使用<font color=""></font>可以修改标签内字体的颜色。具体见参考链接3.
2.本文仅演示了对数据的操作,在service中调用dao的方法,并不涉及account的实现,需要现在数据库中有相关数据才行。
3.事务配置还有只读、超时和回滚规则,具体见参考链接5。
4.本文没有给出接口的内容,需要自己实现。

5 参考链接

1.事务的概念
2.Spring中propagation的7种事务配置
3.CSDN编辑博客时,文字大小、颜色、字体的处理
4.银行卡余额是负数是什么情况?
5.spring 中 isolation 和 propagation 详解
6.事务跟springAOP有什么关系呀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值