Spring Data JPA

Hibernate => JPA(Java Persistence API) => Spring Data JPA,三者是层层封装的关系,Spring Data JPA基于JPA,而JPA又基于Hibernate

Hibernate本身为自动ORM框架,所以基于此框架再封装的框架也皆为自动ORM框架。

Persistence Context

一个临时的、和当前事务绑定的“工作内存”,所有在这个上下文里的实体,JPA 都会帮你跟踪变化、保证唯一性,并在事务提交时把变化同步到数据库

持久化上下文(Persistence Context)是JPA中一个核心概念,它本质上是一个由 EntityManager 管理的“实体缓存 + 变更追踪区”

Entity

在Spring Data JPA中,实体(Entity)具有三种状态,瞬态(Transient)、托管态(Managed)、游离态(Detached)。

后文讲解将围绕以下简化过的实体代码且默认这两种实体的Repository存在:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t_user")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;

}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t_order")
public class Order {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String orderNo;

private Integer status;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

}

Transient(瞬态)

瞬态实体一般为刚创建的实体,这个实体没有被载入到持久化上下文,同时这个实体在数据库中也并不存在。

存在依据为主键是否存在。

@Override
public void registerNewUser(String username) {
// 将要落库的用户没有id,也就是这个实体在数据库中并不存在
User user = new User(null, username, null); // 此时user为Transient
userRepository.save(user); // 使用save方法时为Managed,方法结束后变为Detached
}

Managed(托管态)

托管态实体是经过持久化操作后被载入到持久化上下文的实体,它的确在数据库中存在,从数据库查询得到的实体一般托管态。

托管态的实体在事务结束临界点会自动检查实体是否修改并落库。

@Transactional  // 为整个方法开启事务
@Override
public void changeUsername(Long id, String username) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
// 查询方法结束后user仍然为Managed,因为事务未关闭,数据库连接仍存在
user.setUsername(username);
// changeUsername退出时更改会自动commit
}

Detached(游离态)

游离态实体确实在数据库中存在,但是不在持久化上下文中,也就不被JPA管理,发生的更改也不会被追踪,这类实体一般出现在事务结束后。

游离态实体可由持久化操作再次被JPA管理,转化为托管态。

以下示例代码块与托管态示例代码块相似,值得注意的是,以下示例代码块方法并没有@Transactional注解。

@Override
public void changeUsername(Long id, String username) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
// findById方法结束后事务关闭,user转换为Detached
user.setUsername(username); // Detached状态下的更改不会被提交
userRepository.save(user); // 调用save方法时开启事务,user转换为Managed修改被提交
}

Removed(删除态)

删除态实体为被delete方法处理后的实体,若事务仍在,他们依然存在于持久化上下文中,他们会在事务结束后将删除落库。

删除态实体不要进行再次持久化进入托管态。你仍然可以使用实体中的一些属性,但不建议这么做,尤其是懒加载属性,会抛出LazyInitializationException

@Override
public void deleteUser(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
// findById方法结束后user变为Detached
userRepositoy.delete(user); // 调用delete方法时转换为Managed
// delete方法后user转换为Removed
}

Repository

Repository<T, ID>  ← 最顶层标记接口

CrudRepository<T, ID> ← 提供基本 CRUD

PagingAndSortingRepository<T, ID> ← 在 Crud 基础上增加分页、排序

JpaRepository<T, ID> ← JPA 专用扩展(Flush、批量操作等)

自定义 Repository 接口(继承 JpaRepository 或其他)

By default, methods inherited from CrudRepository inherit the transactional configuration from SimpleJpaRepository. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies. Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method.

默认情况下,从 CrudRepository继承的方法会从 SimpleJpaRepository继承其事务配置。对于读操作,事务配置的readOnly标志会被设为true;其他(写)操作则会使用普通的 @Transactional注解,以便应用默认的事务配置。那些由事务性仓库片段(transactional repository fragments)支持的方法,会继承该片段方法实际的事务属性。

—— Spring Data JPA Docs

在Spring Data JPA下SimpleJpaRepository实现了JpaRepositoryCrudRepositoryPagingAndSortingRepository中的通用CRUD方法,同时也为这些方法添加了@Transactional注解。

以下是SimpleJpaRepository常用且带有@Transactional注解的方法源码:

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
// existing code...
@Override
@Transactional
public void deleteById(ID id) { /* existing code... */ }

