首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【愚公系列】2023年04月 Java教学课程 134-Spring框架的事务

【愚公系列】2023年04月 Java教学课程 134-Spring框架的事务

作者头像
愚公搬代码
发布2023-04-16 16:21:10
发布2023-04-16 16:21:10
5990
举报
文章被收录于专栏:历史专栏历史专栏


一、事务

1.事务回顾

1.1 什么是事务

事务是指数据库中一组操作,这些操作要么全部被执行,要么全部不被执行。在事务中,如果任何一个操作失败,则整个事务都会被回滚,即所有操作都不会生效。这样可以保证数据库的一致性和完整性。

1.2 事务的作用

事务是指一组操作被看作是一个单独的工作单元,这组操作要么全部完成,要么全部不完成,不会出现部分完成的情况。事务的作用主要包括以下几个方面:

  • 原子性:事务中的所有操作要么全部成功,要么全部失败,不会出现部分成功的情况。
  • 一致性:事务执行前后,数据库的状态应该保持一致,即符合数据库的约束条件。
  • 隔离性:多个事务并发执行时,每个事务都应该感觉不到其他事务的存在。
  • 持久性:一旦事务提交,数据库的修改应该是永久性的,即使系统故障也不会丢失。

以上是事务的主要作用,通过使用事务可以保证数据的完整性和一致性,提高数据库的并发能力和可靠性。

1.3 事务的隔离级

  • 脏读:允许读取未提交的信息
    • 原因:Read uncommitted

    解决方案: (表级读锁)

  • 不可重复读:读取过程中单个数据发生了变化
    • 解决方案: Repeatable read (行级写锁)
  • 幻读:读取过程中数据条目发生了变化
    • 解决方案: Serializable(表级写锁)

2.事务管理

2.1 Spring事务核心对象

Spring事务是指基于Spring框架实现的一种事务管理机制,它可以保证数据操作的一致性和完整性。通过使用Spring的事务管理,我们可以很方便地实现声明式事务,避免手动管理事务的繁琐和容易出错。同时,Spring事务还支持多种事务隔离级别和传播行为,可以根据需求进行灵活配置。

Spring为业务层提供了整套的事务解决方案

  • PlatformTransactionManager
  • TransactionDefinition
  • TransactionStatus

2.2 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事务中

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

获取事务 :

代码语言:javascript
复制
TransactionStatus getTransaction(TransactionDefinition definition)

提交事务 :

代码语言:javascript
复制
void commit(TransactionStatus status) 

回滚事务 :

代码语言:javascript
复制
void rollback(TransactionStatus status)

2.3 TransactionDefinition

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

获取事务定义名称

代码语言:javascript
复制
String getName()

获取事务的读写属性

代码语言:javascript
复制
boolean isReadOnly()

获取事务隔离级别

代码语言:javascript
复制
int getIsolationLevel()

获事务超时时间

代码语言:javascript
复制
int getTimeout()

获取事务传播行为特征

代码语言:javascript
复制
int getPropagationBehavior()

2.4 TransactionStatus

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

2.5 事务控制方式

  • 编程式
  • 声明式(XML)
  • 声明式(注解)

2.6 案例说明

2.6.1 案例说明

银行转账业务说明

银行转账操作中,涉及从A账户到B账户的资金转移操作。数据层仅提供单条数据的基础操作,未设计多账户间的业务操作。

2.6.2 案例环境(基于Spring、Mybatis整合)
  • 业务层接口提供转账操作
代码语言:javascript
复制
/**
* 转账操作
* @param outName     出账用户名
* @param inName      入账用户名
* @param money       转账金额
*/
public void transfer(String outName,String inName,Double money);
  • 业务层实现提供转账操作
代码语言:javascript
复制
public void transfer(String outName,String inName,Double money){
    accountDao.inMoney(outName,money);                                                       accountDao.outMoney(inName,money);
}
  • 数据层提供对应的入账与出账操作
