Spring Data JPA 注解详解

📦 完整代码示例https://github.com/penghs520/springboot-transaction

目录

  1. @Modifying 注解
  2. @Lock 注解
  3. @Query 注解
  4. @Transactional 注解
  5. @Entity 相关注解
  6. @Repository 注解
  7. 实际应用示例
  8. 最佳实践
  9. 总结

@Modifying 注解

作用

@Modifying 注解用于标识执行修改操作(INSERT、UPDATE、DELETE)的查询方法,告诉Spring Data JPA这是一个修改操作而不是查询操作。

语法

1
2
3
4
5
6
@Modifying(
clearAutomatically = false, // 是否自动清除一级缓存
flushAutomatically = false // 是否自动刷新到数据库
)
@Query("UPDATE Entity e SET e.field = :value WHERE e.id = :id")
int updateMethod(@Param("id") Long id, @Param("value") String value);

关键特性

1. 返回值类型

  • int: 返回受影响的行数
  • void: 不返回任何值
  • boolean: 返回操作是否成功

2. 事务要求

  • 必须在事务内执行,否则抛出 TransactionRequiredException
  • 通常与 @Transactional 注解配合使用

3. 缓存处理

1
2
3
@Modifying(clearAutomatically = true)  // 自动清除一级缓存
@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
int updateUserName(@Param("id") Long id, @Param("name") String name);

项目中的实际应用

1
2
3
4
5
6
7
8
9
// 扣减用户余额
@Modifying
@Query("UPDATE UserAccount ua SET ua.balance = ua.balance - :amount WHERE ua.userId = :userId AND ua.balance >= :amount")
int deductBalance(@Param("userId") Long userId, @Param("amount") BigDecimal amount);

// 扣减库存
@Modifying
@Query("UPDATE Inventory i SET i.stockQuantity = i.stockQuantity - :quantity WHERE i.productId = :productId AND i.stockQuantity >= :quantity")
int deductStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);

实现机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Spring Data JPA 内部实现(简化版)
public class ModifyingQueryExecutor {
public Object execute(Query query, Object[] parameters) {
if (method.isAnnotationPresent(Modifying.class)) {
// 1. 执行修改操作
int affectedRows = entityManager.createQuery(jpqlQuery)
.setParameters(parameters)
.executeUpdate(); // 关键:使用executeUpdate()

// 2. 处理缓存
if (modifying.clearAutomatically()) {
entityManager.clear();
}
if (modifying.flushAutomatically()) {
entityManager.flush();
}

return affectedRows;
}
}
}

@Lock 注解

作用

@Lock 注解用于在查询时对数据库记录加锁,防止并发访问时的数据不一致问题。

语法

1
2
3
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM Entity e WHERE e.id = :id")
Optional<Entity> findByIdWithLock(@Param("id") Long id);

锁模式类型

1. 悲观锁(Pessimistic Locking)

1
2
3
4
5
6
7
8
9
// 悲观读锁(共享锁)
@Lock(LockModeType.PESSIMISTIC_READ)
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdForRead(@Param("id") Long id);

// 悲观写锁(排他锁)- 最常用
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdForUpdate(@Param("id") Long id);

2. 乐观锁(Optimistic Locking)

1
2
3
4
5
6
7
8
9
// 乐观读锁
@Lock(LockModeType.OPTIMISTIC)
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdOptimistic(@Param("id") Long id);

// 乐观写锁
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdOptimisticWrite(@Param("id") Long id);

数据库层面的SQL实现

1
2
3
4
5
6
7
8
9
10
-- PESSIMISTIC_WRITE 在不同数据库中的实现

-- MySQL/PostgreSQL/H2
SELECT * FROM user_account WHERE user_id = ? FOR UPDATE;

-- Oracle
SELECT * FROM user_account WHERE user_id = ? FOR UPDATE;

-- SQL Server
SELECT * FROM user_account WITH (UPDLOCK) WHERE user_id = ?;