@Override
@Transactional
public void delete(T entity) { /* existing code... */ }

@Override
@Transactional
public void deleteAllById(Iterable<? extends ID> ids) { /* existing code... */ }

@Override
@Transactional
public void deleteAll(Iterable<? extends T> entities) { /* existing code... */ }

@Override
@Transactional
public <S extends T> S save(S entity) { /* existing code... */ }

@Override
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) { /* existing code... */ }
// existing code...
}

我们都知道delete相关方法负责删除实体,save方法负责插入、更新实体,不难发现带有@Transactional注解的方法都是处理写入操作的。而在SimpleJpaRepository类上有@Transactional(readOnly = true)注解,说明除去这几个涉及写入操作的方法,其余方法都具有只读事务。

值得注意的是,在我们使用自定义方法(根据命名规则自动生成的数据库操作方法)时是不带有事务的,这对读操作不会产生较大影响,除非你要求保证读一致性(在同一事务内多次读保持数据一致),但是写操作必须显式声明事务,这解释了为什么同样的操作,为什么自定义的写方法执行不成功,而默认实现方法却没有问题,因为默认实现方法自带事务。

// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {

void deleteUserById(Long id);

}
// UserService.java
@RequiredArgsConstructor
@Service
public class UserService {

private final UserRepository userRepository;

public void defaultDeleteMethod1(Long id) {
userRepository.deleteById(id); // ok
}

public void defaultDeleteMethod2(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
userRepository.delete(user); // ok
}

public void myDeleteMethod(Long id) {
userRepository.deleteUserById(id); // error
}

@Transactional
public void myTransactionalDeleteMethod(Long id) {
userRepository.deleteUserById(id); // ok
}

}

Spring Transaction

事务(Transaction)是一组操作的集合,这些操作要么全部成功,要么什么都不做(rollback)。

事务特性

特性 说明
原子性(Atomicity) 事务是不可分割的工作单位,要么一起成功,要么什么都不做,事务在执行过程中出现错误会执行回滚,回到事务执行前的状态。
一致性(Consistency) 一个事务执行后,数据库的所有数据必须符合所有要求,一致性是事务的最终目标,其余特性均为一致性的实现手段。
隔离性(Isolation) 数据库允许多个事务同时对数据进行读写,同时防止并发条件下出现数据不一致情况。
持久性(Durability) 事务结束后,对数据的修改是永久性的(只要不是删除或再次修改)。

其中,原子性隔离性较为重要。

事务隔离级别

在介绍事务隔离级别前,需要引入以下几个概念:

问题 说明
脏读 一个事务读到了另一个未提交事务修改过的数据。
不可重复读 在同一个事务中,两次读取同一行数据,结果却不一样,因为中间被别的事务修改并提交了。
幻读 在同一个事务中,两次执行相同条件的查询,返回的行数或内容集合不同,因为中间有事务插入或删除了符合该条件的新行

在了解由事务并发可能导致的问题后我们介绍事务隔离级别:

隔离级别 脏读 不可重复读 幻读
READ_UNCOMMITTE
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE

事务的隔离级别越高,异常发生概率越低,但性能也越低,所以要根据实际情况选择合适的隔离级别,不要总是选择隔离性最高,最安全的SERIALIZABLE

事务传播

类型 说明
REQUIRED (default) 如果当前存在事务,则加入;否则新建一个事务。
SUPPORTS 如果当前存在事务,则加入;否则以非事务方式执行。
MANDATORY 必须存在事务,否则抛出异常。
REQUIRES_NEW 新建事务,如果当前存在事务则挂起。
NOT_SUPPORTED 以非事务方式执行,如果当前存在事务则挂起。
NEVER 以非事务方式执行,如果当前存在事务则抛出异常。
NESTED 如果当前存在事务,则在嵌套事务中执行。

在讲解事务传播机制前我们需要补充代码背景,代码背景如下,所需依赖已注入,后续讲解将专注于方法代码块,而不是类与依赖。

我们假设用户一定存在,且订单也一定存在,异常情况不在我们考虑范围内。

// UserService.java
@RequiredArgsConstructor
@Service
public class UserService {

private final UserRepository userRepository;

private final OrderService orderService;

public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson");
userRepository.save(user);
try {
orderService.orderMethod(id);
} catch(Exception e) {}
user.setUsername("Michael");
userRepository.save(user);
throw new RuntimeException();
}

}

