014_Spring事务

一. 事务

1. 什么事务?

1.1. 事务: 逻辑上的一组操作, 组成这组操作的各个单元, 要么全都成功, 要么全都失败。

2. 事务的特性

2.1. 原子性: 事务不可分割。

2.2. 一致性: 事务执行前后数据完整性保持一致。

2.3. 隔离性: 一个事务的执行不应该受到其他事务的干扰。

2.4. 持久性: 一旦事务结束, 数据就持久化到数据库。

3. 如果不考虑隔离性引发安全性问题

3.1. 读问题

3.1.1. 脏读: 一个事务读到另一个事务未提交的数据。

3.1.2. 不可重复读: 一个事务读到另一个事务已经提交的update的数据, 导致一个事务中多次查询结果不一致。

3.1.3. 虚读、幻读: 一个事务读到另一个事务已经提交的insert的数据, 导致一个事务中多次查询结果不一致。

3.2. 写问题

3.2.1. 丢失更新。

4. 设置事务的隔离级别解决读问题

4.1. Read uncommitted: 未提交读, 任何读问题解决不了。

4.2. Read committed: 已提交读, 解决脏读, 但是不可重复读和虚读有可能发生。

4.3. Repeatable read: 重复读, 解决脏读不可重复读和幻读(MySQL8中)

4.4. Serializable: 解决所有读问题, 同时似乎给整张表添加了一个锁, 客户并发读, 但不能并发写。

二. Spring的事务管理的API

1. PlatformTransactionManager平台事务管理器接口

1.1. DataSourceTransactionManager: 底层使用JDBC管理事务。

1.2. HibernateTransactionManager: 底层使用Hibernate管理事务。

2. TransactionDefinition事务定义信息

2.1. 事务定义: 用于定义事务的相关的信息, 隔离级别、超时信息、传播行为、是否只读(只读的时候不能写)。

3. TransactionStatus事务的状态

3.1. 事务状态: 用于记录在事务管理过程中, 事务的状态的对象。

4. 事务管理的API的关系

4.1. Spring进行事务管理的时候, 首先平台事务管理器根据事务定义信息进行事务的管理, 在事务管理过程中, 产生各种状态, 将这些状态的信息记录到事务状态的对象中。

5. Spring中提供了七种事务的传播行为:

5.1. 保证多个操作在同一个事务中

5.1.1. PROPAGATION_REQUIRED: 默认值, 如果A中有事务, 使用A中的事务, 如果A没有, 创建一个新的事务, 将操作包含进来。

5.1.2. PROPAGATION_SUPPORTS: 支持事务, 如果A中有事务, 使用A中的事务。如果A没有事务, 不使用事务。

5.1.3. PROPAGATION_MANDATORY: 如果A中有事务, 使用A中的事务。如果A没有事务, 抛出异常。

5.2. 保证多个操作不在同一个事务中

5.2.1. PROPAGATION_REQUIRES_NEW: 如果A中有事务, 将A的事务挂起(暂停), 创建新事务, 只包含自身操作。如果A中没有事务, 创建一个新事务, 包含自身操作。

5.2.2. PROPAGATION_NOT_SUPPORTED: 如果A中有事务, 将A的事务挂起。不使用事务管理。

5.2.3. PROPAGATION_NEVER: 如果A中有事务, 报异常。

5.3. 嵌套式事务

5.3.1. PROPAGATION_NESTED: 嵌套事务, 如果A中有事务, 按照A的事务执行, 执行完成后,设置一个保存点, 执行B中的操作, 如果没有异常, 执行通过, 如果有异常, 可以选择回滚到最初始位置, 也可以回滚到保存点。

三. 编程式事务

1. 配置平台事务管理器

2. Spring提供了事务管理的模板类

3. 在业务层注入事务管理的模板

4. 编写事务管理的代码

5. 例子

5.1. 新建一个名为SpringJdbcProgramTx的Java工程, 拷入相关jar包

5.2. 新建一个Account.java

package com.lywgames.tx;

import java.io.Serializable;

public class Account implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer id;
	private String name;
	private Float 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 Float getMoney() {
		return money;
	}

	public void setMoney(Float money) {
		this.money = money;
	}

	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	}

}

5.3. 新建一个AccountDao.java数据库操作接口

package com.lywgames.tx;

/**
 * 转账的Dao的接口
 */