项目中的实际应用

1
2
3
4
5
6
7
8
9
// 悲观锁查询用户账户(防止余额被其他事务修改)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT ua FROM UserAccount ua WHERE ua.userId = :userId")
Optional<UserAccount> findByUserIdWithLock(@Param("userId") Long userId);

// 悲观锁查询库存(防止库存被其他事务修改)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT i FROM Inventory i WHERE i.productId = :productId")
Optional<Inventory> findByProductIdWithLock(@Param("productId") Long productId);

并发控制示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 1. 使用悲观锁查询,防止并发修改
UserAccount userAccount = userAccountRepository.findByUserIdWithLock(orderRequest.getUserId())
.orElseThrow(() -> new BusinessException("USER_NOT_FOUND", "用户账户不存在"));

Inventory inventory = inventoryRepository.findByProductIdWithLock(orderRequest.getProductId())
.orElseThrow(() -> new BusinessException("PRODUCT_NOT_FOUND", "产品不存在"));

// 2. 执行原子性操作
int stockUpdated = inventoryRepository.deductStock(orderRequest.getProductId(), orderRequest.getQuantity());
int balanceUpdated = userAccountRepository.deductBalance(orderRequest.getUserId(), totalAmount);

// 3. 检查操作结果
if (stockUpdated == 0 || balanceUpdated == 0) {
throw new BusinessException("OPERATION_FAILED", "扣减失败");
}
}

@Query 注解

作用

@Query 注解用于自定义查询语句,支持JPQL和原生SQL。

语法

1
2
3
4
5
6
7
8
9
10
11
// JPQL查询
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findByName(@Param("name") String name);

// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE name = ?1", nativeQuery = true)
List<User> findByNameNative(String name);

// 分页查询
@Query("SELECT u FROM User u WHERE u.age > :age")
Page<User> findByAgeGreaterThan(@Param("age") Integer age, Pageable pageable);

项目中的实际应用

1
2
3
4
5
6
7
// 统计成功订单数量
@Query("SELECT COUNT(o) FROM Order o WHERE o.userId = :userId AND o.status = 'SUCCESS'")
long countSuccessfulOrdersByUserId(@Param("userId") Long userId);

// 检查库存是否充足
@Query("SELECT CASE WHEN i.stockQuantity >= :quantity THEN true ELSE false END FROM Inventory i WHERE i.productId = :productId")
boolean hasEnoughStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);

@Transactional 注解

作用

@Transactional 注解用于声明事务边界,确保数据一致性。

语法

1
2
3
4
5
6
7
8
9
10
@Transactional(
propagation = Propagation.REQUIRED, // 事务传播行为
isolation = Isolation.READ_COMMITTED, // 事务隔离级别
timeout = 30, // 事务超时时间(秒)
rollbackFor = Exception.class, // 回滚异常类型
noRollbackFor = RuntimeException.class // 不回滚异常类型
)
public void businessMethod() {
// 业务逻辑
}

事务传播行为详解

事务传播行为定义了当一个事务方法被另一个事务方法调用时,应该如何处理事务。

1
2
3
4
5
6
7
8
9
public enum Propagation {
REQUIRED, // 默认:如果存在事务则加入,否则创建新事务
SUPPORTS, // 如果存在事务则加入,否则以非事务方式执行
MANDATORY, // 必须在事务中执行,否则抛出异常
REQUIRES_NEW, // 总是创建新事务,挂起当前事务
NOT_SUPPORTED, // 以非事务方式执行,挂起当前事务
NEVER, // 以非事务方式执行,如果存在事务则抛出异常
NESTED // 嵌套事务
}

1. REQUIRED(默认行为)- 最常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Service
public class OrderService {

@Autowired
private PaymentService paymentService;

@Autowired
private InventoryService inventoryService;

// 主事务方法
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest request) {
// 1. 创建订单记录
Order order = new Order();
orderRepository.save(order);

// 2. 调用支付服务(加入当前事务)
paymentService.processPayment(request.getPaymentInfo());

// 3. 调用库存服务(加入当前事务)
inventoryService.deductStock(request.getProductId(), request.getQuantity());

// 如果任何一步失败,整个事务回滚
return OrderResponse.success(order);
}
}

