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库来完成。