Spring事务----事务管理----编程式事务----Aop控制事务----声明式事务(xml和注解)----模板对象----事务底层原理

事务的介绍:#

什么是事务?#

事务指数据库中多个操作合并在一起形成的操作序列。

事务的作用: #

1.当数据库操作序列中个别操作失败时,提供一种方式使数据库状态恢复到正常状态(A),保障数据库即使在异常状态下仍能保持数据一致性(C)(要么操作前状态,要么操作后状态)。

2.当出现并发访问数据库时,在多个访问间进行相互隔离,防止并发访问操作结果互相干扰(I)。

事务特征(ACID):#

  - 原子性(Atomicity)指事务是一个不可分割的整体,其中的操作要么全执行或全不执行

  - 一致性(Consistency)事务前后数据的完整性必须保持一致

  - 隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离

  - 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务的隔离级别:#

脏读:允许读取未提交的信息

- 原因:Read uncommitted

解决方案: (表级读锁)

不可重复读:读取过程中单个数据发生了变化

- 解决方案: Repeatable read (行级写锁)

- 幻读:读取过程中数据条目发生了变化

- 解决方案: Serializable(表级写锁)

事务管理:#

Spring事务核心对象:#

- J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者数据层并无太大差别,当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操作进行组合并归属于同一个事务进行处理。
- Spring为业务层提供了整套的事务解决方案:

PlatformTransactionManager
TransactionDefinition
TransactionStatus

PlatformTransactionManager(平台事务管理器)接口:#

- 平台事务管理器实现类:   

- DataSourceTransactionManager 适用于Spring JDBC或MyBatis
- HibernateTransactionManager 适用于Hibernate3.0及以上版本
- JpaTransactionManager 适用于JPA
- JdoTransactionManager 适用于JDO
- JtaTransactionManager 适用于JTA

- JPA(Java Persistence API)Java EE 标准之一,为POJO提供持久化标准规范,并规范了持久化开发的统一API,符合JPA规范的开发可以在不同的JPA框架下运行

- JDO(Java Data Object )是Java对象持久化规范,用于存取某种数据库中的对象,并提供标准化API。与JDBC相比,JDBC仅针对关系数据库进行操作,JDO可以扩展到关系数据库、文件、XML、对象数据库(ODBMS)等,可移植性更强

- JTA(Java Transaction API)Java EE 标准之一,允许应用程序执行分布式事务处理。与JDBC相比,JDBC事务则被限定在一个单一的数据库连接,而一个JTA事务可以有多个参与者,比如JDBC连接、JDO 都可以参与到一个JTA事务中

此接口定义了事务的基本操作:#

//- 获取事务 :
  TransactionStatus getTransaction(TransactionDefinition definition)
//- 提交事务 :
  void commit(TransactionStatus status) 
//- 回滚事务 :
  void rollback(TransactionStatus status)

TransactionDefinition此接口定义了事务的基本信息:#

复制代码
// - 获取事务定义名称
  String getName()
//- 获取事务的读写属性
  boolean isReadOnly()
//- 获取事务隔离级别
  int getIsolationLevel()
//- 获事务超时时间
  int getTimeout()
//- 获取事务传播行为特征
  int getPropagationBehavior()
复制代码

TransactionStatus此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作:#

事务控制方式:#

- 编程式

- 声明式(XML)

- 声明式(注解)

案例环境(基于Spring、Mybatis整合):#

复制代码
//业务层接口提供转账操作:
/**
* 转账操作
* @param outName     出账用户名
* @param inName      入账用户名
* @param money       转账金额
*/
public void transfer(String outName,String inName,Double money);

//业务层实现提供转账操作:
public void transfer(String outName,String inName,Double money){
    accountDao.inMoney(outName,money);
  accountDao.outMoney(inName,money); }
//数据层提供对应的入账与出账操作: <update id="inMoney"> update account set money = money + #{money} where name = #{name} </update> <update id="outMoney"> update account set money = money - #{money} where name = #{name} </update>
复制代码

编程式事务和Aop控制事务使用案例:#

相关左边引入:#

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>Spring_day04_01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

    </dependencies>
</project>
pom.xml
复制代码

jdbc相关参数配置:#

复制代码
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2
jdbc.username=root
jdbc.password=123456
src/main/resources/jdbc.properties
复制代码

spring核心配置文件:#

复制代码
<?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:aop="http://www.springframework.org/schema/aop"
       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/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.itheima.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.dao"/>
    </bean>


    <!--###########################################-->

    <bean id="txAdvice" class="com.itheima.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

