spring事务管理

简单记录spring的事务管理,及三种声明式事务的实现。

事务的特性

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

原子性(atomicity): 一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。

一致性(consistency): 事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation): 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability): 持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

事务的隔离级别

事务的隔离级别是为了解决脏读不可重复读幻读
隔离级别有四个级别:
级别0:Read uncommitted (未提交读)
最低级别,效率最高,但不能避免并发引起的脏读、不可重复读、幻读问题

级别1:Read committed (提交读)
解决脏读问题,不能解决不可重复读、幻读

级别2:Repeatable read (可重复读)
解决了脏读、不可重复读问题,不能解决幻读

级别3:Serializable(可序列化)
最高隔离级别,保证事务串行执行,解决了脏读、不可重复读、幻读问题。但是效率最低,一般不建议用。

注意:mysql默认使用级别为二的Repeatable read (可重复读),Oracle默认是级别一Read committed (提交读)

spring特有的事务传播行为

在开发业务中可能会出现一个事务调用另一个事务的情况,spring定义了七种事务的传播行为。使用最多和默认使用的是 PROPAGATION_REQUIRED

事务传播行为类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就不使用事务。
PROPAGATION_MANDATORY如果当前有事务,使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

spring在TransactionDefinition类中定义了四种隔离级别和七种传播行为
在这里插入图片描述

spring事务的实现

一.编程式事务管理
一般不用。略…
二.声明式事务管理
1.基于注解的事务管理方式
2.使用xml配置声明式事务,基于aop
3.基于aspectJ的XML方式的配置

一、使用spring声明式事务管理,基于注解的方式。(最简单,方便的方式)

使用mybatis + spring模拟转账环境

数据库

CREATE DATABASE transaction;

-- 创建表