@Service
public class PaymentService {

// REQUIRED:加入外部事务,如果没有外部事务则创建新事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void processPayment(PaymentInfo paymentInfo) {
// 处理支付逻辑
// 如果这里抛出异常,整个订单创建事务都会回滚
paymentRepository.save(new Payment(paymentInfo));

if (paymentInfo.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("INVALID_AMOUNT", "支付金额无效");
}
}
}

2. REQUIRES_NEW - 独立事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Service
public class OrderService {

@Autowired
private AuditService auditService;

@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest request) {
try {
// 主业务逻辑
Order order = new Order();
orderRepository.save(order);

// 记录审计日志(独立事务)
auditService.logOrderCreation(order.getId(), request.getUserId());

// 模拟业务异常
if (request.getAmount().compareTo(new BigDecimal("10000")) > 0) {
throw new BusinessException("AMOUNT_TOO_LARGE", "订单金额过大");
}

return OrderResponse.success(order);

} catch (Exception e) {
// 即使主事务回滚,审计日志也会保存(因为是独立事务)
throw e;
}
}
}

@Service
public class AuditService {

// REQUIRES_NEW:总是创建新事务,与外部事务独立
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void logOrderCreation(String orderId, Long userId) {
// 创建独立事务,即使外部事务回滚,这里的数据也会提交
AuditLog auditLog = new AuditLog();
auditLog.setOrderId(orderId);
auditLog.setUserId(userId);
auditLog.setAction("ORDER_CREATED");
auditLog.setTimestamp(LocalDateTime.now());

auditLogRepository.save(auditLog);

// 这个事务独立提交,不受外部事务影响
}
}

3. NESTED - 嵌套事务(保存点机制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Service
public class OrderService {

@Autowired
private PromotionService promotionService;

@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest request) {
// 主事务
Order order = new Order();
orderRepository.save(order);

try {
// 尝试应用优惠券(嵌套事务)
promotionService.applyCoupon(order.getId(), request.getCouponCode());

} catch (CouponException e) {
// 优惠券应用失败,但不影响主订单创建
log.warn("优惠券应用失败: {}", e.getMessage());
// 嵌套事务回滚到保存点,主事务继续
}

return OrderResponse.success(order);
}
}

@Service
public class PromotionService {

// NESTED:创建嵌套事务(保存点)
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void applyCoupon(String orderId, String couponCode) {
// 嵌套事务开始,创建保存点

Coupon coupon = couponRepository.findByCode(couponCode)
.orElseThrow(() -> new CouponException("优惠券不存在"));

if (coupon.isExpired()) {
// 这个异常只会回滚到保存点,不影响外部事务
throw new CouponException("优惠券已过期");
}

if (coupon.isUsed()) {
// 这个异常只会回滚到保存点,不影响外部事务
throw new CouponException("优惠券已使用");
}

// 应用优惠券
coupon.setUsed(true);
coupon.setUsedOrderId(orderId);
couponRepository.save(coupon);
}
}

4. SUPPORTS - 支持事务但不强制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Service
public class ReportService {

// SUPPORTS:如果有事务就加入,没有就以非事务方式执行
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public OrderStatistics generateOrderStatistics(Long userId) {
// 这个方法可能在事务中被调用,也可能独立调用
// 如果在事务中调用,会加入外部事务
// 如果独立调用,以非事务方式执行

List<Order> orders = orderRepository.findByUserId(userId);

return OrderStatistics.builder()
.totalOrders(orders.size())
.totalAmount(orders.stream()
.map(Order::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add))
.build();
}
}

