Java开发面试问题,Java中高级核心知识全面解析(10)

}

## 2. Spring 事务管理接口介绍
Spring 框架中,事务管理相关最重要的 3 个接口如下:

 - `PlatformTransactionManager` : (平台)事务管理器,Spring 事务策略的核心。
 - `TransactionDefinition` : 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
 - `TransactionStatus` : 事务运行状态。


我们可以把 `PlatformTransactionManager` 接口可以被看作是事务上层的管理者,而 `TransactionDefinition 和 TransactionStatus` 这两个接口可以看作是事物的描述。

`PlatformTransactionManager` 会根据 `TransactionDefinition` 的定义比如事务超时时间、隔离界别、传播行为等来进行事务管理 ,而 `TransactionStatus` 接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。
### 1)PlatformTransactionManager:事务管理接口
**Spring 并不直接管理事务,而是提供了多种事务管理器** 。Spring 事务管理器的接口是:`PlatformTransactionManager` 。

通过这个接口,Spring 为各个平台如 JDBC( `DataSourceTransactionManager` )、Hibernate( `HibernateTransactionManager` )、JPA( `JpaTransactionManager `)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

`PlatformTransactionManager` **接口的具体实现如下:**
![](https://upload-images.jianshu.io/upload_images/25222111-7f456d31d5a95825?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
`PlatformTransactionManager` 接口中定义了三个方法:
```java
package org.springframework.transaction; 

import org.springframework.lang.Nullable; 

public interface PlatformTransactionManager { 
	//获得事务 
	TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; 
	//提交事务 
	void commit(TransactionStatus var1) throws TransactionException; 
	//回滚事务 
	void rollback(TransactionStatus var1) throws TransactionException; 
}

这里多插一嘴。为什么要定义或者说抽象出来 PlatformTransactionManager 这个接口呢?

主要是因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。我前段时间分享过:“为什么我们要用接口?”

2)TransactionDefinition:事务属性

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition)方法来得到一个事务,这个方法里面的参数是 TransactionDefinition 类 ,这个类就定义了一些基本的事务属性。

那么什么是 事务属性 呢?

事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。

事务属性包含了 5 个方面:

TransactionDefinition 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。

package org.springframework.transaction; 

import org.springframework.lang.Nullable; 

public interface TransactionDefinition { 
	int PROPAGATION_REQUIRED = 0; 
	int PROPAGATION_SUPPORTS = 1; 
	int PROPAGATION_MANDATORY = 2; 
	int PROPAGATION_REQUIRES_NEW = 3; 
	int PROPAGATION_NOT_SUPPORTED = 4; 
	int PROPAGATION_NEVER = 5; 
	int PROPAGATION_NESTED = 6; 
	int ISOLATION_DEFAULT = -1; 
	int ISOLATION_READ_UNCOMMITTED = 1; 
	int ISOLATION_READ_COMMITTED = 2; 
	int ISOLATION_REPEATABLE_READ = 4; 
	int ISOLATION_SERIALIZABLE = 8; 
	int TIMEOUT_DEFAULT = -1; 
	// 返回事务的传播行为,默认值为 REQUIRED。 
	int getPropagationBehavior(); 
	//返回事务的隔离级别,默认值是 DEFAULT 
	int getIsolationLevel(); 
	// 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 
	int getTimeout(); 
	// 返回是否为只读事务,默认值为 false 
	boolean isReadOnly(); 
	
	@Nullable 
	String getName(); 
}

3)TransactionStatus:事务状态

TransactionStatus 接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。

PlatformTransactionManager.getTransaction(…)方法返回一个TransactionStatus 对象。

TransactionStatus 接口接口内容如下:

public interface TransactionStatus{ 
	boolean isNewTransaction(); // 是否是新的事物 
	boolean hasSavepoint(); // 是否有恢复点 
	void setRollbackOnly(); // 设置为只回滚 
	boolean isRollbackOnly(); // 是否为只回滚 
	boolean isCompleted; // 是否已完成 
}

3.事务属性详解

实际业务开发中,大家一般都是使用 @Transactional 注解来开启事务,很多人并不清楚这个参数里 面的参数是什么意思,有什么用。为了更好的在项目中使用事务管理,强烈推荐好好阅读一下下面的内容。

1)事务传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题。

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

举个例子!

我们在 A 类的 aMethod()方法中调用了 B 类的 bMethod() 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 bMethod() 如果发生异常需要回滚,如何配置事务传播行为才能让aMethod() 也跟着回滚呢?这个时候就需要事务传播行为的知识了,如果`你不知道的话一定要好好看一下。

Class A { 
	@Transactional(propagation=propagation.xxx) 
	public void aMethod { 
		//do something 
		B b = new B(); 
		b.bMethod(); 
	} 
}

Class B { 
	@Transactional(propagation=propagation.xxx) 
	public void bMethod { 
		//do something 
	} 
}

TransactionDefinition 定义中包括了如下几个表示传播行为的常量:

public interface TransactionDefinition { 
	int PROPAGATION_REQUIRED = 0; 
	int PROPAGATION_SUPPORTS = 1; 
	int PROPAGATION_MANDATORY = 2; 
	int PROPAGATION_REQUIRES_NEW = 3; 
	int PROPAGATION_NOT_SUPPORTED = 4; 
	int PROPAGATION_NEVER = 5; 
	int PROPAGATION_NESTED = 6; 
	...... 
}

不过如此,为了方便使用,Spring会相应地定义了一个枚举类:Propagation

package org.springframework.transaction.annotation; 

import org.springframework.transaction.TransactionDefinition; 

public enum Propagation { 
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), 
	
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), 
	
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), 
	
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), 
	
	NEVER(TransactionDefinition.PROPAGATION_NEVER), 
	
	NESTED(TransactionDefinition.PROPAGATION_NESTED); 
	
	private final int value; 
	
	Propagation(int value) { 
		this.value = value; 
	}
	
	public int value() { 
		return this.value; 
	} 
}

