Spring事务

Posted 春眠不觉笑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务相关的知识,希望对你有一定的参考价值。

Spring事务

一、事务的作用

  将若干的数据库操作作为一个整体控制,一起成功或一起失败。
  原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  一致性:指事务前后数据的完整性必须保持一致。
  隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
  持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即时数据库发生故障也不应该对其有任何影响。

二、Spring事务管理高层抽象主要包括3个接口

  --Platform TransactionManager 事务管理器(提交、回滚事务)
     Spring为不同的持久化框架提供了不同的Platform TransactionManager接口实现。如:
        使用Spring JDBC或iBatis进行持久化数据时使用DataSourceTransactionManager
        使用Hibernate3.0版本进行持久化数据时使用HibernateTransactionManager
  --TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
        脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
        不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。
        幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
        事务隔离级别:(五种)

  •     DEFAULT--使用后端数据库默认的隔离级别(Spring中的选择项)
  •     READ_UNCOMMITED--允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
  •     READ_COMMITTED--允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生
  •     REPEATABLE_READ--对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生
  •     SERIALIZABLE--完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的

    其中,mysql默认采用REPEATABLE_READ隔离级别;Oracle默认采用READ_COMMITTED隔离级别

        事务传播行为:(七种)

  •     REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  •     SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
  •     MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
  •     REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
  •     NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  •     NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
  •     NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作。拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对DataSourceTransactionManager有效

  --TransactionStatus 事务具体运行状态

三、Spring提供了以下方法控制事务

  a.编程式事务管理(基于Java编程控制,很少使用)
       利用TransactionTemplate将多个DAO操作封装起来
  *b.声明式事务管理(基于Spring的AOP配置控制)
       -基于TransactionProxyFactoryBean的方式.(很少使用)
            需要为每个进行事务管理的类,配置一个TransactionProxyFactoryBean进行增强.
       -基于XML配置(经常使用)
            一旦配置好之后,类上不需要添加任何东西。
            如果Action作为目标对象切入事务,需要在<aop:config>元素里添加proxy-target-class="true"属性。原因是通知Spring框架采用CGLIB技术生成具有事务管理功能的Action类。
       -基于注解(配置简单,经常使用)
            在applicationContext.xml中开启事务注解配置。(applicationContext.xml中只需定义Bean并追加以下元素)
<bean id="txManager" class="...">
  <property name="sessionFactory">
  </property>
<tx:annotation-driven transaction-manager="txManager"/>

            在目标组件类中使用@Transactional,该标记可定义在类前或方法前。

转账的例子

 cn.kitty.bean    Account

public class Account {
    private Integer aid;//id
    private String sname;//账户名称
    private Double sbalance;//余额

    public Account() {
    }

    public Account(Integer aid, String sname, Double sbalance) {
        this.aid = aid;
        this.sname = sname;
        this.sbalance = sbalance;
    }

Stock

public class Stock {
    private  Integer sid;
    private String sname;
    private Integer scount;

    public Stock() {
    }

    public Stock(Integer sid, String sname, Integer scount) {
        this.sid = sid;
        this.sname = sname;
        this.scount = scount;
    }

StockException

package cn.kitty.bean;

public class StockException extends  Exception {
    /**
     * //一个类继承  Exception 是运行时异常
     *运行时异常 Spring自动回滚
     * 编译时异常 Spring自动提交
     * 编译时异常最大的区别
     */
        public StockException() {
            super();
        }

        public StockException(String message) {
            super(message);
        }

}

dao

package cn.kitty.dao;

public interface IAccountDao {
   //update账户
    public void  updateAcount(int aid,int abalance, boolean isbuy );
}

 

package cn.kitty.dao;

public interface IStockDao {
   //update账户
   public void  updateStock(int sid,int scount, boolean isbuy );
}

dao.impl

package cn.kitty.dao.impl;

import cn.kitty.dao.IAccountDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    public void updateAcount(int aid, int abalance, boolean isbuy) {

        String sql=null;
        if(isbuy){
           sql="UPDATE account SET abalance=abalance - ? WHERE aid= ?";
        }else {
            sql="UPDATE account SET abalance=abalance + ? WHERE aid= ?";
        }

        this.getJdbcTemplate().update(sql,abalance,aid);
    }
}

 

package cn.kitty.dao.impl;

import cn.kitty.dao.IStockDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class StockDaoimpl extends JdbcDaoSupport implements IStockDao {

    public void updateStock(int sid, int scount, boolean isbuy) {
        String sql=null;
         if(isbuy){
             sql="UPDATE  stock  SET  scount=scount+?   WHERE  sid=?  ";
         }else{
             sql="UPDATE  stock  SET  scount=scount-?   WHERE  sid=?  ";
         }

     this.getJdbcTemplate().update(sql,scount,sid);
    }
}

Service

package cn.kitty.service;

import cn.kitty.bean.StockException;
import org.springframework.transaction.annotation.Transactional;

public interface IStockService {
    //模拟一个真正的购买过程
    @Transactional (rollbackFor = StockException.class)
    public void buyStock(int sid,int scount,int aid,int abalance);
}

service.impl

package cn.kitty.service;

import cn.kitty.dao.IAccountDao;
import cn.kitty.dao.IStockDao;

public class StockServiceImpl implements IStockService {
    private  IStockDao stockDao;
    private IAccountDao accountDao;
    public void buyStock(int sid, int scount, int aid, int abalance) {
           stockDao.updateStock(sid,scount,true);

           accountDao.updateAcount(aid,abalance,true);
    }

    public IStockDao getStockDao() {
        return stockDao;
    }

    public void setStockDao(IStockDao stockDao) {
        this.stockDao = stockDao;
    }

    public IAccountDao getAccountDao() {
        return accountDao;
    }

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd

">
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="jdbc:mysql:///aount"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
    <bean id="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--Dao-->
    <bean id="stockDao" class="cn.kitty.dao.impl.StockDaoimpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="accountDao" class="cn.kitty.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--平台事务管理器-->
     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource"></property>
     </bean>
    <!--service-->
    <bean id="stockService" class="cn.kitty.service.StockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    <!--事务拦截处理:事务代理工厂Bean TransactionProxyFactoryBean-->
    <bean  id="beanfactory" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target" ref="stockService"></property>
        <property name="transactionManager" ref="transactionManager"></property>
        <property name="transactionAttributes">
           <props>
               <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException</prop>
           </props>

          </property>
    </bean>
</beans>

 Test测试类

public class Test20171019 {
    @Test
    public void shiwu(){
        ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        IStockService   service = (IStockService)context.getBean("beanfactory");
        service.buyStock(1,10,1,300);
    }
}

 

以上是关于Spring事务的主要内容,如果未能解决你的问题,请参考以下文章

BottomNavigationView 滞后于片段事务

理解片段事务期间片段的生命周期方法调用

提交带有全屏片段的片段事务

使用 OnItemClickListener 列出视图片段到片段事务

spring中声明式事务 配置好后,在java代码中怎么使用

阶段3 2.Spring_10.Spring中事务控制_5 spring事务控制的代码准备