public interface AccountDao {
	public void outMoney(String from, Float money);

	public void inMoney(String to, Float money);
}

5.4. 新建一个AccountDaoImpl.java数据库操作实现类

package com.lywgames.tx;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * 转账的Dao的实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void outMoney(String from, Float money) {
		getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);
	}

	@Override
	public void inMoney(String to, Float money) {
		getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
	}

}

5.5. 新建一个AccountService.java业务接口

package com.lywgames.tx;

/**
 * 转账的业务层的接口
 */
public interface AccountService {
	public void transfer(String from, String to, Float money);
}

5.6. 新建一个AccountServiceImpl.java业务实现类

package com.lywgames.tx;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * 转账的业务层的实现类
 */
public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}

	private TransactionTemplate transactionTemplate;
	
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}
	
	/**
	 * from: 转出账号
	 * to: 转入账号
	 * money: 转账金额
	 */
	@Override
	public void transfer(String from, String to, Float money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.outMoney(from, money);
				int d = 1/0;
				accountDao.inMoney(to, money);
			}
		});
	}

}

5.7. 新建一个Test.java测试类

package com.lywgames.tx;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService accountService = context.getBean(AccountService.class);
		accountService.transfer("王五", "李四", 100.00F);
		context.close();
	}
}

5.8. 在src目录下创建jdbc.properties数据库连接配置

5.9. 在src目录下创建applicationContext.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/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	<!-- 通过context标签引入属性文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

	<!-- 配置Spring内置连接池 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>

	<!-- 配置平台事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 配置事务管理的模板 -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="transactionManager" />
	</bean>

	<!-- Dao继承了JdbcDaoSupport, 可以不配置jdbcTemplate模板bean, 在注入dataSource属性时, 会自动创建jdbcTemplate。 -->
	<bean id="accountDao" class="com.lywgames.tx.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<bean id="accountService" class="com.lywgames.tx.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
		<!-- 注入事务管理的模板 -->
		<property name="transactionTemplate" ref="transactionTemplate" />
	</bean> 
</beans>

5.10. 查看account表

5.11. 运行项目, 抛出异常, 查看数据库结果, 转载失败, 每个人的钱数不变

5.12. 注释掉异常, 运行项目, 查看数据库结果, 转账成功

四. 声明式事务管理(XML方式的声明式事务管理)

1. 配置平台事务管理器

2. 配置增强

3. AOP的配置

4. 例子

4.1. 新建一个名为SpringJdbcXmlTx的Java工程, 拷入相关jar包

4.2. 新建一个Account.java

package com.lywgames.tx;

import java.io.Serializable;

public class Account implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer id;
	private String name;
	private Float 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 Float getMoney() {
		return money;
	}

	public void setMoney(Float money) {
		this.money = money;
	}

	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	}

}

4.3. 新建一个AccountDao.java数据库操作接口

package com.lywgames.tx;

/**
 * 转账的Dao的接口
 */
public interface AccountDao {
	public void outMoney(String from, Float money);

	public void inMoney(String to, Float money);
}

4.4. 新建一个AccountDaoImpl.java数据库操作实现类

package com.lywgames.tx;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * 转账的Dao的实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void outMoney(String from, Float money) {
		getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);
	}

	@Override
	public void inMoney(String to, Float money) {
		getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
	}

}

4.5. 新建一个AccountService.java业务接口

package com.lywgames.tx;

/**
 * 转账的业务层的接口
 */
public interface AccountService {
	public void transfer(String from, String to, Float money);
}

4.6. 新建一个AccountServiceImpl.java业务实现类

package com.lywgames.tx;

/**
 * 转账的业务层的实现类
 */
public class AccountServiceImpl implements AccountService {

	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}

	/**
	 * from: 转出账号
	 * to: 转入账号
	 * money: 转账金额
	 */
	@Override
	public void transfer(String from, String to, Float money) {
		accountDao.outMoney(from, money);
		int d = 1/0;
		accountDao.inMoney(to, money);
	}

}

4.7. 新建一个Test.java测试类

package com.lywgames.tx;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService accountService = context.getBean(AccountService.class);
		accountService.transfer("王五", "李四", 100.00F);
		context.close();
	}
}

4.8. 在src目录下创建jdbc.properties数据库连接配置

