Spring5学习笔记——事务

概念

  1. 事务是数据库操作的最基本单元,逻辑上一组操作,要么都成功,如果有一个失败则所有操作都要回滚到操作之前的状态。

  2. 事务的 ACID 特性:
    (1)原子性
    (2)一致性
    (3)隔离性
    (4)持久性

Spring 进行事务管理

  1. 事务添加到 JavaEE 三层结构里面的 Service 层(业务逻辑层)中。
  2. Spring 进行事务管理操作有两种实现方式:编程式事务管理、声明式事务管理(常用)
  3. 声明式事务管理:
    (1)基于注解方式实现(常用)
    (2)基于 xml 配置文件方式实现
  4. Spring 进行声明式事务管理,底层使用 AOP 原理

API

Spring 提供 PlatformTransactionManager 接口,实现对事务的管理操作,对于不同的框架,提供不同的实现类,如:不整合其他外部框架时,使用 DataSourceTransactionManager 实现类;整合 MyBatis 框架时,使用 DataSourceTransactionManager 实现类。
在这里插入图片描述

注解实现

步骤

(1)在 Spring 配置文件中配置事务管理器

<!-- 1.创建事务管理器对象 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入与JdbcTemplate相同的数据库连接池对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>

(2)开启事务注解

<!-- 使用 tx(事务)名称空间 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

(3)在要添加事务的类或方法上添加@Transactional注解

@Transactional
public class UserService {
}

注意:@Transactional 注解可以添加在类上面,也可以添加在方法上面
(1)若添加在类上面,表示为这个类内所有的方法都添加事务
(2)若添加在方法上面,表示为这个方法添加事务

@Transactional 参数

(1)propagation:传播行为。在事务操作中,多个方法之间相互调用时,如何进行事务处理。

Spring 中的 7 种传播行为:默认使用 REQUIRED
在这里插入图片描述
REQUIRED:如果 A 方法有事务,调用 B 方法后,B 使用 A 的事务;如果 A 方法没有事务,调用 B 方法之后,开启一个新的事务。

REQUIRED_NEW:不论 A 方法有无事务,调用 B 方法后,都开启一个新的事务。

(2)isolation:隔离级别。

4 种隔离级别,MySQL 默认使用 REPEATABLE_READ
在这里插入图片描述
(3)timeout:超时时间。

事务需要在一定时间内进行提交,如果在规定时间内未提交要进行回滚,默认值为 -1,表示不超时,设置时间以秒单位。

(4)readOnly:是否只读。

  • 读:查询操作,写:添加、修改、删除操作
  • 默认值为 false,表示可以查询、添加、修改、删除
  • 设置 readOnly 值为 true,表示只能进行查询

(5)rollbackFor:回滚。

设置出现哪些异常要进行回滚,参数形式为rollbackFor = 异常类.class

(6)noRollbackFor:不回滚。

设置出现哪些异常不回滚,参数形式为rollbackFor = 异常类.class

实现

jdbctransaction.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:property-placeholder location="classpath:druid2.property"/>
	<!-- 创建 数据库连接池对象 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<!-- 注入属性 -->
		<property name="driverClassName" value="${druid.driverClassName}"></property>
		<property name="url" value="${druid.url}"></property>
		<property name="username" value="${druid.username}"></property>
		<property name="password" value="${druid.password}"></property>
	</bean>
	
	<!-- 创建 JdbcTemplate 对象 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启组件扫描,用注解创建对象以及注入属性 -->
	<context:component-scan base-package="com.mcc.spring5.jdbc.transaction"></context:component-scan>
	
	<!-- 进行事务操作 -->
	<!-- 1.创建事务管理器对象 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入与JdbcTemplate相同的数据库连接池对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 2.引入 tx 名称空间 -->
	<!-- 3.开启事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
</beans>

UserService.java

package com.mcc.spring5.jdbc.transaction;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;

@Service
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)//设置传播行为、隔离级别、超时时间、是否只读
public class UserService {
	@Autowired
	private UserDAO userDao;
	
	//转账的方法
	public void accountMoney() {
		//lucy减少100
		userDao.reduceMoney();
		//模拟异常
		int i = 1/0;
		//mary增加100
		userDao.addMoney();
	}
}

UserDAO.java

package com.mcc.spring5.jdbc.transaction;

public interface UserDAO {

	void reduceMoney();

	void addMoney();

}

UserDAOImpl.java