// OrderService.java
@RequiredArgsConstructor
@Service
public class OrderService {

private final OrderRepository orderRepository;

public void orderMethod(Long id) {
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1);
orderRepository.save(order);
}
throw new RuntimeException();
}

}

REQUIRED

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行且事务未提交
// save方法自带REQUIRED事务,加入userMethod事务中
userRepository.save(user); // [Managed] UPDATE已执行但事务未提交,仍可回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {} // 即使异常被捕获,但AOP代理的工作机制为,只要发生了异常传播,那么事务回滚
// 事务已回滚,后续代码无任何意义
user.setUsername("Michael");
userRepository.save(user);
throw new RuntimeException();
}

@Transactional
public void orderMethod(Long id) { // 本方法加入到userMethod的事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] 已修改事务未提交
// save方法自带REQUIRED事务,加入userMethod事务中
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,仍可回滚
}
throw new RuntimeException(); // 抛出异常,所有修改进行回滚
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] 修改不被管理
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行且事务已提交,不再接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {} // 即使异常被捕获,但AOP代理的工作机制为,只要发生了异常传播,那么事务回滚
// 事务已回滚,后续代码无任何意义
user.setUsername("Michael");
userRepository.save(user);
throw new RuntimeException();
}

@Transactional
public void orderMethod(Long id) { // 本方法新建事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] 已修改事务未提交
// save方法自带REQUIRED事务,加入userMethod事务中
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,仍可回滚
}
throw new RuntimeException(); // 抛出异常,所有修改进行回滚
}

SUPPORTS

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入userMethod方法事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {} // 即使异常被捕获,但AOP代理的工作机制为,只要发生了异常传播,那么事务回滚
// 事务已回滚,后续代码无任何意义
user.setUsername("Michael");
userRepository.save(user);
throw new RuntimeException();
}

@Transactional(propagation = Propagation.SUPPORTS)
public void orderMethod(Long id) { // 本方法加入到userMethod的事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] 已修改事务未提交
// save方法自带REQUIRED事务,加入userMethod事务中
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接收回滚
}
throw new RuntimeException(); // 抛出异常,所有修改进行回滚
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.SUPPORTS)
public void orderMethod(Long id) { // 当前没有事务,所以此方法也没有事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务已提交,不接收回滚
}
throw new RuntimeException(); // 抛出异常,但是没有事务,不回滚
}

MANDATORY

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常,事务回滚
} catch(Exception e) {}
// 事务已回滚,后续代码无任何意义
user.setUsername("Michael");
userRepository.save(user);
throw new RuntimeException();
}

@Transactional(propagation = Propagation.MANDATORY)
public void orderMethod(Long id) { // 加入到userMethod的事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接收回滚
}
throw new RuntimeException(); // 抛出异常,事务回滚
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.MANDATORY)
public void orderMethod(Long id) { // 当前没有事务,此方法并未执行,直接抛出异常
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1);
orderRepository.save(order);
}
throw new RuntimeException();
}

REQUIRES_NEW

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
// userMethod事务挂起
orderService.orderMethod(id); // orderMethod事务回滚,userMethod事务不受影响继续执行
} catch(Exception e) {}
// userMethod事务继续执行
user.setUsername("Michael"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
throw new RuntimeException(); // 抛出异常,userMethod事务回滚
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void orderMethod(Long id) { // 当前存在事务,新建事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到orderMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接收回滚
}
throw new RuntimeException(); // 抛出异常,orderMethod事务回滚
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void orderMethod(Long id) { // 当前没有事务,新建事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到orderMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接收回滚
}
throw new RuntimeException(); // // 抛出异常,本事务回滚
}

NOT_SUPPORTED

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
// userMethod事务挂起
orderService.orderMethod(id);
} catch(Exception e) {}
// userMethod事务继续执行
user.setUsername("Michael"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到本事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
throw new RuntimeException(); // 抛出异常,本事务回滚
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void orderMethod(Long id) { // 当前存在事务,将事务挂起,以非事务运行
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务已提交,不接收回滚
}
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void orderMethod(Long id) { // 当前没有事务,以非事务运行
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,新建事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务已提交,不接收回滚
}
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

NEVER

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
orderService.orderMethod(id);
} catch(Exception e) {}
// 本事务继续执行
user.setUsername("Michael"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
throw new RuntimeException(); // 抛出异常,本事务回滚
}

