2021-08-07阅读小笔记:Spring 之 事务介绍

1、编程式事务需要的核心组件有哪些?

主要有 DataSourceTransactionManager 和 TransactionTemplate。

  • DataSourceTransactionManager:事务管理器,它负责控制事务;创建事务管理器需要传入DataSource
  • TransactionTemplate:事务模版,使用它可以完成编程式事务;如果需要使用事务,利用TransactionTemplate的execute来包裹业务逻辑

2、声明式事务有哪些编写方法?它们都需要什么核心组件?

声明式事务的编写方法有:基于xml配置文件和基于注解驱动。不管是哪种编写方法,都需要注入事务管理器到容器中。

3、如何理解事务传播行为?事务传播行为都有哪些类型?

事务传播行为发生在方法调用中,被调用方如何对待调用方的事务使用情况。

事务传播行为分为以下七种:

3.1 REQUIRED:必需的【默认值】

这是 SpringFramework 中事务传播行为的默认行为,它的定义是:如果当前没有事务运行,则会开启一个新的事务;如果当前已经有事务运行,则方法会运行在当前事务中。简单的概括:你没有,我开启;你有了,我加入。

3.2 REQUIRES_NEW :新事务

新事务,顾名思义,它必须要一个全新的事务,那它的定义就可以描述为:如果当前没有事务运行,则会开启一个新的事务;如果当前已经有事务运行,则会将原事务挂起(暂停),重新开启一个新的事务。当新的事务运行完毕后,再将原来的事务释放。简单的概括:你没有,我开启;你有了,我造新的。

3.3 SUPPORTS :支持

支持,这个词跟必需一对比,轻重程度一目了然。支持的定义是:如果当前有事务运行,则方法会运行在当前事务中;如果当前没有事务运行,则不会创建新的事务(即不运行在事务中)。很明显,支持更倾向于一种无所谓的态度,所以简单概括就是:有就有,没有拉倒。

3.4 NOT_SUPPORTED :不支持

不支持,显然跟上面是完全相反的,它的定义是:如果当前有事务运行,则会将该事务挂起(暂停);如果当前没有事务运行,则它也不会运行在事务中。这态度更无所谓了,有事务它反而不稀罕,简单概括下就是:有我不要,没有正好。

3.5 MANDATORY :强制

强制,听起来这个态度就很着急很强硬,它表示的意思是:当前方法必须运行在事务中,如果没有事务,则直接抛出异常。好家伙这也太凶了,如果当前方法执行的时候没有事务,它直接不干活了。所以咱简单概括下:要干活就必须有,没有就打死不干。

3.6 NEVER :不允许

又是一对完全相反的设计,NEVER 定义的是:当前方法不允许运行在事务中,如果当前已经有事务运行,则抛出异常。这家伙跟上面的 MANDATORY 一个货色,只不过两个态度是完全相反的,它的简单概括是:要干活就不准有,有的话就不干活。

3.7 NESTED :嵌套

这个 NESTED 是最特殊的,它就是基于保存点 SavePoint 的传播行为。它的定义是:如果当前没有事务运行,则开启一个新的事务;如果当前已经有事务运行,则会记录一个保存点,并继续运行在当前事务中。如果子事务运行中出现异常,则不会全部回滚,而是回滚到上一个保存点。可以发现,这个设计就是保存点的设计,所以简单概括就可以是:你没有,我开启,你有了,你记下;我走了,你再走,我挂了,就当无事发生。

4、SpringFramework 的事务有哪三大核心?

  • PlatformTransactionManager :平台事务管理器
  • TransactionDefinition :事务定义
  • TransactionStatus :事务状态

SpringFramework 对于事务的控制,可以理解为事务管理器,可以根据事务的定义,获取 / 控制事务的状态。

5、事务监听器的设计是什么?事务监听器可以监听哪些时机的事件?

事务监听器是基于SpringFramework的监听器扩展的,多了几个属性,其中最关键的属性是:phase。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {
    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;

    boolean fallbackExecution() default false;

    @AliasFor(
        annotation = EventListener.class,
        attribute = "classes"
    )
    Class<?>[] value() default {};

    @AliasFor(
        annotation = EventListener.class,
        attribute = "classes"
    )
    Class<?>[] classes() default {};

    String condition() default "";
}