正确的事务传播行为可能的值如下 :

1_ TransactionDefinition.PROPAGATION_REQUIRED

使用的最多的一个事务传播行为,我们平时经常使用的 @Transactional 注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。也就是说:

  1. 如果外部方法没有开启事务的话, Propagation.REQUIRED 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
  2. 如果外部方法开启事务并且被 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。

举个例子:如果我们上面的 aMethod()bMethod() 使用的都是 PROPAGATION_REQUIRED 传播行为的话,两者使用的就是同一个事务,只要其中一个方法回滚,整个事务均回滚。

Class A { 
	@Transactional(propagation=propagation.PROPAGATION_REQUIRED) 
	public void aMethod { 
		//do something 
		B b = new B(); 
		b.bMethod(); 
	} 
}

Class B { 
	@Transactional(propagation=propagation.PROPAGATION_REQUIRED) 
	public void bMethod { 
		//do something 
	} 
}

2_TransactionDefinition.PROPAGATION_REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

举个例子:如果我们上面的 bMethod() 使用 PROPAGATION_REQUIRES_NEW 事务传播行为修饰,aMethod 还是用 PROPAGATION_REQUIRED 修饰的话。如果 aMethod() 发生异常回滚, bMethod() 不会跟着回滚,因为 bMethod() 开启了独立的事务。但是,如果 bMethod() 抛出了未被捕获的异常并且这个异常满足事务回滚规则的话, aMethod() 同样也会回滚,因为这个异常被 aMethod() 的事务管理机制检测到了。

Class A { 
	@Transactional(propagation=propagation.PROPAGATION_REQUIRED) 
	public void aMethod { 
		//do something 
		B b = new B(); 
		b.bMethod(); 
	} 
}

Class B { 
	@Transactional(propagation=propagation.REQUIRES_NEW) 
	public void bMethod { 
		//do something 
	} 
}