</beans>
src/main/resources/applicationContext.xml
复制代码

mybatis映射配置文件:#

复制代码
<?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.itheima.dao.AccountDao">

    <update id="inMoney">
        update account set money = money + #{money} where name = #{name}
    </update>

    <update id="outMoney">
        update account set money = money - #{money} where name = #{name}
    </update>

</mapper>
src/main/resources/com/itheima/dao/AccountDao.xml
复制代码

mybatis映射接口:#

复制代码
package com.itheima.dao;

import com.itheima.domain.Account;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface AccountDao {
    /**
     * 入账操作
     *
     * @param name  入账用户名
     * @param money 入账金额
     */
    void inMoney(@Param("name") String name, @Param("money") Double money);

    /**
     * 出账操作
     *
     * @param name  出账用户名
     * @param money 出账金额
     */
    void outMoney(@Param("name") String name, @Param("money") Double money);

}
src/main/java/com/itheima/dao/AccountDao.java
复制代码

实体对象:#

复制代码
package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
src/main/java/com/itheima/domain/Account.java
复制代码

service业务层接口:#

复制代码
package com.itheima.service;

import com.itheima.domain.Account;

import java.util.List;

public interface AccountService {

    /**
     * 转账操作
     * @param outName     出账用户名
     * @param inName      入账用户名
     * @param money       转账金额
     */
    public void transfer(String outName, String inName, Double money);

}
src/main/java/com/itheima/service/AccountService.java
复制代码

service业务层接口实现类:#

复制代码
package com.itheima.service.impl;


import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

public class AccountServiceImpl implements AccountService {

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

    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void transfer(String outName, String inName, Double money) {
        accountDao.inMoney(outName,money);
////        int i = 1/0;
        accountDao.outMoney(inName,money);
    }


    //编程式事务
//    public void transfer(String outName, String inName, Double money) {
//        //开启事务
//        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
//        //事务定义
//        TransactionDefinition td = new DefaultTransactionDefinition();
//        //事务状态
//        TransactionStatus ts = ptm.getTransaction(td);
//
//        accountDao.inMoney(outName,money);
//        int i = 1/0;
//        accountDao.outMoney(inName,money);
//
//        //提交事务
//        ptm.commit(ts);
//    }

}
src/main/java/com/itheima/service/impl/AccountServiceImpl.java
复制代码

测试:#

复制代码
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.PlatformTransactionManager;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = (AccountService) ctx.getBean("accountService");
        accountService.transfer("Jock1","Jock2",100D);
    }
}
src/main/java/App.java
复制代码

声明式事务(XML)#

复制代码
//AOP配置事务是否具有特例性?
public Object tx(ProceedingJoinPoint pjp) throws Throwable {
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    dstm.setDataSource(dataSource);
    TransactionDefinition td = new DefaultTransactionDefinition();
    TransactionStatus ts = dstm.getTransaction(td);
    Object ret = pjp.proceed(pjp.getArgs());
    dstm.commit(ts);

    return ret;
}

<bean id="txAdvice" class="com.itheima.aop.TxAdvice">
    <property name="dataSource" ref="dataSource"/>
</bean>

//选择适当的的事务管理器类:
 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource"/>
 </bean>

// 使用tx命名空间配置事务专属通知类:
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" read-only="false" />
        <tx:method name="get*" read-only="true" />
        <tx:method name="find*" read-only="true" />
    </tx:attributes>
</tx:advice>

// 使用aop:advisor在AOP配置中引用事务专属通知类:
<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
复制代码

aop:advice与aop:advisor区别#

复制代码
//- aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系

//- aop:advisor配置的通知类必须实现通知接口:

  - MethodBeforeAdvice

  - AfterReturningAdvice

  - ThrowsAdvice
复制代码

tx配置----tx:advice#

复制代码
<!--
- 名称:tx:advice
- 类型:标签
- 归属:beans标签
- 作用:专用于声明事务通知
- 格式:-->
  <beans>
      <tx:advice id="txAdvice" transaction-manager="txManager">
      </tx:advice>
  </beans>
<!--
- 基本属性:
  - id :用于配置aop时指定通知器的id
  - transaction-manager :指定事务管理器bean
-->
复制代码

tx配置----tx:attributes#