phase 它代表的是在事务的哪个阶段触发监听,默认值是 AFTER_COMMIT ,代表提交之后触发。这个触发时机共有 4 个可选择的范围:

  • BEFORE_COMMIT :事务提交之前触发监听
  • AFTER_COMMIT :事务提交之后触发监听
  • AFTER_ROLLBACK :事务回滚之后触发监听
  • AFTER_COMPLETION :事务完成之后触发监听(无论提交或回滚均触发)

实践

实体类、Dao、Service:

Service 中会利用 ApplicationEventPubliser 来发布事件。

@Data
@Accessors(chain = true)
public class User {

    private Long id;
    private String name;
    private String gender;
    private String userType;
}

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 添加用户
     * @param user
     * @return
     */
    public int addUser(User user){
        int result = this.jdbcTemplate.update("insert into user(name,gender,user_type) values(?,?,?)",user.getName(),user.getGender(),user.getUserType());
        System.out.println("插入成功,用户信息:"+user);
        return result;
    }
}

@Service
public class UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    ApplicationEventPublisher eventPublisher;

    /**
     * 添加用户
     * @param user
     * @return
     */
    @Transactional
    public int addUser(User user){
        // 保存用户
        int result = this.userDao.addUser(user);
        // 发布事件
        this.eventPublisher.publishEvent(user);
        return result;
    }
}

配置类:
配置类主要是是扫描组件、开启事务功能、配置数据源&JdbcTemplate&TransactionManager、添加事务监听器;我们会发现,@TransactionEventListener 都会加上@Order 注解,强制来保证执行顺序。

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "com.github.howinfun.demo.jdbc")
public class TransactionEventListenerConfiguration {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?characterEncoding=utf8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

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

    @Bean
    public TransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    /**
     * 不用编译器反射出来的顺序都不一样,所以为了保证执行顺序,我们需要使用@Order注解
     * @param event
     */
    @Order(1)
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void before(PayloadApplicationEvent<User> event){
        System.out.println("接收事务消息-事务提交前:"+event.getPayload());
    }
    @Order(2)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void after(PayloadApplicationEvent<User> event){
        System.out.println("接收事务消息-事务提交后:"+event.getPayload());
    }
    @Order(3)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void completion(PayloadApplicationEvent<User> event){
        System.out.println("接收事务消息-事务完成后:"+event.getPayload());
    }
    @Order(4)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void rollback(PayloadApplicationEvent<User> event){
        System.out.println("接收事务消息-事务回滚后:"+event.getPayload());
    }
}

测试应用:

public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TransactionEventListenerConfiguration.class);
        UserService userService = applicationContext.getBean(UserService.class);
        userService.addUser(new User().setName("阿黄2").setGender("m").setUserType("1"));
    }
}

结果打印:

插入成功,用户信息:User(id=null, name=阿黄2, gender=m, userType=1)
接收事务消息-事务提交前:User(id=null, name=阿黄2, gender=m, userType=1)
接收事务消息-事务提交后:User(id=null, name=阿黄2, gender=m, userType=1)
接收事务消息-事务完成后:User(id=null, name=阿黄2, gender=m, userType=1)

实战之后给我最大的感觉:@TransactionEventListener的phase没啥作用,最多只是能判断AFTER_ROLLBACK是否会触发而已~

6、什么是分布式事务?

事务指的是基于关系型数据库的本地事务,而分布式事务指的是不同应用/关系型数据库之间,通过网络远程写作完成的事务。

  • 不同应用操作同一个数据库
  • 不同应用操作不同的数据库
  • 同一个应用操作不同的数据库

7、如何理解 2PC 、XA 、JTA ?

2PC是一种分布式事务的协议,即两阶段提交;它将一个分布式事务拆分为两个阶段:准备阶段(prepare)和提交阶段(commit)

XA是一种基于2PC协议的交互接口规范,它不是整个分布式方案的代名词。

JTA是XA方案的Java实现,全称 Java Transaction Api;现有借助外部Web容器和借助第三方JTA库两种实现方式。

8、SpringFramework 中的 JTA 如何应用到项目中?

JTA中三个必备角色:全局事务管理器、JTA资源管理器、资源服务器

在 SpringFramework 中,提供了事务管理器和资源服务器,JTA资源管理器需借助上面提到的外部Web容器或者第三方JTA库来完成。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值