@Service
public class OrderService {

@Autowired
private ReportService reportService;

@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrderWithStats(OrderRequest request) {
// 创建订单
Order order = createOrder(request);

// 生成统计信息(加入当前事务)
OrderStatistics stats = reportService.generateOrderStatistics(request.getUserId());

return OrderResponse.success(order, stats);
}

// 独立调用统计服务
public OrderStatistics getOrderStats(Long userId) {
// 这里调用统计服务时,以非事务方式执行
return reportService.generateOrderStatistics(userId);
}
}

5. MANDATORY - 强制要求事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Service
public class PaymentService {

// MANDATORY:必须在事务中执行,否则抛出异常
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void processRefund(String orderId, BigDecimal amount) {
// 这个方法必须在事务中被调用
// 如果没有外部事务,会抛出 IllegalTransactionStateException

Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException("ORDER_NOT_FOUND", "订单不存在"));

if (!order.canRefund()) {
throw new BusinessException("CANNOT_REFUND", "订单不能退款");
}

// 处理退款逻辑
Refund refund = new Refund();
refund.setOrderId(orderId);
refund.setAmount(amount);
refundRepository.save(refund);

// 更新订单状态
order.setStatus(OrderStatus.REFUNDED);
orderRepository.save(order);
}
}

@Service
public class OrderService {

@Autowired
private PaymentService paymentService;

@Transactional(rollbackFor = Exception.class)
public void refundOrder(String orderId) {
// 正确调用:在事务中调用 MANDATORY 方法
Order order = orderRepository.findById(orderId).orElseThrow();
paymentService.processRefund(orderId, order.getAmount());
}

public void incorrectRefundCall(String orderId) {
// 错误调用:没有事务,会抛出异常
Order order = orderRepository.findById(orderId).orElseThrow();
// 这里会抛出 IllegalTransactionStateException
paymentService.processRefund(orderId, order.getAmount());
}
}

6. NOT_SUPPORTED - 非事务执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Service
public class NotificationService {

// NOT_SUPPORTED:以非事务方式执行,挂起外部事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendOrderNotification(String orderId, String userEmail) {
// 这个方法总是以非事务方式执行
// 如果有外部事务,会被挂起

try {
// 发送邮件通知(可能耗时较长)
emailService.sendEmail(userEmail, "订单创建成功", "您的订单 " + orderId + " 已创建成功");

// 发送短信通知
smsService.sendSms(getUserPhone(userEmail), "订单创建成功");

// 记录通知日志(非事务)
notificationLogRepository.save(new NotificationLog(orderId, "EMAIL_SMS_SENT"));

} catch (Exception e) {
// 通知失败不影响主业务事务
log.error("发送通知失败: orderId={}, error={}", orderId, e.getMessage());
}
}
}

@Service
public class OrderService {

@Autowired
private NotificationService notificationService;

@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest request) {
// 主事务
Order order = new Order();
orderRepository.save(order);

// 发送通知(非事务执行,不影响主事务)
notificationService.sendOrderNotification(order.getId(), request.getUserEmail());

// 即使通知发送失败,订单创建事务也会正常提交
return OrderResponse.success(order);
}
}

7. NEVER - 禁止事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Service
public class CacheService {

// NEVER:禁止在事务中执行
@Transactional(propagation = Propagation.NEVER)
public void updateCache(String key, Object value) {
// 这个方法不能在事务中被调用
// 如果在事务中调用,会抛出 IllegalTransactionStateException

// 更新缓存操作
redisTemplate.opsForValue().set(key, value, Duration.ofMinutes(30));

log.info("缓存更新成功: key={}", key);
}
}

@Service
public class OrderService {

@Autowired
private CacheService cacheService;

@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest request) {
Order order = new Order();
orderRepository.save(order);

// 错误调用:在事务中调用 NEVER 方法会抛出异常
// cacheService.updateCache("order:" + order.getId(), order);

return OrderResponse.success(order);
}

// 正确的调用方式
public void updateOrderCache(Order order) {
// 非事务方法中调用 NEVER 方法
cacheService.updateCache("order:" + order.getId(), order);
}
}

