Spring的事务管理的两种实现方式

Spring的事务管理

事务是什么

是用户定义的一组数据库操作的集合,要么全成功,要么全失败

事务特征
特征含义
原子性事务是不可分割的一组操作要么全做,要么全不做
一致性数据库中数据从一个一致的状态转为另一个一致的状态
隔离性不同的事务之间互不影响
持久性事务一旦提交对数据的影响是持久的
事务隔离级别
查看MySQL默认隔离级别

SHOW VARIABLES LIKE ‘%tx_isolation%’

级别类型
类型概念
读未提交事务A和B,事务A未提交的数据,事务B可以读到。读取到的数据是脏数据
读已提交事务A提交的数据,事务B才能读取到。避免了脏读
可重复读直到事务A结束前,一直能读到相同的数据。避免了脏读和不可重复读(MySQL默认隔离级别)
串行化不允许读写并发操作,是最高隔离级别,写时读必须等待,效率低。避免幻读
事务并发问题
前提

不考虑隔离级别引发的安全问题

并发问题
并发问题含义
脏读A事务读取到B事务未提交的数据(只要没提交就是脏读),因为A事务可能回滚
不可重复读A事务多次读取某数据,期间B事务对该数据做了更改并已提交,导致多次读的数据不一致UPDATE、DELETE
幻读事务A读取到了另外一个事务已经提交的数据,导致多次查询结果不一致INSERT
Spring事务管理
  1. 编程式事务:将事务管理代码嵌入到业务方法中来控制事务提交和回滚
  2. 声明式事务:将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理(思想是AOP)
事务管理的核心接口
  1. Spring针对不同的持久层框架,提供接口不同的实现类。
  2. 事务管理器:PlatformTransactionManager
    – getTranscation:获取事务状态
    – commit:提交事务
    – rollback:回滚事务
  3. Spring并不是直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给了Mybatis或者其他框架来实现事务管理
Spring中的声明式事务管理方式
基于XML的事务管理

步骤

  1. 配置事务管理器DataSourceTransactionManager,配置数据源
  2. 配置事务增强(设置事务操作的方法匹配)
  3. 配置切面(配置切入点,配置切面advice-ref,pointcut-ref)

通过在配置文件中配置事务相关的规则
AccountMapper.java

public interface AccountMapper {
	
	// 转出
	int outMoney(@Param("username")String username,@Param("money")int money);
	
	// 转入
	int inMoney(@Param("username")String username,@Param("money")int money);
	
}

AccountMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hpe.mapper.AccountMapper">
	
	<!-- 转出 -->
	<update id="outMoney">
		UPDATE account SET money = money-#{money} WHERE username=#{username}
	</update>
	
	<!-- 转入 -->
	<update id="inMoney">
		UPDATE account SET money = money+#{money} WHERE username=#{username}
	</update>
</mapper>

AccountService.java

public interface AccountService {
	
	/**
	 * 转账的方法
	 * @param out 转出人
	 * @param in 转入人
	 * @param money 转账金额
	 */
	void updateAccount(String out,String in,int money);
}

AccountServiceImpl.java

package com.hpe.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hpe.mapper.AccountMapper;
@Service("accountService")
public class AccountServiceImpl implements AccountService {

	/* 实现转账的操作:
	 * 1.编程式事务
	 * 2.声明式事务
	 */
	@Autowired
	private AccountMapper accountMapper;