4.9. 在src目录下创建applicationContext.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: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/context http://www.springframework.org/schema/context/spring-context.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">

	<!-- 通过context标签引入属性文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

	<!-- 配置Spring内置连接池 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<!-- Dao继承了JdbcDaoSupport, 可以不配置jdbcTemplate模板bean, 在注入dataSource属性时, 会自动创建jdbcTemplate。 -->
	<bean id="accountDao" class="com.lywgames.tx.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<bean id="accountService" class="com.lywgames.tx.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean> 
	
	<!-- 配置事务的增强/通知, 类似前置通知、后置通知等。 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- name="*" 表示所有方法 -->
			<tx:method name="*" propagation="REQUIRED" read-only="false"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- aop的配置 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.lywgames.tx.AccountServiceImpl.*(..))" id="pointcut"/>
		<!-- aop:advisor和aop:aspect都是切面。aop:advisor多个切入点和多个通知的组合。aop:aspect一个切入点和一个通知的组合。 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
	</aop:config>
</beans>

4.10. 查看account表

4.11. 运行项目, 抛出异常, 查看数据库结果, 转载失败, 每个人的钱数不变

4.12. 注释掉异常, 运行项目, 查看数据库结果, 转账成功

五. 声明式事务管理(注解方式的声明式事务管理)

1. 配置平台事务管理器

2. 开启注解事务

3. 在业务层添加注解

4. 例子

4.1. 新建一个名为SpringJdbcAnnotationTx的Java工程, 拷入相关jar包

4.2. 新建一个Account.java

package com.lywgames.tx;

import java.io.Serializable;

public class Account implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer id;
	private String name;
	private Float 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 Float getMoney() {
		return money;
	}

	public void setMoney(Float money) {
		this.money = money;
	}

	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	}

}

4.3. 新建一个AccountDao.java数据库操作接口

package com.lywgames.tx;

/**
 * 转账的Dao的接口
 */
public interface AccountDao {
	public void outMoney(String from, Float money);

	public void inMoney(String to, Float money);
}

4.4. 新建一个AccountDaoImpl.java数据库操作实现类

package com.lywgames.tx;

import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

/**
 * 转账的Dao的实现类
 */
@Component(value="accountDao")
public class AccountDaoImpl implements AccountDao {

	@Resource(name="jdbcTemplate")
	private JdbcTemplate jdbcTemplate;
	
	@Override
	public void outMoney(String from, Float money) {
		jdbcTemplate.update("update account set money = money - ? where name = ?", money, from);
	}

	@Override
	public void inMoney(String to, Float money) {
		jdbcTemplate.update("update account set money = money + ? where name = ?", money, to);
	}

}

4.5. 新建一个AccountService.java业务接口

package com.lywgames.tx;

/**
 * 转账的业务层的接口
 */
public interface AccountService {
	public void transfer(String from, String to, Float money);
}

4.6. 新建一个AccountServiceImpl.java业务实现类

package com.lywgames.tx;

import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 转账的业务层的实现类
 */
@Transactional
@Component(value="accountService")
public class AccountServiceImpl implements AccountService {

	@Resource(name="accountDao")
	private AccountDao accountDao;
	
	/**
	 * from: 转出账号
	 * to: 转入账号
	 * money: 转账金额
	 */
	@Override
	public void transfer(String from, String to, Float money) {
		accountDao.outMoney(from, money);
		int d = 1/0;
		accountDao.inMoney(to, money);
	}

}

4.7. 新建一个Test.java测试类

package com.lywgames.tx;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		AccountService accountService = context.getBean(AccountService.class);
		accountService.transfer("王五", "李四", 100.00F);
		context.close();
	}
}

4.8. 在src目录下创建jdbc.properties数据库连接配置

4.9. 在src目录下创建applicationContext.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: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/context http://www.springframework.org/schema/context/spring-context.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">

	<!-- 使用IOC的注解开发, 配置组件扫描, 哪些包下的类使用了IOC的注解 -->
	<context:component-scan base-package="com.lywgames"></context:component-scan>

	<!-- 通过context标签引入属性文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

	<!-- 配置Spring内置连接池 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<!-- 开启注解事务 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
	<!-- 配置Spring的Jdbc模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
</beans>

4.10. 查看account表

4.11. 运行项目, 抛出异常, 查看数据库结果, 转载失败, 每个人的钱数不变

4.12. 注释掉异常, 运行项目, 查看数据库结果, 转账成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值