8. 实际业务场景综合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Service
public class ComplexOrderService {

@Autowired
private PaymentService paymentService;

@Autowired
private InventoryService inventoryService;

@Autowired
private AuditService auditService;

@Autowired
private NotificationService notificationService;

@Autowired
private PromotionService promotionService;

/**
* 复杂订单创建流程,演示多种传播行为
*/
@Transactional(rollbackFor = Exception.class)
public OrderResponse createComplexOrder(OrderRequest request) {

// 1. 记录审计日志(独立事务,REQUIRES_NEW)
auditService.logOrderCreation("ORDER_START", request.getUserId());

try {
// 2. 创建订单(当前事务)
Order order = new Order();
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
orderRepository.save(order);

// 3. 处理支付(加入当前事务,REQUIRED)
paymentService.processPayment(request.getPaymentInfo());

// 4. 扣减库存(加入当前事务,REQUIRED)
inventoryService.deductStock(request.getProductId(), request.getQuantity());

// 5. 尝试应用优惠券(嵌套事务,NESTED)
try {
promotionService.applyCoupon(order.getId(), request.getCouponCode());
} catch (CouponException e) {
// 优惠券失败不影响主流程
log.warn("优惠券应用失败: {}", e.getMessage());
}

// 6. 发送通知(非事务,NOT_SUPPORTED)
notificationService.sendOrderNotification(order.getId(), request.getUserEmail());

// 7. 记录成功审计日志(独立事务,REQUIRES_NEW)
auditService.logOrderCreation("ORDER_SUCCESS", request.getUserId());

return OrderResponse.success(order);

} catch (Exception e) {
// 8. 记录失败审计日志(独立事务,REQUIRES_NEW)
auditService.logOrderCreation("ORDER_FAILED", request.getUserId());
throw e;
}
}
}

9. 传播行为选择指南

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 传播行为选择指南:
*
* REQUIRED (默认):
* - 适用场景:大部分业务方法
* - 特点:简单可靠,保证数据一致性
*
* REQUIRES_NEW:
* - 适用场景:审计日志、独立的业务操作
* - 特点:独立事务,不受外部事务影响
*
* NESTED:
* - 适用场景:可选的业务操作,失败不影响主流程
* - 特点:部分回滚,灵活性高
*
* SUPPORTS:
* - 适用场景:查询方法,可能在事务中也可能独立调用
* - 特点:灵活适应调用环境
*
* MANDATORY:
* - 适用场景:必须在事务中执行的关键操作
* - 特点:强制事务约束,防止误用
*
* NOT_SUPPORTED:
* - 适用场景:耗时操作、外部系统调用
* - 特点:避免长时间占用事务资源
*
* NEVER:
* - 适用场景:纯缓存操作、工具方法
* - 特点:明确禁止事务,避免不必要的事务开销
*/

@Transactional 在接口和实现类上的区别

1. 在接口上使用 @Transactional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 接口定义
public interface OrderService {

@Transactional(rollbackFor = Exception.class)
OrderResponse createOrder(OrderRequest orderRequest);

@Transactional(rollbackFor = Exception.class)
OrderResponse cancelOrder(String orderId);

// 只读事务
@Transactional(readOnly = true)
OrderResponse getOrderById(String orderId);
}

// 实现类
@Service
public class OrderServiceImpl implements OrderService {

@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
// 继承接口的事务配置:rollbackFor = Exception.class
// 整个订单创建过程在一个事务中
}

@Override
public OrderResponse cancelOrder(String orderId) {
// 继承接口的事务配置:rollbackFor = Exception.class
// 整个订单取消过程在一个事务中
}

@Override
public OrderResponse getOrderById(String orderId) {
// 继承接口的事务配置:readOnly = true
// 只读事务,性能更好
}
}