3_TransactionDefinition.PROPAGATION_NESTED:

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED。也就是说:

  1. 在外部方法未开启事务的情况下 Propagation.NESTEDPropagation.REQUIRED 作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
  2. 如果外部方法开启事务的话, Propagation.NESTED 修饰的内部方法属于外部事务的子事务,外部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事务。

这里还是简单举个例子:

如果 aMethod() 回滚的话, bMethod()bMethod2() 都要回滚,而 bMethod() 回滚的话,并不会造成 aMethod()bMethod() 回滚。

Class A { 
	@Transactional(propagation=propagation.PROPAGATION_REQUIRED) 
	public void aMethod { 
		//do something 
		B b = new B(); 
		b.bMethod(); 
		b.bMethod2(); 
	} 
}

Class B { 
	@Transactional(propagation=propagation.PROPAGATION_NESTED) 
	public void bMethod { 
		//do something 
	}
	@Transactional(propagation=propagation.PROPAGATION_NESTED) 
	public void bMethod2 { 
		//do something 
	} 
}

4_ TransactionDefinition.PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

这个使用的很少,就不举例子来说了。

若是错误的配置以下 3 种事务传播行为,事务将不会发生回滚,这里不对照案例讲解了,使用的很少。

  • TransactionDefinition.PROPAGATION_SUPPORTS : 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED : 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER : 以非事务方式运行,如果当前存在事务,则抛出异常。

2)事务隔离级别

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

public interface TransactionDefinition { 
	...... 
	int ISOLATION_DEFAULT = -1; 
	int ISOLATION_READ_UNCOMMITTED = 1; 
	int ISOLATION_READ_COMMITTED = 2; 
	int ISOLATION_REPEATABLE_READ = 4; 
	int ISOLATION_SERIALIZABLE = 8; 
	...... 
}

和事务传播行为这块一样,为了方便使用,Spring 也相应地定义了一个枚举类: Isolation

public enum Isolation { 
	DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), 
	
	READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), 
	
	READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), 
	
	REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), 
	
	SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); 
	
	private final int value; 
	
	Isolation(int value) { 
		this.value = value; 
	}
	public int value() { 
		return this.value; 
	} 
}

下面我依次对每一种事务隔离级别进行介绍:

  • TransactionDefinition.ISOLATION_DEFAULT :使用后端数据库默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

因为平时使用 MySQL 数据库比较多,这里再多提一嘴!

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ可重读)。我们可以通过SELECT @@tx_isolation; 命令来查看,如下:

这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 REPEATABLE-READ可重读)事务隔离级别下使用的是 Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如
SQL Server)是不同的。所以说 InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL 标准的 SERIALIZABLE (可串行化) 隔离级别。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED (读取提交内容) :,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ可重读) 并不会什么任何性能上的损失。

3)事务超时属性

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

4)事务只读属性

package org.springframework.transaction; 

import org.springframework.lang.Nullable; 

public interface TransactionDefinition { 
	...... 
	// 返回是否为只读事务,默认值为 false 
	boolean isReadOnly(); 
}

对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。

很多人就会疑问了,为什么我一个数据查询操作还要启用事务支持呢?

MySQL 默认对每一个新建立的连接都启用了 autocommit 模式。在该模式下,每一个发送到MySQL 服务器的 sql 语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务,并开启一个新的事务。

但是,如果你给方法加上了 Transactional 注解的话,这个方法执行的所有 sql 会被放在一个事务中。如果声明了只读事务的话,数据库就会去优化它的执行,并不会带来其他的什么收益。

最后

无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。

针对以上面试技术点,我在这里也做一些资料分享,希望能更好的帮助到大家。

戳这里免费领取以下资料

来其他的什么收益。

最后

无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。

针对以上面试技术点,我在这里也做一些资料分享,希望能更好的帮助到大家。

戳这里免费领取以下资料

[外链图片转存中…(img-Gaikvv7g-1628071671869)]

[外链图片转存中…(img-x5bS7854-1628071671871)]

[外链图片转存中…(img-fmvbxI1J-1628071671872)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值