トランザクション性

デフォルトでは、CrudRepository から継承されたメソッドは SimpleJpaRepository (Javadoc) からトランザクション構成を継承します。読み取り操作の場合、トランザクション構成 readOnly フラグが true に設定されます。他のすべてはプレーンな @Transactional で構成されているため、デフォルトのトランザクション構成が適用されます。トランザクションリポジトリフラグメントによってサポートされるリポジトリメソッドは、実際のフラグメントメソッドからトランザクション属性を継承します。

リポジトリで宣言されているメソッドの 1 つのトランザクション構成を微調整する必要がある場合は、次のように、リポジトリインターフェースでメソッドを再宣言します。

例 1: CRUD のカスタムトランザクション設定
public interface UserRepository extends CrudRepository<User, Long> {

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

  // Further query method declarations
}

これを行うと、findAll() メソッドは 10 秒のタイムアウトで、readOnly フラグなしで実行されます。

トランザクションの動作を変更するもう 1 つの方法は、(通常)複数のリポジトリをカバーするファサードまたはサービス実装を使用することです。その目的は、非 CRUD 操作のトランザクション境界を定義することです。次の例は、そのようなファサードを複数のリポジトリに使用する方法を示しています。

例 2: ファサードを使用して複数のリポジトリ呼び出しのトランザクションを定義する
@Service
public class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  public 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 を明示的に使用する必要があることに注意してください。この例では、コンポーネントスキャンを使用することを前提としています。

JPA の観点からは save の呼び出しは厳密には必要ありませんが、Spring Data によって提供されるリポジトリの抽象化との整合性を保つために必要です。

トランザクションクエリメソッド

宣言されたクエリメソッド (既定のメソッドを含む) は、既定で適用されるトランザクション構成を取得しません。これらのメソッドをトランザクションで実行するには、次の例に示すように、定義したリポジトリインターフェースで @Transactional を使用します。

例 3: クエリメソッドで @Transactional を使用する
@Transactional(readOnly = true)
interface UserRepository extends JpaRepository<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 に設定して実行されます。

readOnly フラグを設定することにより、読み取り専用クエリにトランザクションを使用し、そのようなものとしてマークできます。ただし、そうすることで、クエリの操作をトリガーしないことを確認することはできません(ただし、一部のデータベースは、読み取り専用トランザクション内で INSERT および UPDATE ステートメントを拒否します)。readOnly フラグは、代わりに、パフォーマンスの最適化のための基礎となる JDBC ドライバーへのヒントとして伝搬されます。さらに、Spring は、基礎となる JPA プロバイダーでいくつかの最適化を実行します。例: Hibernate で使用する場合、トランザクションを readOnly として設定すると、フラッシュモードは NEVER に設定されます。これにより、Hibernate はダーティチェックをスキップします(大きなオブジェクトツリーの顕著な改善)。

例ではリポジトリでの @Transactional の使用について説明していますが、適切な一貫性と必要なトランザクション参加を確保するために、作業単位を開始するときにトランザクション境界を宣言することが一般的に推奨されます。