2. 在实现类上使用 @Transactional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 接口定义(无事务注解)
public interface OrderService {
OrderResponse createOrder(OrderRequest orderRequest);
OrderResponse cancelOrder(String orderId);
OrderResponse getOrderById(String orderId);
}

// 实现类
@Service
public class OrderServiceImpl implements OrderService {

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 在实现类方法上定义事务配置
// 整个订单创建过程在一个事务中
}

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse cancelOrder(String orderId) {
// 在实现类方法上定义事务配置
// 整个订单取消过程在一个事务中
}

@Override
@Transactional(readOnly = true)
public OrderResponse getOrderById(String orderId) {
// 在实现类方法上定义事务配置
// 只读事务
}
}

3. 混合使用(实现类覆盖接口配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 接口定义(默认事务配置)
public interface OrderService {

@Transactional // 默认配置
OrderResponse createOrder(OrderRequest orderRequest);

@Transactional(readOnly = true) // 只读事务
OrderResponse getOrderById(String orderId);
}

// 实现类
@Service
public class OrderServiceImpl implements OrderService {

@Override
@Transactional(rollbackFor = Exception.class, timeout = 30) // 覆盖接口配置
public OrderResponse createOrder(OrderRequest orderRequest) {
// 使用实现类的事务配置,而不是接口的默认配置
// rollbackFor = Exception.class, timeout = 30秒
}

@Override
public OrderResponse getOrderById(String orderId) {
// 继承接口的事务配置:readOnly = true
}
}

4. 类级别的事务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 接口级别配置
@Transactional(readOnly = true) // 默认所有方法都是只读事务
public interface OrderService {

OrderResponse getOrderById(String orderId); // 继承只读事务

@Transactional(rollbackFor = Exception.class) // 覆盖为读写事务
OrderResponse createOrder(OrderRequest orderRequest);
}

// 实现类级别配置
@Service
@Transactional(rollbackFor = Exception.class) // 默认所有方法都回滚所有异常
public class OrderServiceImpl implements OrderService {

@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
// 使用类级别配置:rollbackFor = Exception.class
}

@Override
@Transactional(readOnly = true) // 覆盖类级别配置
public OrderResponse getOrderById(String orderId) {
// 使用方法级别配置:readOnly = true
}
}

5. 优先级和最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 事务配置优先级(从高到低):
* 1. 实现类方法级别的 @Transactional
* 2. 实现类类级别的 @Transactional
* 3. 接口方法级别的 @Transactional
* 4. 接口类级别的 @Transactional
*/

// ✅ 推荐做法1:在接口上定义事务语义
public interface OrderService {

/**
* 创建订单 - 需要事务支持
* 任何异常都应该回滚
*/
@Transactional(rollbackFor = Exception.class)
OrderResponse createOrder(OrderRequest orderRequest);

/**
* 查询订单 - 只读操作
* 使用只读事务提高性能
*/
@Transactional(readOnly = true)
OrderResponse getOrderById(String orderId);
}

// ✅ 推荐做法2:在实现类上定义具体的事务配置
@Service
public class OrderServiceImpl implements OrderService {

@Override
@Transactional(
rollbackFor = Exception.class,
timeout = 30, // 30秒超时
isolation = Isolation.READ_COMMITTED
)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 具体的事务配置
}

@Override
public OrderResponse getOrderById(String orderId) {
// 继承接口的只读事务配置
}
}

6. 注意事项和陷阱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// ❌ 错误:私有方法上的事务注解无效
@Service
public class OrderServiceImpl implements OrderService {

@Transactional // 无效!Spring AOP无法代理私有方法
private void privateMethod() {
// 这个方法不会有事务支持
}

@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
privateMethod(); // 调用私有方法,没有事务
}
}

// ❌ 错误:同类内部调用,事务注解无效
@Service
public class OrderServiceImpl implements OrderService {

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 有事务
return this.internalCreateOrder(orderRequest); // 内部调用,事务失效!
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public OrderResponse internalCreateOrder(OrderRequest orderRequest) {
// 这个事务配置不会生效,因为是内部调用
return null;
}
}

