web-dev-qa-db-ja.com

Spring Data JPAおよびNamedEntityGraphs

現在、必要なデータのみを取得できるように取り組んでいます。 findAll()メソッドは、呼び出される場所に応じてデータを取得する必要があります。エンティティグラフごとに異なるメソッドを記述したくはありません。また、私はentitymanagersを呼び出して(繰り返し)クエリを自分で作成することを避けます。基本的に、findAllメソッドでビルドを使用しますが、好みのエンティティグラフを使用します。チャンスはありますか?

@Entity
@Table(name="complaints")
@NamedEntityGraphs({
    @NamedEntityGraph(name="allJoinsButMessages", attributeNodes = {
            @NamedAttributeNode("customer"),
            @NamedAttributeNode("handling_employee"),
            @NamedAttributeNode("genre")
    }),
    @NamedEntityGraph(name="allJoins", attributeNodes = {
            @NamedAttributeNode("customer"),
            @NamedAttributeNode("handling_employee"),
            @NamedAttributeNode("genre"),
            @NamedAttributeNode("complaintMessages")
    }),
    @NamedEntityGraph(name="noJoins", attributeNodes = {

    })
})
public class Complaint implements Serializable{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private long id;

    private Timestamp date;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer")
    private User customer;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "handling_employee")
    private User handling_employee;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="genre")
    private Genre genre;

    private boolean closed;

    @OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>();

//getters and setters
}

そして、私のJPARepository

@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{

    List<Complaint> findByClosed(boolean closed);

    @EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
    @Override
    List<Complaint> findAll(Sort sort);
}
41
dendimiiii

同様の問題にぶつかり、いくつかの将来的な解決策を考案しましたが、一般的な問題と思われるものに対するエレガントな解決策はないようです。

1)プレフィックス。 Data jpaでは、メソッド名に複数のプレフィックス(find、get、...)を使用できます。 1つの可能性は、異なる名前付きグラフで異なるプレフィックスを使用することです。これは最も手間がかかりますが、開発者からメソッドの意味を隠し、誤ったエンティティの読み込みでいくつかの非自明な問題を引き起こす可能性があります。

@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
    User findByUserID(int id);

    @EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
    User readByUserId(int id);
}

2)CustomRepository。別の可能な解決策は、カスタムクエリメソッドを作成し、EntityManagerを挿入することです。このソリューションは、メソッドに意味のある名前を付けることができるため、リポジトリへの最もクリーンなインターフェイスを提供しますが、ソリューションを提供するためにコードに追加するのは非常に複雑であり、Springマジックを使用する代わりにエンティティマネージャーを手動で取得します。

interface UserRepositoryCustom {
    public User findUserWithMembershipYearsById(int id);
}

class UserRepositoryImpl implements UserRepositoryCustom {
    @PersistenceContext
    private EntityManager em;
    @Override
    public User findUserWithMembershipYearsById(int id) {
        User result = null;
        List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
                .setParameter("id", id)
                .setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
                .getResultList();
        if(users.size() >= 0) {
            result = users.get(0);
        }
        return result;
    }
}

@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
    User findByUserID(int id);
}

3)JPQL。基本的に、これは名前付きエンティティグラフを放棄し、JPQLを使用して結合を処理するだけです。私の意見では理想的ではありません。

@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
    @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
    User findByUserID(int id);

    @Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
    User findUserWithTags(@Param("id") final int id);
}

実装が最も簡単なため、オプション1を使用しましたが、これはリポジトリを使用する場合、フェッチメソッドを調べて正しいエンティティグラフを使用することを確認する必要があることを意味します。幸運を。

ソース:

すべてのソースを投稿するほどの評判はありません。ごめんなさい :(

39
Chris Spencer

同じ問題があり、それを解決するためにSpring Data JPA拡張を構築しました:

https://github.com/Cosium/spring-data-jpa-entity-graph

この拡張により、名前付きまたは動的に構築されたEntityGraphを任意のリポジトリメソッドの引数として渡すことができます。

この拡張機能を使用すると、このメソッドをすぐに使用できます。

List<Complaint> findAll(Sort sort, EntityGraph entityGraph);

また、実行時に選択されたEntityGraphで呼び出すことができます。

14

つかいます @EntityGraph 一緒に @Query

@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{

   @EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
   @Query("SELECT c FROM Complaint ORDER BY ..")
   @Override
   List<Complaint> findAllJoinsButMessages();

   @EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
   @Query("SELECT c FROM Complaint ORDER BY ..")
   @Override
   List<Complaint> findAllJoin();

   ...

}

11
GKislin

あなたが要求するEntiyGraph名を子で作成して、find allメソッドに同じ名前を付けてみてください。例:

@EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
 Employee getProfileAddressRecordById(long id);

あなたの場合:

@NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
        @NamedAttributeNode("customer"),
        @NamedAttributeNode("handling_employee"),
        @NamedAttributeNode("genre")
})

リポジトリ内のメソッド名

@EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
 findAllCustomerHandlingEmployeeGenre

これにより、さまざまなfindAllメソッドを追跡できます。

1
smile