代码语言:javascript
复制
<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>
2.6.3 编程式事务
代码语言:javascript
复制
public void transfer(String outName,String inName,Double money){
    //创建事务管理器
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    //为事务管理器设置与数据层相同的数据源
    dstm.setDataSource(dataSource);
    //创建事务定义对象
    TransactionDefinition td = new DefaultTransactionDefinition();
    //创建事务状态对象,用于控制事务执行
    TransactionStatus ts = dstm.getTransaction(td);
    accountDao.inMoney(outName,money);
    int i = 1/0;    //模拟业务层事务过程中出现错误
    accountDao.outMoney(inName,money);
    //提交事务
    dstm.commit(ts);
}

2.7 使用AOP控制事务

将业务层的事务处理功能抽取出来制作成AOP通知,利用环绕通知运行期动态织入

代码语言:javascript
复制
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;
}

配置AOP通知类,并注入dataSource

代码语言:javascript
复制
<bean id="txAdvice" class="com.itheima.aop.TxAdvice">
    <property name="dataSource" ref="dataSource"/>
</bean>

使用环绕通知将通知类织入到原始业务对象执行过程中

代码语言:javascript
复制
<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
    <aop:aspect ref="txAdvice">
        <aop:around method="tx" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

2.8 声明式事务(XML)

声明式事务是建立在AOP之上的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

代码语言:javascript
复制
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

使用tx命名空间配置事务专属通知类

代码语言:javascript
复制
<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配置中引用事务专属通知类

代码语言:javascript
复制
<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
2.8.1 aop:advice与aop:advisor区别
  • aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系
  • aop:advisor配置的通知类必须实现通知接口
    • MethodBeforeAdvice
    • AfterReturningAdvice
    • ThrowsAdvice
    • ……
2.8.2)tx配置----tx:advice

名称:tx:advice

类型:标签

归属:beans标签

作用:专用于声明事务通知

格式:

代码语言:javascript
复制
<beans>
    <tx:advice id="txAdvice" transaction-manager="txManager">
    </tx:advice>
</beans>

基本属性:

  • id :用于配置aop时指定通知器的id
  • transaction-manager :指定事务管理器bean
2.8.3)tx配置----tx:attributes

名称:tx:attributes

类型:标签

归属:tx:advice标签

作用:定义通知属性

格式:

代码语言:javascript
复制
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    </tx:attributes>
</tx:advice>

基本属性:

2.8.4)tx配置----tx:method

名称:tx:method

类型:标签

归属:tx:attribute标签

作用:设置具体的事务属性

格式:

代码语言:javascript
复制
<tx:attributes>
    <tx:method name="*" read-only="false" />
    <tx:method name="get*" read-only="true" />
</tx:attributes>

说明:

通常事务属性会配置多个,包含1个读写的全事务属性,1个只读的查询类事务属性

tx:method属性

2.9 事务传播行为

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

2.10 事务传播行为

2.11 事务传播应用

  • 场景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需要新事务)

2.12 声明式事务(注解)

2.12.1 @Transactional

名称:@Transactional

类型:方法注解,类注解,接口注解

位置:方法定义上方,类定义上方,接口定义上方

作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性

范例:

代码语言:javascript
复制
@Transactional(
    readOnly = false,
    timeout = -1,
    isolation = Isolation.DEFAULT,
    rollbackFor = {ArithmeticException.class, IOException.class},
    noRollbackFor = {},
    propagation = Propagation.REQUIRES_NEW
)
2.12.2 tx:annotation-driven

名称:tx:annotation-driven

类型:标签

归属:beans标签

作用:开启事务注解驱动,并指定对应的事务管理器

范例:

代码语言:javascript
复制
<tx:annotation-driven transaction-manager="txManager"/>

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

名称:@EnableTransactionManagement

类型:类注解

位置:Spring注解配置类上方

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

范例:

代码语言:javascript
复制
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
代码语言:javascript
复制
public class TransactionManagerConfig {
    @Bean
    public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

3.模板对象

3.1 Spring模块对象