// ✅ 正确:通过注入自己来解决内部调用问题
@Service
public class OrderServiceImpl implements OrderService {

@Autowired
private OrderService self; // 注入自己

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 有事务
return self.internalCreateOrder(orderRequest); // 通过代理调用,事务生效
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public OrderResponse internalCreateOrder(OrderRequest orderRequest) {
// 这个事务配置会生效
return null;
}
}

项目中的实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
// 整个订单创建过程在一个事务中
// 如果任何步骤失败,整个事务回滚
}

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse cancelOrder(String orderId) {
// 整个订单取消过程在一个事务中
// 确保数据一致性
}

@Entity 相关注解

1. @Entity

标识一个类为JPA实体。

1
2
3
4
5
@Entity
@Table(name = "orders")
public class Order {
// 实体类内容
}

2. @Table

指定数据库表名和约束。

1
2
3
4
5
6
7
8
9
@Table(
name = "orders",
schema = "public",
uniqueConstraints = @UniqueConstraint(columnNames = {"order_id"}),
indexes = @Index(name = "idx_user_id", columnList = "user_id")
)
public class Order {
// 实体类内容
}

3. @Id 和 @GeneratedValue

标识主键和生成策略。

1
2
3
4
5
6
7
8
9
10
11
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// 生成策略类型
public enum GenerationType {
AUTO, // 自动选择
IDENTITY, // 数据库自增
SEQUENCE, // 序列
TABLE // 表生成器
}

4. @Column

指定列属性。

1
2
3
4
5
6
7
8
9
@Column(
name = "order_id",
nullable = false,
unique = true,
length = 50,
precision = 10,
scale = 2
)
private String orderId;

5. @Enumerated

枚举类型映射。

1
2
3
4
5
6
7
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 20)
private OrderStatus status;

public enum OrderStatus {
PENDING, SUCCESS, FAILED, CANCELLED
}

6. @Version

乐观锁版本控制。

1
2
@Version
private Long version;

7. 生命周期回调注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
public class Order {

@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}

@PreUpdate
public void preUpdate() {
this.updatedAt = LocalDateTime.now();
}

@PostLoad
public void postLoad() {
// 加载后的处理逻辑
}
}

@Repository 注解

作用

标识数据访问层组件,提供异常转换功能。

语法

1
2
3
4
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Repository方法
}

异常转换

1
2
3
4
5
6
7
// Spring会自动将JPA异常转换为Spring的DataAccessException
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 如果发生异常,Spring会自动转换:
// javax.persistence.EntityNotFoundException -> EmptyResultDataAccessException
// javax.persistence.PersistenceException -> DataAccessException
}

实际应用示例

