SpringFramework~AOP~事务

本文介绍了事务的概念和特性(ACID),阐述了Spring事务管理接口,包括平台事务管理器、事务定义信息和事务运行状态。还详细讲解了事务属性,如隔离级别、传播行为、超时属性、只读属性和回滚规则,最后介绍了Spring中事务的XML和注解配置方式。

事务概述

事务是逻辑上的一组操作,要么都执行,要么都不执行。

事物的特性(ACID)
  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据保持一致;
  3. 隔离性: 并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
Spring事务管理接口
  • PlatformTransactionManager: (平台)事务管理器

  • TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)

  • TransactionStatus: 事务运行状态

所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。

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

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

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。

  • 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

(2)事务传播行为(为了解决业务层方法之间互相调用的事务问题):

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运 行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

不支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
(3) 事务超时属性(一个事务允许执行的最长时间):

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

(4) 事务只读属性(对事物资源是否执行只读操作):

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。

(5) 回滚规则(定义事务回滚规则):

这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。
但是可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

Spring中的事务XML配置方式

<?xml version="1.0" encoding="UTF-8"?>
<!-- 通过xml配置声明式事务,我们需要添加tx命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.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">

    <!-- 通过xml方式配置Spring声明式事务 配置start -->

    <context:component-scan base-package="com.lanou3g.spring.transaction" />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="teacherDao" class="com.lanou3g.spring.transaction.dao.TeacherDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
    

    <!-- 第一步:配置数据源 -->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="username" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
        <property name="connectionProperties">
            <props>
                <prop key="characterEncoding">${jdbc.characterEncoding}</prop>
            </props>
        </property>
    </bean>

    <!-- 第二步:初始化事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 第三步:配置事务AOP通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" rollback-for="ArithmeticException" />
            <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <!-- 第四步:定义AOP配置(将上面的通知和表达式组装到一起) -->
    <aop:config>
        <aop:pointcut id="all_dao_method" expression="execution(* com.lanou3g.spring.transaction.dao.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="all_dao_method" />
    </aop:config>
    
    <!-- 通过xml方式配置Spring声明式事务 配置结束 -->
</beans>

Spring中的事务注解配置方式

Student实体类

package com.lanou3g.spring.tx;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Student {
    private Integer id;
    private String sname;
    private Integer age;
    private String gender;
    private String nick_name;
    public Student(){

    }
    public Student(String sname,Integer age,String gender,String nick_name){
        this.sname = sname;
        this.age = age;
        this.gender = gender;
        this.nick_name = nick_name;
    }
}

StudentDaoImpl学生接口实现类

package com.lanou3g.spring.tx;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.ExecutionException;

@Setter
@Getter
@Repository
public class StudentDaoImpl {

    @Autowired //DI自动注入
    private JdbcTemplate jdbcTemplate;

    @Transactional(readOnly = true) //开启事务,设置事务属性
    public List<Student> queryAll(){
        List<Student> studentList = jdbcTemplate.query("select * from student",
                new BeanPropertyRowMapper<Student>(Student.class));
        return studentList;
    }

    @Transactional(rollbackFor = {Exception.class})
    public int insertStudent(Student student){
        int rows = jdbcTemplate.update("insert into student(sname,age,gender,nick_name)  values(?,?,?,?)",
                student.getSname(),student.getAge(),student.getGender(),student.getNick_name());
        return rows;
    }
}

APP纯注解入口类

package com.lanou3g.spring;

import com.lanou3g.spring.tx.Student;
import com.lanou3g.spring.tx.StudentDaoImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.List;

/**
 * 纯注解方式入口类
 */
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.tx")
@EnableTransactionManagement    //开启spring事务注解支持
@EnableAspectJAutoProxy     //开启aop注解支持
public class App {

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    /**
     * 定义事务管理器bean
     */
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
        //testTransaction(ctx);
        testQuery(ctx);
    }

    static void testQuery(ApplicationContext ctx) {
        StudentDaoImpl studentDao = ctx.getBean(StudentDaoImpl.class);
        List<Student> studentList = studentDao.queryAll();
        for(Student student : studentList) {
            System.out.println("姓名: " + student.getSname()+", 花名: " + student.getNick_name());
        }
    }

    static void testTransaction(ApplicationContext ctx) {
        StudentDaoImpl studentDao = ctx.getBean(StudentDaoImpl.class);
        int rowEffect = studentDao.insertStudent(
                new Student("刘自豪",25,"男","胡萝卜"));
        System.out.println("影响了" + rowEffect + "行.");
    }

}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值