package com.mcc.spring5.jdbc.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAOImpl implements UserDAO{
	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public void reduceMoney() {
		String sql = "update `user_account` set `money`=`money`-? where `username` = ?";
		jdbcTemplate.update(sql,100,"lucy");
	}
	
	@Override
	public void addMoney() {
		String sql = "update `user_account` set `money`=`money`+? where `username` = ?";
		jdbcTemplate.update(sql,100,"mary");
	}
}

测试

@Test
public void testAnnotation() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("jdbctransaction.xml");
	UserService userService = context.getBean("userService", UserService.class);
	userService.accountMoney();
	context.close();
}

XML 实现

在 spring 配置文件中进行配置:
(1)配置事务管理器
(2)配置事务通知<tx:advice id=""></tx:advice>
(3)配置切入点和切面<aop:config></aop:config>

jdbctransactionXML.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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<!-- 加载 数据库连接池配置文件 -->
	<context:property-placeholder location="classpath:druid2.property"/>
	<!-- 创建 数据库连接池对象 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<!-- 注入属性 -->
		<property name="driverClassName" value="${druid.driverClassName}"></property>
		<property name="url" value="${druid.url}"></property>
		<property name="username" value="${druid.username}"></property>
		<property name="password" value="${druid.password}"></property>
	</bean>
	
	<!-- 创建 JdbcTemplate 对象 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启组件扫描,用注解创建对象以及注入属性 -->
	<context:component-scan base-package="com.mcc.spring5.jdbc.transaction"></context:component-scan>
	
	<!-- 进行事务操作 -->
	<!-- 1.创建事务管理器对象 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入与JdbcTemplate相同的数据库连接池对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 2.配置事务通知 -->
	<tx:advice id="txAdvice">
		<!-- 配置事务参数 -->
		<tx:attributes>
			<!-- 为何种方法添加事务操作 -->
			<tx:method name="accountMoney" propagation="REQUIRED"/><!-- name="account*" 表示所有以account开始的方法 -->
		</tx:attributes>
	</tx:advice>
	<!-- 3.配置切入点和切面 -->
	<aop:config>
		<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.mcc.spring5.jdbc.transaction.UserService.*(..))" id="pointcut"/>
		<!-- 配置切面,注意:使用的是专门用于配置事务的切面标签 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
	</aop:config>
	
</beans>

完全注解开发

使用 配置类 代替 xml 配置文件.

  1. @Bean:使用在方法上,在加载配置类时,自动执行该方法,获取返回值对象,保存到 IOC 容器中。
  2. 如何保证使用的是同一个数据库连接池对象?
    将 DataSource 对象以参数形式传入到其他方法中,保证使用的是同一个数据库连接池对象。
    原因:在加载配置类时,由于 @Bean 注解的作用,会自动调用方法,因为方法的形参为 DataSource 对象,Spring 会自动在 IOC 容器中寻找 DataSource 对象,并传入方法中。

TxConfig.java

package com.mcc.spring5.jdbc.transaction.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;

//Spring配置类
@Configuration//声明配置类
@EnableTransactionManagement//开启事务注解
@ComponentScan(basePackages = {"com.mcc.spring5.jdbc.transaction"})//开启组件扫描
public class TxConfig {
	//创建数据库连接池对象
	@Bean
	public DruidDataSource getDruidDataSource() {
		//加载配置文件
		Properties prop = new Properties();
		InputStream inputStream = TxConfig.class.getClassLoader().getResourceAsStream("druid2.property");
		try {
			prop.load(inputStream);
		} catch (IOException e) {
			throw new RuntimeException("Error load file named druid2.property");
		}
		//创建druid对象
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(prop.getProperty("druid.driverClassName"));
		dataSource.setUrl(prop.getProperty("druid.url"));
		dataSource.setUsername(prop.getProperty("druid.username"));
		dataSource.setPassword(prop.getProperty("druid.password"));
		return dataSource;
	}
	//创建JdbcTemplate对象
	@Bean
	public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate();
		jdbcTemplate.setDataSource(dataSource);
		return jdbcTemplate;
	}
	//创建事务管理器对象
	@Bean
	public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource dataSource) {
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		transactionManager.setDataSource(dataSource);
		return transactionManager;
	}
}

测试

@Test
public void testConfig() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
	UserService userService = context.getBean("userService", UserService.class);
	userService.accountMoney();
	context.close();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值