spring5之事务

一:事务介绍:

大家所了解的事务Transaction,它是一些列严密操作动作,要么都操作完成,要么都回滚撤销。Spring事务管理基于底层数据库本身的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下Spring事务。

事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。
(1)原子性(Atomicity)
    原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,不会结束在事务的中间环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
(2)一致性(Consistency)
    事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
    举例:拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
(3)隔离性(Isolation)
    当不同的事务同时操纵相同的数据时,多个并发事务之间要相互隔离。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
(4)持久性(Durability)
    持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的。这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

二、数据库的4种隔离级别:

如果不考虑隔离性会引发安全性问题

隔离级别:定义了一个事务可能受其他并发事务影响的程度。

(1)并发事务引起的问题:典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题:

脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。

不可重复读(Nonrepeatable read)——不可重复读发生在一个事务内执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。

幻读/虚读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入/删除了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,或者少了一些记录。

(2)解决读问题:设置事务的隔离级别:

READ UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。

READ COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。

REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。

SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。

mysql数据库的默认隔离级别就是可重复读,Oracle默认是已提交读

参考链接:https://blog.csdn.net/qq_34598667/article/details/83627563

二、事务的传播机制

数据库向用户提供保存当前程序状态的方法,叫事务提交(commit);当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollback)

Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。

spring在TransactionDefinition接口中定义了七个事务传播行为:
PROPAGION_XXX:事务的传播行为。

保证在同一个事务中
PROPAGION_REQUIRED:required , 必须。支持当前事务,如果不存在,就新建一个(默认)
PROPAGION_SUPPORTS:supports ,支持。支持当前事务,如果不存在,就不使用事务
PROPAGION_MANDATORY:mandatory ,强制。支持当前事务,如果不存在,就抛出异常

保证没有在同一个事务中
PROPAGION_REQUIRES_NEW:requires_new,必须新的。如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGION_NOT_SUPPORTED:not_supported ,不支持。以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGION_NEVER:never,从不。以非事务方式运行,如果有事务存在,抛出异常
PROPAGION_NESTED:nested ,嵌套。如果当前事务存在,则嵌套事务执行

参考链接:
https://www.cnblogs.com/myseries/p/10834172.html

三、事务超时:

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

在这里插入图片描述

四、Spring进行事务管理的常用API

在这里插入图片描述

1)PlatformTransactionManager:平台事务管理器
Spring进行事务操作时候,主要使用一个PlatformTransactionManager接口,它表示事务管理器,即真正管理事务的对象。
Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring针对不同的持久化框架,提供了不同PlatformTransactionManager接口的实现类:

(1)org.springframework.jdbc.datasource.DataSourceTransactionManager :使用 Spring JDBC或iBatis 进行持久化数据时使用

(2)org.springframework.orm.hibernate3.HibernateTransactionManager :使用 Hibernate版本进行持久化数据时使用

五、Spring进行事务操作的方式:

1.编程式事务管理(大量代码书写实现事务管理包括自己定义事务的开始beginTransaction(),提交commit(),异常后的回滚rollback()等。)

2.声明式事务管理:
2.1基于xml配置文件方式(不使用)
2.2基于注解方式

事务管理案例:

第一步: 创建数据库表,添加记录
在这里插入图片描述

第二步:创建service,搭建dao,完成对象创建和注入关系
(1)service注入dao,在dao中注入JdbcTemplate,在JdbcTemplate中注入DataSource;

UserServiceImpl 实现类:


package com.fan.service.impl;

import com.fan.dao.UserDao;

import com.fan.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service("userService")
@Transactional//为此类添加事务,类中所有方法也添加了事务
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    public void accountMoney() {
        //1.lucy先少100
        userDao.reduceMoney();


        //中间断电发生异常
        int a = 10/0;
        //2.tom 再增加多   100
        userDao.addMoney();
    }
}

UserDaoImpl 实现类:

package com.fan.dao.impl;

import com.fan.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Resource
    private JdbcTemplate jdbcTemplate;

    //少钱的方法
    public void reduceMoney() {
        String sql ="update t_acount SET money=money-? WHERE name=?";
        int lucy = jdbcTemplate.update(sql, 100, "lucy");
        System.out.println(lucy);
    }
    //增加钱的方法
    public void addMoney() {
        String sql ="update t_acount SET money=money+? WHERE name=?";
        int n = jdbcTemplate.update(sql, 100, "tom");
        System.out.println(n);
    }
}

第三步:在dao中创建两个方法。多钱的和少钱的方法。在service中创建转载的方法。

第四步:测试



import com.fan.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")//注解形式加载xml文件
@Component
public class TestJdbc {
    @Resource//注入service对象
    private UserService userService;

    //测试数据源连接
    @Resource(name = "dataSource")
    private DataSource dataSource;

    @Test//测试数据源连接
    public void getConnection2() throws SQLException {
        System.out.println(dataSource);
        System.out.println(dataSource.getConnection());
    }

    @Test//测试转账
    public void test01(){
        userService.accountMoney();

    }

}

出现问题,转账的时候出现异常。怎么解决,用事务。

事务的过程:
在这里插入图片描述

在这里插入图片描述

操作过程:

第一步.xml中创建事务管理器:

在这里插入图片描述
在这里插入图片描述
第二步.xml中开启事务注解:

在这里插入图片描述
数据库的连接信息db.properties:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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.xsd
            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">
    <!--1.加载properties配置文件-->

    <context:component-scan base-package="com.fan"></context:component-scan>
    <context:property-placeholder location="classpath:db.properties" ></context:property-placeholder>
    <!--2.设置数据源c3p0-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--3.将数据源配到jdbcTemplate中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--设置属性-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--有关事务的管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

</beans>

第三步.在类或者方法上使用事务注解@Transactional
在这里插入图片描述

@Service("userService")
@Transactional//为此类添加事务,类中所有方法也添加了事务
public class UserServiceImpl implements UserService {
	...
}

事务配置的参数:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

不可重复读:一个维提交事务读取到另一个提交事务修改的数据
在这里插入图片描述

在这里插入图片描述

mysql默认是的第三个可重读读级别:
在这里插入图片描述

只读设置:
在这里插入图片描述
回滚设置:
在这里插入图片描述

全注解的事务操作tx:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38568503/article/details/114800261