事務性

`CrudRepository` 例項的方法預設是事務性的。對於讀取操作,事務配置的 `readOnly` 標誌設定為 `true`。所有其他操作都配置了普通的 `@Transactional` 註解,因此應用預設的事務配置。詳情請參閱 SimpleJdbcRepository 的 Javadoc。如果您需要調整 Repository 中某個方法的事務配置,請在您的 Repository 介面中重新宣告該方法,如下所示

CRUD 的自定義事務配置
interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  List<User> findAll();

  // Further query method declarations
}

上述配置使 `findAll()` 方法以 10 秒的超時時間執行,並且沒有 `readOnly` 標誌。

另一種改變事務行為的方式是使用通常覆蓋多個 Repository 的 Facade(門面)或服務實現。其目的是為非 CRUD 操作定義事務邊界。以下示例展示瞭如何建立這樣的 Facade

使用 Facade 為多個 Repository 呼叫定義事務
@Service
public class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
}

前面的示例使得對 `addRoleToAllUsers(…)` 的呼叫在事務中執行(參與現有事務或在沒有事務執行時建立新事務)。Repository 的事務配置被忽略,因為外部事務配置決定了實際使用的 Repository。請注意,您必須顯式啟用 `<tx:annotation-driven />` 或使用 `@EnableTransactionManagement` 才能使基於註解的 Facade 配置生效。請注意,前面的示例假設您使用元件掃描。

事務性查詢方法

為了使您的查詢方法具有事務性,請在您定義的 Repository 介面上使用 `@Transactional`,如下例所示

在查詢方法上使用 @Transactional
@Transactional(readOnly = true)
interface UserRepository extends CrudRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常,您希望將 `readOnly` 標誌設定為 true,因為大多數查詢方法只讀取資料。與此不同的是,`deleteInactiveUsers()` 方法使用了 `@Modifying` 註解並覆蓋了事務配置。因此,該方法的 `readOnly` 標誌設定為 `false`。

強烈建議使查詢方法具有事務性。這些方法可能執行多個查詢以填充實體。如果沒有公共事務,Spring Data JDBC 會在不同的連線中執行查詢。這可能會對連線池造成過度壓力,甚至在多個方法在持有一個連線的同時請求新連線時導致死鎖。
透過設定 `readOnly` 標誌來標記只讀查詢是完全合理的。然而,這並不能確保您不會觸發操作資料的查詢(儘管有些資料庫在只讀事務中拒絕 `INSERT` 和 `UPDATE` 語句)。相反,`readOnly` 標誌會作為效能最佳化的提示傳播給底層的 JDBC 驅動程式。

JDBC 鎖定

Spring Data JDBC 支援對派生查詢方法進行鎖定。要在 Repository 中的給定派生查詢方法上啟用鎖定,您需要使用 `@Lock` 註解對其進行標註。所需的值型別為 `LockMode`,提供兩個值:`PESSIMISTIC_READ` 保證您讀取的資料不會被修改,而 `PESSIMISTIC_WRITE` 獲取用於修改資料的鎖。有些資料庫不區分這兩種模式。在這種情況下,兩種模式都等同於 `PESSIMISTIC_WRITE`。

在派生查詢方法上使用 @Lock
interface UserRepository extends CrudRepository<User, Long> {

  @Lock(LockMode.PESSIMISTIC_READ)
  List<User> findByLastname(String lastname);
}

如您所見,方法 `findByLastname(String lastname)` 將會以悲觀讀鎖執行。如果您使用 MySQL Dialect 的資料庫,例如,將生成以下查詢

MySQL 方言生成的 SQL 查詢
Select * from user u where u.lastname = lastname LOCK IN SHARE MODE