	@Override
	public void updateAccount(String out, String in, int money) {
		// 转出
		accountMapper.outMoney(out, money);
		// 模拟异常(算数异常)
		//int i = 10/0;
		// 转入
		accountMapper.inMoney(in, money);
	}

}

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:component-scan base-package="com.hpe"></context:component-scan>
	
	<!-- 加载外部资源文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	
	<!-- 配置c3p0数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 通过Spring管理SqlSessionFactory mapper接口 -->
	<!-- 配置SqlSessionFactory为了使用 Spring来管理SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 配置mybatis核心配置文件的路径 -->
		<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
		<!-- 指定数据源 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 指定批量创建别名的包 -->
		<property name="typeAliasesPackage" value="com.hpe.po"></property>
	</bean>
	
	<!-- 批量创建Mapper接口实现类的Bean,可以不指定id
		 默认Bean是由id的,是Mapper接口的名称且首字母小写-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定需要创建实现类的Mapper接口所在的包
			 可以指定多个包,使用逗号隔开即可-->
		<property name="basePackage" value="com.hpe.mapper"></property>
		<!-- 需要指定SqlSessionFactory,使用sqlSessionFactoryBeanName
			 通过这种方式就可以等到Spring初始化完成后,再去构造SqlSessionFactory,否则无法连接数据库 -->
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
	</bean>
	
	<!-- Spring声明式事务管理:XML方式 -->
	<!-- 1.配置事务管理器(XML及注解方式均需配置) -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
		
	<!-- 2.配置事务增强:需要编写切入点及细节 -->
	<!-- propagation:事务的传播行为
		   REQUIRED:必须的,如果有事务则使用,没有则创建
		   SUPPORTS:可选的,如果有事务则使用,没有不使用
	 	 isolation:事务的隔离级别
	 	   DEFAULT:mysql默认隔离级别(可重复读)
	 	         读未提交
	 	         读已提交
	 	         可串行化:可避免幻读
	 	 rollback-for:回滚(对哪写异常回滚)
	 	 no-rollback-for:不回滚(对哪些异常不回滚)
	 	   注意: 如果不设置默认的话,是对运行时异常进行回滚
	 	 read-only:如果将select设置为true,则告诉数据库引擎查询是只读的。
	 	 timeout=-1:过期时间(设置多长时间不回滚)
	 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 设置进行事务操作方法匹配的规则(规范程序员的命名规则) -->
			<tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 3.配置切面 -->
	<aop:config>
		<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.hpe.service.*.*(..))" id="pointcut1"/>
		<!-- 配置切面(应用增强)
			 advice-ref:使用哪个增强
			 pointcut-ref:将增强应用到哪个切入点上面(也可以使用pointcut指定相应的表达式即可)
		 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
	</aop:config>
	
</beans>

Test.java

public class SpringTest {

	// 使用事务模拟转账
	@Test
	public void test1(){
		// 1.加载Spring的配置文件
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 2.创建UserService对象
		AccountService accountService = context.getBean(AccountService.class);
		// 3.调用mapper中的方法
		accountService.updateAccount("张三", "李四", 100);
	}	
}
基于注解的事务管理

步骤

  1. 配置事务管理器DataSourceTransactionManager,配置数据源
  2. 配置事务相关的注解
  3. 在使用事务的方法或者类上面添加注解

代码实战
AccountMapper.java、AccountMapper.xml、AccountService.java相比于上面并未改动(此处不再贴重复代码)
AccountServiceImpl2.java

package com.hpe.service;

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

import com.hpe.mapper.AccountMapper;
@Service("accountService2")
// @Transactional:表示事务的设置对这个类的所有方法都起作用
//@Transactional
public class AccountServiceImpl2 implements AccountService {

	@Autowired
	private AccountMapper accountMapper;

	// @Transactional:表示事务的设置只对这个方法起作用
	@Transactional
	@Override
	public void updateAccount(String out, String in, int money) {
		// 转出
		accountMapper.outMoney(out, money);
		// 模拟异常(算数异常)
		//int i = 10/0;
		// 转入
		accountMapper.inMoney(in, money);
	}

}

applicationContext2.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:component-scan base-package="com.hpe"></context:component-scan>
	
	<!-- 加载外部资源文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	
	<!-- 配置c3p0数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 通过Spring管理SqlSessionFactory mapper接口 -->
	<!-- 配置SqlSessionFactory为了使用 Spring来管理SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 配置mybatis核心配置文件的路径 -->
		<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
		<!-- 指定数据源 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 指定批量创建别名的包 -->
		<property name="typeAliasesPackage" value="com.hpe.po"></property>
	</bean>
	
	<!-- 批量创建Mapper接口实现类的Bean,可以不指定id
		 默认Bean是由id的,是Mapper接口的名称且首字母小写-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定需要创建实现类的Mapper接口所在的包
			 可以指定多个包,使用逗号隔开即可-->
		<property name="basePackage" value="com.hpe.mapper"></property>
		<!-- 需要指定SqlSessionFactory,使用sqlSessionFactoryBeanName
			 通过这种方式就可以等到Spring初始化完成后,再去构造SqlSessionFactory,否则无法连接数据库 -->
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
	</bean>
	
	<!-- Spring声明式事务管理:注解方式 -->
	<!-- 1.配置事务管理器(XML及注解方式均需配置) -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
		
	<!-- 2.注册事务注解驱动
		 transaction-manager:自动检测事务处理 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
</beans>

测试方法

@Test
public void test2(){
	// 1.加载Spring的配置文件
	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
	// 2.创建UserService对象
	AccountService accountService = context.getBean("accountService2",AccountService.class);
	// 3.调用mapper中的方法
	accountService.updateAccount("张三", "李四", 100);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值