CREATE TABLE person(
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户Id',
  `name` varchar(120) NOT NULL COMMENT '用户名字',
  `money` bigint NOT NULL COMMENT '账户余额',
  PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET = utf8 COMMENT '人员表';

-- 插入数据

insert into person(name,money) 
values
("张三",12000),
("李四",1000),
("王五",32000),
("赵六",7000),
("田七",180000);

dao层接口personDao.java

package com.smallchili.Transaction.dao;

import org.apache.ibatis.annotations.Param;

public interface PersonDao {

	/**
	 * @param outId 转账人Id
	 * @param money 金额
	 */
	public void outMoney(@Param("outId") int outId,@Param("money") int money);
    
	/**
	 * @param inId 收款方Id
	 * @param money 金额
	 */
	public void inMoney(@Param("inId") int inId,@Param("money") int money);
	
}

resources下mapper文件夹对应的personDao.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.smallchili.Transaction.dao.PersonDao">
   <update id="outMoney">
     update 
       person
     set
       money = money - #{money}
     where  money >= 0
     and money >= #{money}   
     and id = #{outId}  
   </update>
   
   <update id="inMoney">
     update 
       person
     set
       money = money + #{money}
     where id = #{inId}
   </update>
</mapper> 

Service层
接口PersonService

package com.smallchili.Transaction.service;

public interface PersonService {

	public void transfer(int outId, int inId, int money);
	
}

实现类PersonServiceImpl

package com.smallchili.Transaction.service.impl;

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

import com.smallchili.Transaction.dao.PersonDao;
import com.smallchili.Transaction.service.PersonService;

@Service
public class PersonServiceImpl implements PersonService{

	@Autowired
	PersonDao personDao;
	
	@Override
	/**
	 * @Transactional的注解属性:
	 * propagation:事务的传播行为
	 * isolation  :事务隔离级别
	 * readOnly   :只读
	 * rollbackFor:发生哪些异常要回滚
	 * noRollbackFor:发生哪些异常不回滚
	 * */
	@Transactional	
	public void transfer(int outId, int inId, int money) {		
		personDao.outMoney(outId, money);
		int a = 1/0;//制造异常
		personDao.inMoney(inId,money);		
	}

	
	
}

测试类

package com.smallchili.Transaction;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.smallchili.Transaction.service.PersonService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext1.xml")//配置1,注解
//@ContextConfiguration("classpath:applicationContext2.xml")//配置2,xml
//@ContextConfiguration("classpath:applicationContext3.xml")//配置3,aspectJ

public class PersonServiceTest {
	//@Resource(name="personServiceProxy")//配置2,要代理类
	@Resource
	PersonService personService;
	
	@Test
	public void transferTest(){
		int outId = 2;
		int inId = 1;
		int money = 10000;
		personService.transfer(outId, inId, 10000);
	}
	
}

项目依赖

    <!-- junit单元测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    
 <!-- 日志依赖 使用slf4j+logback -->
    <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.7.12</version>
    </dependency>
    <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-core</artifactId>
       <version>1.1.1</version>
    </dependency>
    <!-- 实现slf4j接口并整合 -->
    <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.1.1</version>
    </dependency> 
    
 <!-- mysql数据库连接驱动和c3p0连接池 -->
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.35</version>
         <scope>runtime</scope>
    </dependency>
    <dependency>
       <groupId>c3p0</groupId>
       <artifactId>c3p0</artifactId>
       <version>0.9.1.2</version>
    </dependency>
    
   <!-- mybatis相关依赖 -->
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.3.0</version>
      </dependency>
      <!-- MyBatis自身实现spring整合的依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
       
    <!-- spring依赖 -->
    <!-- 1.spring核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <!-- 2.spring dao层依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>         
    <dependency><!-- 使用声名式事务,事务相关依赖 --> 
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency> 
    <!-- spring 测试相关依赖 -->
       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency> 

spring整合mybatis基本配置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 
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx
    https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置mybatis过程 -->
    <!-- 1.配置数据库相关参数 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 2.数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
     <property name="driverClass" value="${jdbc.driver}"/>
     <property name="jdbcUrl" value="${jdbc.url}"/>
     <property name="user" value="${jdbc.username}"/>
     <property name="password" value="${jdbc.password}"/>
    </bean>
      
     <!-- 3.配置sqlSessionFactory -->
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource"/>
     <!-- 扫描sql配置文件,mapper需要的xml文件 -->
     <property name="mapperLocations" value="classpath:mapper/*.xml"/>
     </bean>
     
     <!--4.配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中  -->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     <!-- 注入sqlSessionFactory -->
     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
     <!-- 给出需要扫的接口包 -->
     <property name="basePackage" value="com.smallchili.Transaction.dao"/>
     </bean>

   <!-- 扫描service包下注解 -->
   <context:component-scan base-package="com.smallchili.Transaction.service"></context:component-scan>   

<!-- 配置事务管理器 -->
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource"></property>
 </bean>
   <!-- 配置基于注解的声明式事务 
        默认使用注解来管理事务行为
      默认是jdk代理  proxy-target-class="false" 如果设置true 则使用cglib,要引入cglib依赖
   -->
   <tx:annotation-driven transaction-manager="transactionManager"/>  
</beans>

一、 方式一:基于注解的事务管理方式的主要依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>         
    <dependency><!-- 使用声明式事务,事务相关依赖 --> 
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency> 

主要配置

<!-- 配置事务管理器 -->
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource"></property>
 </bean>
   <!-- 配置基于注解的声明式事务 
        默认使用注解来管理事务行为
      默认是jdk代理  proxy-target-class="false" 如果设置true 则使用cglib,要引入cglib依赖
   -->
   <tx:annotation-driven transaction-manager="transactionManager"/>  

要加事务的方法上

	@Transactional
	public void transfer(int outId, int inId, int money) {		
		personDao.outMoney(outId, money);
		int a = 1/0;//制造异常
	   personDao.inMoney(inId,money);		
	}

注解里有一些可选属性配置,可根据需求配置

	/**
	 * @Transactional的注解属性:
	 * propagation:事务的传播行为
	 * isolation  :事务隔离级别
	 * readOnly   :只读
	 * rollbackFor:发生哪些异常要回滚
	 * noRollbackFor:发生哪些异常不回滚
	 * */
	@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
二、方式2:使用xml配置声明式事务,基于代理AOP

主要依赖

      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>         
    <!-- 使用aop -->
    <!-- aop联盟的包 -->
    <dependency>
       <groupId>aopalliance</groupId>
       <artifactId>aopalliance</artifactId>
       <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency> 

主要配置文件

 <!-- 配置事务管理器 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource"></property>
   </bean>
<!-- 配置要使用事务的类,目标类的bean -->
  <bean id="personService" class="com.smallchili.Transaction.service.impl.PersonServiceImpl"/>
  
   <!-- 配置业务层的代理: -->
   <bean id="personServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      <!-- 注入目标对象 -->
      <property name="target" ref="personService"/>
       <!-- 注入事务管理器 -->
      <property name="transactionManager" ref="transactionManager"/>
      <!-- 注入事务属性 -->
      <property name="transactionAttributes">
         <props>
           <!-- transfer是类里的方法
           PROPAGATION 事务传播行为 -->
           <prop key="transfer">PROPAGATION_REQUIRED</prop>
         </props>     
      </property>
   </bean>

另外,还可以这样配置抽取出来,使用用parent属性

<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
 *     abstract="true">
 *   <property name="transactionManager" ref="transactionManager"/>
 *   <property name="transactionAttributes">
 *     <props>
 *       <prop key="insert*">PROPAGATION_REQUIRED</prop>
 *       <prop key="update*">PROPAGATION_REQUIRED</prop>
 *       <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
 *     </props>
 *   </property>
 * </bean>
 *
 * <bean id="myProxy" parent="baseTransactionProxy">
 *   <property name="target" ref="myTarget"/>
 * </bean>
 *
 * <bean id="yourProxy" parent="baseTransactionProxy">
 *   <property name="target" ref="yourTarget"/>
 * </bean>

注意测试时要用注入的是代理类

package com.smallchili.Transaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.smallchili.Transaction.service.PersonService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class PersonServiceTest {

	@Resource(name="personServiceProxy")//指定代理类
	PersonService personService;
		
	@Test
	public void transferTest(){
		int outId = 2;
		int inId = 1;
		int money = 10000;
		personService.transfer(outId, inId, 10000);
	}
	
}

三、基于aspectJ的XML方式的配置
主要依赖

  <!-- 2.spring dao层依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>             
  <!-- 使用 aspectj-->  
   <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
   </dependency>
   <!-- spring整合aspectj的依赖-->  
   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency> 
  </dependencies>

主要配置

 <!-- 配置事务管理器 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- 配置事务通知(事务的增强) -->
   <tx:advice id="txAdivce" transaction-manager="transactionManager">
     <tx:attributes>
       <tx:method name="transfer" propagation="REQUIRED"/>
     </tx:attributes> 
   </tx:advice> 
   
   <!-- 配置切面 -->
   <aop:config>
      <!-- 配置切入点 -->
      <aop:pointcut expression="execution(* com.smallchili.Transaction.service.*.*(..))" id="pointcut"/>
     <!-- 配置切面 -->
      <aop:advisor advice-ref="txAdivce" pointcut-ref="pointcut"/>
   </aop:config>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值