@Transactional(propagation = Propagation.NEVER)
public void orderMethod(Long id) { // 当前存在事务,此方法并未执行,直接抛出异常
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1);
orderRepository.save(order);
}
throw new RuntimeException();
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.NEVER)
public void orderMethod(Long id) {
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务已提交,不接受回滚
}
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

NESTED

⚠️注意与REQUIRES_NEWS进行区分。

REQUIRES_NEWS会开启平级事务,而NESTED会开启子事务。

REQUIRES_NEWS开启的平级事务不会受调用方所处事务控制,调用方事务回滚不会引起新开启事务的回滚,因为他们是两个事务,平级的。NESTED开启的子事务受调用方所处事务控制,调用方事务(父事务)的回滚会引起新开启事务的回滚。

简而言之,REQUIRES_NEWS事务、NESTED事务都不会影响调用方事务,但REQUIRES_NEWS事务不受调用方事务影响,而NESTED事务会受调用方事务影响。

@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
orderService.orderMethod(id);
} catch(Exception e) {}
user.setUsername("Michael"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
throw new RuntimeException(); // 抛出异常,userMethod、orderMethod事务均回滚
}

@Transactional(propagation = Propagation.NESTED)
public void orderMethod(Long id) { // 当前存在事务,新建嵌套事务,位于userMethod事务内
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到orderMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接受回滚
}
} // 嵌套事务结束,已提交,但仍受父事务userMethod控制
@Transactional
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
try {
orderService.orderMethod(id);
} catch(Exception e) {}
// 异常已被捕获,本事务继续执行
user.setUsername("Michael"); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到userMethod的事务
userRepository.save(user); // [Managed] UPDATE已执行,事务未提交,接受回滚
} // 事务结束,userMethod事务已提交,orderMethod事务已回滚

@Transactional(propagation = Propagation.NESTED)
public void orderMethod(Long id) { // 当前存在事务,新建嵌套事务,位于userMethod事务内
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到orderMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接受回滚
}
throw new RuntimeException(); // 抛出异常,本事务回滚
}
public void userMethod(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new EntityNotFoundException("User not found.")
);
user.setUsername("Jackson"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
try {
orderService.orderMethod(id); // 此方法抛出异常
} catch(Exception e) {}
// 没有事务,所以没有回滚一说,继续执行
user.setUsername("Michael"); // [Detached] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,开启新事务
userRepository.save(user); // [Managed] UPDATE已执行,事务已提交,不接受回滚
throw new RuntimeException(); // 上抛异常,但是没有事务,不回滚,所有修改均生效
}

@Transactional(propagation = Propagation.NESTED)
public void orderMethod(Long id) { // 当前没有事务,新建事务
List<Order> orders = orderRepository.findOrdersByUser_Id(id);
for (Order order : orders) {
order.setStatus(1); // [Managed] UPDATE未执行,事务未提交
// save方法自带REQUIRED事务,加入到orderMethod的事务
orderRepository.save(order); // [Managed] UPDATE已执行,事务未提交,接收回滚
}
throw new RuntimeException(); // // 抛出异常,本事务回滚
}

控制方式

声明式事务 编程式事务
定义方式 通过注解/XML配置 通过代码手动控制
侵入性 低(非侵入) 高(侵入业务代码)
灵活性 一般
维护成本
适用场景 大多数标准业务 复杂事务逻辑
事务边界 方法级 任意代码块
异常处理 自动(根据配置) 手动控制
动态控制 困难 灵活
性能 略好(AOP优化) 略差(对象创建开销)

感谢腾讯元宝生成的以下示例代码。

声明式事务

@Transaction Annotation

@Service
@RequiredArgsConstructor
public class OrderService {

private final OrderRepository orderRepository;
private final UserRepository userRepository;

/**
* 主业务:创建订单
* 使用 NESTED 事务处理积分扣减,即使积分扣减失败也不影响订单创建
*/
@Transactional
public Order createOrderWithNestedPointDeduction(Long userId, String orderNo) {
// 创建订单
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));

Order order = new Order();
order.setOrderNo(orderNo);
order.setStatus(0);
order.setUser(user);
order = orderRepository.save(order);

// 扣减积分(NESTED事务,失败只回滚这部分)
try {
deductPointsNested(user.getId(), 100);
} catch (Exception e) {
log.warn("扣减积分失败,但订单已创建: {}", e.getMessage());
}

