1. ホーム
  2. java

[解決済み] Spring ControllerでJPAとHibernateを使用してFetchType.LAZYのアソシエーションを取得する方法

2022-04-25 10:10:32

質問

Personクラスがあります。

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(fetch = FetchType.LAZY)
    private List<Role> roles;
    // etc
}

多対多のリレーションが遅延している状態。

私のコントローラでは

@Controller
@RequestMapping("/person")
public class PersonController {
    @Autowired
    PersonRepository personRepository;

    @RequestMapping("/get")
    public @ResponseBody Person getPerson() {
        Person person = personRepository.findOne(1L);
        return person;
    }
}

そして、PersonRepositoryは、次のコードに従って書かれたものだけです。 このガイド

public interface PersonRepository extends JpaRepository<Person, Long> {
}

しかし、このコントローラでは 私は実際にlazy-dataを必要としています。どのようにしたら、その読み込みをトリガーできるのでしょうか?

アクセスしようとすると、次のように失敗します。

ロールのコレクションを遅延初期化することに失敗しました。 no.dusken.momus.model.Person.roles, プロキシを初期化できませんでした - no セッション

など、試行錯誤の結果、例外が発生しました。

私の xml-description。 必要であれば

ありがとうございます。

解決方法は?

遅延コレクションを初期化するために、明示的に呼び出す必要があります (一般的な方法としては、遅延コレクションを呼び出すために .size() を使用します)。Hibernateでは、このための専用のメソッドがあります( Hibernate.initialize() ) が、JPAにはそれに相当するものがありません。もちろん、セッションがまだ利用可能なときに呼び出されるようにしなければなりませんので、コントローラメソッドに @Transactional . 代替案としては、コントローラとリポジトリの間に中間サービス層を作成し、遅延コレクションを初期化するメソッドを公開することができます。

更新します。

上記の解決策は簡単ですが、データベースへの問い合わせが2つになることに注意してください (1つはユーザーに対するもの、もう1つはロールに対するものです)。より良いパフォーマンスを得たい場合は、以下のメソッドをSpring Data JPAリポジトリのインターフェイスに追加してください。

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query("SELECT p FROM Person p JOIN FETCH p.roles WHERE p.id = (:id)")
    public Person findByIdAndFetchRolesEagerly(@Param("id") Long id);

}

このメソッドでは、JPQLの フェッチジョイン 節を使用して、データベースへの一回のラウンドトリップでロールの関連付けを積極的にロードすることで、上記のソリューションで2つの異なるクエリによって発生するパフォーマンスの損失を軽減することができます。