事務性
CrudRepository 例項的方法預設是事務性的。對於讀取操作,事務配置的 readOnly 標誌被設定為 true。所有其他操作都配置了普通的 @Transactional 註解,因此應用預設事務配置。有關詳細資訊,請參閱 SimpleJdbcRepository 的 Javadoc。如果您需要調整倉庫中宣告的方法的事務配置,請在倉庫介面中重新宣告該方法,如下所示
interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
List<User> findAll();
// Further query method declarations
}
上述程式碼導致 findAll() 方法在超時 10 秒且沒有 readOnly 標誌的情況下執行。
另一種改變事務行為的方法是使用通常涵蓋多個倉庫的門面或服務實現。其目的是為非 CRUD 操作定義事務邊界。以下示例展示瞭如何建立這樣的門面
@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(…) 的呼叫在事務內部執行(參與現有事務或在沒有正在執行的事務時建立新事務)。倉庫的事務配置被忽略,因為外部事務配置決定了實際要使用的倉庫。請注意,您必須顯式啟用 <tx:annotation-driven /> 或使用 @EnableTransactionManagement 才能使基於註解的門面配置生效。請注意,上述示例假定您使用元件掃描。
事務性查詢方法
為了使您的查詢方法具有事務性,請在您定義的倉庫介面上使用 @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 支援在派生查詢方法上進行鎖定。要在倉庫中的給定派生查詢方法上啟用鎖定,請使用 @Lock 對其進行註解。LockMode 型別的必需值提供兩個值:PESSIMISTIC_READ 保證您正在讀取的資料不會被修改,而 PESSIMISTIC_WRITE 獲取一個鎖來修改資料。一些資料庫不進行這種區分。在這種情況下,兩種模式都等同於 PESSIMISTIC_WRITE。
interface UserRepository extends CrudRepository<User, Long> {
@Lock(LockMode.PESSIMISTIC_READ)
List<User> findByLastname(String lastname);
}
如上所示,方法 findByLastname(String lastname) 將以悲觀讀鎖執行。如果您使用的是具有 MySQL 方言的資料庫,例如,這將導致以下查詢
Select * from user u where u.lastname = lastname LOCK IN SHARE MODE
目前不支援在基於字串的查詢上使用 @Lock。使用 @Query 建立的查詢方法將忽略 @Lock 提供的鎖定資訊,在基於字串的查詢上使用 @Lock 將導致日誌中出現警告。未來的版本將丟擲異常。 |