  • TransactionTemplate
  • JdbcTemplate
  • RedisTemplate
  • RabbitTemplate
  • JmsTemplate
  • HibernateTemplate
  • RestTemplate
代码语言:javascript
复制
public class JDBCConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    //注册JdbcTemplate模块对象bean
    @Bean("jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean("jdbcTemplate2")
    public NamedParameterJdbcTemplate getJdbcTemplate2(@Autowired DataSource dataSource){
        return new NamedParameterJdbcTemplate(dataSource);
    }
}

3.2 JdbcTemplate

提供标准的sql语句操作API

代码语言:javascript
复制
public void save(Account account) {
    String sql = "insert into account(name,money)values(?,?)";
    jdbcTemplate.update(sql,account.getName(),account.getMoney());
}

3.3 NamedParameterJdbcTemplate

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

代码语言:javascript
复制
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);
}

3.4 RedisTemplate

代码语言:javascript
复制
@PropertySource("redis.properties")
public class RedisConfig {

    @Value("${redis.host}")
    private String hostName;

    @Value("${redis.port}")
    private Integer port;

//    @Value("${redis.password}")
//    private String password;

    @Value("${redis.maxActive}")
    private Integer maxActive;
    @Value("${redis.minIdle}")
    private Integer minIdle;
    @Value("${redis.maxIdle}")
    private Integer maxIdle;
    @Value("${redis.maxWait}")
    private Integer maxWait;



    @Bean
    //配置RedisTemplate
    public RedisTemplate createRedisTemplate(RedisConnectionFactory redisConnectionFactory){
        //1.创建对象
        RedisTemplate redisTemplate = new RedisTemplate();
        //2.设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //3.设置redis生成的key的序列化器,对key编码进行处理
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        //4.返回
        return redisTemplate;
    }

    @Bean
    //配置Redis连接工厂
    public RedisConnectionFactory createRedisConnectionFactory(RedisStandaloneConfiguration redisStandaloneConfiguration,GenericObjectPoolConfig genericObjectPoolConfig){
        //1.创建配置构建器,它是基于池的思想管理Jedis连接的
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder builder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
        //2.设置池的配置信息对象
        builder.poolConfig(genericObjectPoolConfig);
        //3.创建Jedis连接工厂
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration,builder.build());
        //4.返回
        return jedisConnectionFactory;
    }

    @Bean
    //配置spring提供的Redis连接池信息
    public GenericObjectPoolConfig createGenericObjectPoolConfig(){
        //1.创建Jedis连接池的配置对象
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        //2.设置连接池信息
        genericObjectPoolConfig.setMaxTotal(maxActive);
        genericObjectPoolConfig.setMinIdle(minIdle);
        genericObjectPoolConfig.setMaxIdle(maxIdle);
        genericObjectPoolConfig.setMaxWaitMillis(maxWait);
        //3.返回
        return genericObjectPoolConfig;
    }


    @Bean
    //配置Redis标准连接配置对象
    public RedisStandaloneConfiguration createRedisStandaloneConfiguration(){
        //1.创建Redis服务器配置信息对象
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        //2.设置Redis服务器地址,端口和密码(如果有密码的话)
        redisStandaloneConfiguration.setHostName(hostName);
        redisStandaloneConfiguration.setPort(port);
//        redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
        //3.返回
        return redisStandaloneConfiguration;
    }

}

RedisTemplate对象结构

代码语言:javascript
复制
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());
}

4.事务底层原理解析

4.1 策略模式应用

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 一、事务
    • 1.事务回顾
      • 1.1 什么是事务
      • 1.2 事务的作用
      • 1.3 事务的隔离级
    • 2.事务管理
      • 2.1 Spring事务核心对象
      • 2.2 PlatformTransactionManager
      • 2.3 TransactionDefinition
      • 2.4 TransactionStatus
      • 2.5 事务控制方式
      • 2.6 案例说明
      • 2.7 使用AOP控制事务
      • 2.8 声明式事务(XML)
      • 2.9 事务传播行为
      • 2.10 事务传播行为
      • 2.11 事务传播应用
      • 2.12 声明式事务(注解)
      • 2.13 声明式事务(纯注解驱动)
    • 3.模板对象
      • 3.1 Spring模块对象
      • 3.2 JdbcTemplate
      • 3.3 NamedParameterJdbcTemplate
      • 3.4 RedisTemplate
    • 4.事务底层原理解析
      • 4.1 策略模式应用
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档