完整的订单创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@Service
public class OrderServiceImpl implements OrderService {

@Autowired
private UserAccountRepository userAccountRepository;

@Autowired
private InventoryRepository inventoryRepository;

@Autowired
private OrderRepository orderRepository;

@Override
@Transactional(rollbackFor = Exception.class)
public OrderResponse createOrder(OrderRequest orderRequest) {
try {
// 1. 使用悲观锁查询用户账户
UserAccount userAccount = userAccountRepository.findByUserIdWithLock(orderRequest.getUserId())
.orElseThrow(() -> new BusinessException("USER_NOT_FOUND", "用户账户不存在"));

// 2. 使用悲观锁查询库存
Inventory inventory = inventoryRepository.findByProductIdWithLock(orderRequest.getProductId())
.orElseThrow(() -> new BusinessException("PRODUCT_NOT_FOUND", "产品不存在"));

// 3. 验证库存和余额
if (inventory.getStockQuantity() < orderRequest.getQuantity()) {
throw new BusinessException("INSUFFICIENT_STOCK", "库存不足");
}

BigDecimal totalAmount = inventory.getPrice().multiply(new BigDecimal(orderRequest.getQuantity()));
if (userAccount.getBalance().compareTo(totalAmount) < 0) {
throw new BusinessException("INSUFFICIENT_BALANCE", "账户余额不足");
}

// 4. 执行原子性操作
int stockUpdated = inventoryRepository.deductStock(orderRequest.getProductId(), orderRequest.getQuantity());
if (stockUpdated == 0) {
throw new BusinessException("STOCK_DEDUCTION_FAILED", "库存扣减失败");
}

int balanceUpdated = userAccountRepository.deductBalance(orderRequest.getUserId(), totalAmount);
if (balanceUpdated == 0) {
throw new BusinessException("BALANCE_DEDUCTION_FAILED", "余额扣减失败");
}

// 5. 创建订单记录
Order order = new Order(
generateOrderId(),
orderRequest.getUserId(),
orderRequest.getProductId(),
orderRequest.getQuantity(),
inventory.getPrice(),
totalAmount,
Order.OrderStatus.SUCCESS
);

order = orderRepository.save(order);

return OrderResponse.success(order, inventory.getProductName(), "订单创建成功");

} catch (BusinessException e) {
// 创建失败的订单记录
Order failedOrder = new Order(
generateOrderId(),
orderRequest.getUserId(),
orderRequest.getProductId(),
orderRequest.getQuantity(),
BigDecimal.ZERO,
BigDecimal.ZERO,
Order.OrderStatus.FAILED
);
orderRepository.save(failedOrder);

return OrderResponse.failure(generateOrderId(), orderRequest.getUserId(),
orderRequest.getProductId(), e.getMessage());
}
}
}

最佳实践

1. 事务管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ✅ 正确:在Service层使用@Transactional
@Service
public class OrderService {
@Transactional(rollbackFor = Exception.class)
public void createOrder() {
// 业务逻辑
}
}

// ❌ 错误:在Controller层使用@Transactional
@Controller
public class OrderController {
@Transactional // 不推荐
public void createOrder() {
// 业务逻辑
}
}

2. 锁的使用

1
2
3
4
5
6
7
8
9
// ✅ 正确:在需要的地方使用锁
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdWithLock(@Param("id") Long id);

// ❌ 错误:过度使用锁
@Lock(LockModeType.PESSIMISTIC_WRITE) // 不必要的锁
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findByName(@Param("name") String name);

3. 查询优化

1
2
3
4
5
6
7
// ✅ 正确:使用投影查询
@Query("SELECT new com.example.UserDTO(u.id, u.name) FROM User u WHERE u.age > :age")
List<UserDTO> findUsersByAge(@Param("age") Integer age);

// ✅ 正确:使用分页查询
@Query("SELECT u FROM User u WHERE u.status = :status")
Page<User> findByStatus(@Param("status") String status, Pageable pageable);

4. 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
// ✅ 正确:明确的异常处理
@Transactional(rollbackFor = Exception.class)
public void businessMethod() {
try {
// 业务逻辑
} catch (BusinessException e) {
// 业务异常,需要回滚
throw e;
} catch (Exception e) {
// 系统异常,需要回滚
throw new BusinessException("SYSTEM_ERROR", "系统异常", e);
}
}

5. 性能考虑

1
2
3
4
5
6
7
8
// ✅ 正确:批量操作
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
int updateUserStatus(@Param("ids") List<Long> ids, @Param("status") String status);

// ✅ 正确:使用索引优化查询
@Query("SELECT u FROM User u WHERE u.email = :email") // email字段有索引
Optional<User> findByEmail(@Param("email") String email);

总结

Spring Data JPA注解提供了强大的数据访问和事务管理功能:

  1. @Modifying: 用于修改操作,确保数据一致性
  2. @Lock: 用于并发控制,防止数据竞争
  3. @Query: 用于自定义查询,提高查询效率
  4. @Transactional: 用于事务管理,确保ACID特性
  5. @Entity相关注解: 用于实体映射,定义数据结构

合理使用这些注解可以构建出高性能、高可靠性的数据访问层。