トランザクション性
CrudRepository
インスタンスのメソッドは、デフォルトでトランザクションです。読み取り操作の場合、トランザクション構成 readOnly
フラグは true
に設定されます。他のすべては、デフォルトのトランザクション構成が適用されるように、プレーンな @Transactional
アノテーションで構成されています。詳細については、SimpleJdbcRepository
(Javadoc) の Javadoc を参照してください。リポジトリで宣言されたメソッドの 1 つのトランザクション構成を微調整する必要がある場合は、次のように、リポジトリインターフェースでメソッドを再宣言します。
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 は異なる接続でクエリを実行します。これにより、接続プールに過度の負担がかかる可能性があり、複数のメソッドが 1 つの接続を保持している間に新しい接続をリクエストすると、デッドロックが発生する可能性もあります。 |
readOnly フラグを設定することにより、読み取り専用クエリをそのようにマークすることは間違いなく合理的です。ただし、これは、操作クエリをトリガーしないことのチェックとしては機能しません(ただし、一部のデータベースは、読み取り専用トランザクション内の INSERT および UPDATE ステートメントを拒否します)。代わりに、readOnly フラグは、パフォーマンスを最適化するためのヒントとして、基盤となる JDBC ドライバーに伝達されます。 |
JDBC ロック
Spring Data JDBC は、派生クエリメソッドのロックをサポートします。リポジトリ内の特定の派生クエリメソッドのロックを有効にするには、@Lock
でアノテーションを付けます。型 LockMode
の必須値には、読み取っているデータが変更されないことを保証する PESSIMISTIC_READ
と、データを変更するためのロックを取得する PESSIMISTIC_WRITE
の 2 つの値があります。一部のデータベースでは、この区別がされません。その場合、両方のモードは 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