return order;
}

/**
* NESTED 事务:扣减用户积分
*/
@Transactional(propagation = Propagation.NESTED)
public void deductPointsNested(Long userId, Integer points) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));

// 模拟积分检查
if (user.getPoints() < points) {
throw new RuntimeException("积分不足");
}

// 扣减积分(实际项目中需要User有points字段)
// user.setPoints(user.getPoints() - points);
// userRepository.save(user);
}
}

编程式事务

TransactionTemplate

@Service
@RequiredArgsConstructor
public class OrderService {

private final OrderRepository orderRepository;
private final UserRepository userRepository;
private final TransactionTemplate transactionTemplate;

/**
* 使用编程式事务创建订单
* 可以在代码中灵活控制事务行为
*/
public Order createOrderProgrammatic(Long userId, String orderNo) {
return transactionTemplate.execute(status -> {
try {
// 检查用户是否存在
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));

// 创建订单
Order order = new Order();
order.setOrderNo(orderNo);
order.setStatus(0);
order.setUser(user);

return orderRepository.save(order);

} catch (Exception e) {
// 手动标记回滚
status.setRollbackOnly();
log.error("创建订单失败", e);
throw e;
}
});
}

/**
* 复杂业务:需要多个独立事务
* 订单创建和库存扣减分开处理
*/
public void createOrderWithStockCheck(Long userId, String orderNo) {
// 第一个事务:验证并锁定用户
User user = transactionTemplate.execute(status -> {
return userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
});

// 第二个事务:创建订单
Order order = transactionTemplate.execute(status -> {
Order o = new Order();
o.setOrderNo(orderNo);
o.setStatus(0);
o.setUser(user);
return orderRepository.save(o);
});

// 第三个事务:扣减库存(可能失败,但不影响订单)
try {
transactionTemplate.execute(status -> {
// 扣减库存逻辑
// stockService.deduct(order.getProductId(), 1);
return true;
});
} catch (Exception e) {
log.warn("扣减库存失败: {}", e.getMessage());
// 更新订单状态为"待补货"
order.setStatus(2);
orderRepository.save(order);
}
}
}

PlatformTransactionManager

@Service
@RequiredArgsConstructor
public class ComplexOrderService {

private final OrderRepository orderRepository;
private final UserRepository userRepository;
private final PlatformTransactionManager transactionManager;

/**
* 使用PlatformTransactionManager进行更精细的控制
*/
public void processComplexOrder(OrderRequest request) {
// 定义事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setTimeout(60);

// 开启事务
TransactionStatus status = transactionManager.getTransaction(def);

try {
// 1. 创建用户(如果不存在)
User user = userRepository.findByUsername(request.getUsername())
.orElseGet(() -> {
User newUser = new User();
newUser.setUsername(request.getUsername());
return userRepository.save(newUser);
});

// 2. 创建主订单
Order mainOrder = new Order();
mainOrder.setOrderNo(generateOrderNo());
mainOrder.setStatus(0);
mainOrder.setUser(user);
mainOrder = orderRepository.save(mainOrder);

// 3. 创建子订单
for (OrderItem item : request.getItems()) {
Order subOrder = new Order();
subOrder.setOrderNo(generateSubOrderNo());
subOrder.setStatus(0);
subOrder.setUser(user);
orderRepository.save(subOrder);
}

// 提交事务
transactionManager.commit(status);
log.info("订单处理完成,主订单号: {}", mainOrder.getOrderNo());

} catch (DataAccessException e) {
// 数据访问异常,回滚事务
log.error("数据操作失败,回滚事务", e);
transactionManager.rollback(status);
throw e;
} catch (Exception e) {
// 其他业务异常,根据业务规则决定是否回滚
if (isCriticalError(e)) {
log.error("严重错误,回滚事务", e);
transactionManager.rollback(status);
} else {
log.warn("非严重错误,提交事务", e);
transactionManager.commit(status);
}
throw e;
}
}

private boolean isCriticalError(Exception e) {
// 判断是否为需要回滚的关键错误
return e instanceof DataIntegrityViolationException;
}

private String generateOrderNo() {
return "ORD" + System.currentTimeMillis();
}

private String generateSubOrderNo() {
return "SUB" + System.currentTimeMillis() + ThreadLocalRandom.current().nextInt(1000);
}
}