复制代码
<!--
- 名称:tx:attributes
- 类型:**标签**
- 归属:tx:advice标签
- 作用:定义通知属性
- 格式:-->
  <tx:advice id="txAdvice" transaction-manager="txManager">
      <tx:attributes>
      </tx:attributes>
  </tx:advice>
<!--
- 基本属性:
  - 无
-->
复制代码

tx配置----tx:method#

复制代码
<!--
- 名称:tx:method
- 类型:标签
- 归属:tx:attribute标签
- 作用:设置具体的事务属性
- 格式:-->
  <tx:attributes>
      <tx:method name="*" read-only="false" />
      <tx:method name="get*" read-only="true" />
  </tx:attributes>
<!--
- 说明:
    通常事务属性会配置多个,包含1个读写的全事务属性,1个只读的查询类事务属性
-->
复制代码

tx:method属性

事务传播行为#

//- 事务管理员
//- 事务协调员

事务传播行为描述的是事务协调员对事务管理员所携带事务的处理态度#

事务传播行为:#

事务传播应用#

复制代码
/*
- 场景A:生成订单业务
  - 子业务S1:记录日志到数据库表X
  - 子业务S2:保存订单数据到数据库表Y
  - 子业务S3:……
  - 如果S2或S3或……事务提交失败,此时S1是否回滚?如何控制?
  - (S1需要新事务)

- 场景B:生成订单业务
  - 背景1:订单号生成依赖数据库中一个专门用于控制订单号编号生成的表M获取
  - 背景2:每次获取完订单号,表M中记录的编号自增1
  - 子业务S1:从表M中获取订单编号
  - 子业务S2:保存订单数据,订单编号来自于表M
  - 子业务S3:……
  - 如果S2或S3或……事务提交失败,此时S1是否回滚?如何控制?
  - (S1需要新事务)
*/
复制代码

声明式事务(注解)#

@Transactional#

复制代码
/*
- 名称:@Transactional
- 类型:方法注解,类注解,接口注解
- 位置:方法定义上方,类定义上方,接口定义上方
- 作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性
- 范例:*/
  @Transactional(
      readOnly = false,
      timeout = -1,
      isolation = Isolation.DEFAULT,
      rollbackFor = {ArithmeticException.class, IOException.class},
      noRollbackFor = {},
      propagation = Propagation.REQUIRES_NEW
  )
复制代码

tx:annotation-driven#

/*
- 名称:tx:annotation-driven
- 类型:标签
- 归属:beans标签
- 作用:开启事务注解驱动,并指定对应的事务管理器
- 范例:
  <tx:annotation-driven transaction-manager="txManager"/>
*/

声明式事务(纯注解驱动)#

复制代码
- 名称:@EnableTransactionManagement

- 类型:**类注解**

- 位置:Spring注解配置类上方

- 作用:开启注解驱动,等同XML格式中的注解驱动

/*
- 范例:
  @Configuration
  @ComponentScan("com.itheima")
  @PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
  @EnableTransactionManagement
  public class SpringConfig {
  }
*/

  public class TransactionManagerConfig {
      @Bean
      public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
          return new DataSourceTransactionManager(dataSource);
      }
  }
复制代码

模板对象#

Spring模块对象#

复制代码
/*
- TransactionTemplate

- JdbcTemplate

- RedisTemplate

- RabbitTemplate

- JmsTemplate

- HibernateTemplate

- RestTemplate
*/
复制代码

JdbcTemplate(了解)#

提供标准的sql语句操作API:#

public void save(Account account) {
    String sql = "insert into account(name,money)values(?,?)";
    jdbcTemplate.update(sql,account.getName(),account.getMoney());
}

NamedParameterJdbcTemplate(了解)#

提供标准的具名sql语句操作API#

public void save(Account account) {
    String sql = "insert into account(name,money)values(:name,:money)";
    Map pm = new HashMap();
    pm.put("name",account.getName());
    pm.put("money",account.getMoney());
    jdbcTemplate.update(sql,pm);
}

RedisTemplate#

RedisTemplate对象结构#

public void changeMoney(Integer id, Double money) {
    redisTemplate.opsForValue().set("account:id:"+id,money);
}
public Double findMondyById(Integer id) {
    Object money = redisTemplate.opsForValue().get("account:id:" + id);
    return new Double(money.toString());
}

事务底层原理解析#

//策略模式(Strategy Pattern)使用不同策略的对象实现不同的行为方式,策略对象的变化导致行为的变化。

 

posted @   1640808365  阅读(123)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
主题色彩