Spring事物

走远了吗. 提交于 2019-12-03 11:02:12

事务

  事务必须服从ISO/IEC所指定的ACID原则。ACID是原子性,一致性,隔离性,持久性的缩写。

  原子性:

    表示事务执行过程中的任何失败都将导致事务所做的任何修改失效;

  一致性:

    表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行之前的状态;

  隔离性:

    表示事务执行过程中对数据的修改,在事务提交之前对其他事务不可见;

  持久性:

    表示已提交的数据在事务执行失败时,数据的状态都应该正确;

  通俗的理解,事务是一组院子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行有错误,则撤销先前执行过的所有指令。更简单的说就是:要么全部执行成功,要么撤销不执行

隔离级别

  1. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。(读未提交)
        这种隔离级别会产生脏读,不可重复读和幻像读。
  2. ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据(读已提交)
  3. ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。(可重复读)
      它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
  4. ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。(串行化)

    除了防止脏读,不可重复读外,还避免了幻像读。 

    

脏读,幻读,不可重复读

脏读

脏读 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
   e.g.
       1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
       2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
       3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000
         像这样,Mary记取的工资数8000是一个脏数据。

不可重复读

不可重复读 是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
   e.g.
  1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
   2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
  3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000

解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。

幻读

幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样
  e.g. 
  目前工资为1000的员工有10人。
   1.事务1,读取所有工资为1000的员工。
   2.这时事务2向employee表插入了一条员工记录,工资也为1000
     3.事务1再次读取所有工资为1000的员工 共读取到了11条记录, 
 解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题

 

在一个程序中,依据事务的隔离级别将会有三种情况发生。
  ◆脏读:一个事务会读进还没有被另一个事务提交的数据,所以你会看到一些最后被另一个事务回滚掉的数据。
  ◆不可重复读:一个事务读进一条记录,另一个事务更改了这条记录并提交完毕,这时候第一个事务再次读这条记录时,它已经改变了
  ◆ 幻影读:一个事务用Where子句来检索一个表的数据,另一个事务插入一条新的记录,并且符合Where条件,这样,第一个事务用同一个where条件来检索数据后,就会多出一条记录

传播行为

1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2、PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

3、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4、PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 

 实例:

    entity实体类:

 

public class Accounts {
    private int accountid;
    private String accountname;
    private double balance;

    public int getAccountid() {
        return accountid;
    }

    public void setAccountid(int accountid) {
        this.accountid = accountid;
    }

    public String getAccountname() {
        return accountname;
    }

    public void setAccountname(String accountname) {
        this.accountname = accountname;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

    dao层接口:

    //加钱
    void addMoney(double money);

    //减钱
    void subMoney(double money);

    daoimpl实现类接口:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addMoney(double money) {
        String sql="UPDATE accounts SET balance=balance+? WHERE accountname='张三'";
        this.getJdbcTemplate().update(sql,money);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void subMoney(double money) {
        String sql="UPDATE accounts SET balance=balance-? WHERE accountname='小红'";
        this.getJdbcTemplate().update(sql,money);
    }

    service业务层:

    //转账
    void transferMoney();

    service业务实现层:

    //转账的方法
    /*@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)*/
    @Transactional
    @Override
    public void transferMoney() {
        accountDao.subMoney(1000);      //价钱
        int num=5/0;    //模拟一个异常    使用事务解决
        accountDao.addMoney(1000);
    }

    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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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">

    <!--添加jdbc-->
    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"></property>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${url}"></property>
        <property name="driverClassName" value="${driver}"></property>
        <property name="username" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>

    <!--配置jdbcTemplate核心对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--Dao接口的实现类对象-->
    <bean id="accountDao" class="com.te.daoimpl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--service接口的实现类对象-->
    <bean id="accountService" class="com.te.serviceImpl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

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

    <!--配置事务-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transferMoney" propagation="REQUIRED" isolation="READ_COMMITTED" />
        </tx:attributes>
    </tx:advice>
    <!--定义切面-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.te.service.*.*(..))"></aop:advisor>
    </aop:config>

</beans>

    测试:

    @Test
    public void test03(){
        ApplicationContext ctx=new ClassPathXmlApplicationContext("application-del.xml");
        AccountService accountService =(AccountService)ctx.getBean("accountService");
        //转账
        accountService.transferMoney();
    }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!