2015-08-11 26 views
22

derzeit ringe ich mit der Fähigkeit, nur die Daten zu holen, die ich brauche. Die Methode findAll() muss Daten abrufen, abhängig davon, wo sie aufgerufen wird. Ich möchte nicht für jedes Entity-Diagramm verschiedene Methoden schreiben. Ich würde auch vermeiden, Entitymanager aufzurufen und die (sich wiederholenden) Anfragen selbst zu bilden. Grundsätzlich möchte ich die Build in findAll-Methode verwenden, aber mit dem Entity-Graph meiner Mögen. Irgendeine Chance?Spring Data JPA und NamedEntityGraphs

@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 
} 

Und mein 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); 
} 
+0

Ich denke, die anders benannte Methoden Lösung wäre angemessener. –

Antwort

24

Wir liefen in ein ähnliches Problem und entwickelt mehrere potenzielle Lösungen, aber es scheint nicht für eine elegante Lösung zu sein, was ein weit verbreitetes Problem zu sein scheint.

1) Präfixe. Daten jpa bietet mehrere Präfixe (find, get, ...) für einen Methodennamen. Eine Möglichkeit besteht darin, verschiedene Präfixe mit verschiedenen benannten Graphen zu verwenden. Dies ist die geringste Arbeit, verbirgt aber die Bedeutung der Methode vor dem Entwickler und hat ein großes Potenzial, einige nicht offensichtliche Probleme mit dem Laden der falschen Entitäten zu verursachen.

@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. Eine weitere mögliche Lösung besteht darin, benutzerdefinierte Abfragemethoden zu erstellen und den EntityManager zu injizieren. Diese Lösung bietet Ihnen die sauberste Schnittstelle zu Ihrem Repository, da Sie Ihren Methoden einen aussagekräftigen Namen geben können. Es ist jedoch ein erheblicher Aufwand, Ihren Code zur Lösung hinzuzufügen UND Sie greifen den Entity Manager manuell, anstatt Spring Magic zu verwenden.

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. Im Wesentlichen gibt dies nur auf benannte Entitätsdiagramme auf und verwendet JPQL, um Ihre Joins für Sie zu bearbeiten. Nicht ideal meiner Meinung nach.

@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); 
} 

Wir gingen mit der Option 1, da es die einfachste in der Umsetzung ist aber das bedeutet, wenn wir unsere Repositories verwenden wir bei der Abruf Methoden suchen müssen sicherstellen, dass wir das mit der richtigen Entität Graph verwenden . Viel Glück.

Quellen:

Ich habe nicht genug Ruf alle meine Quellen zu veröffentlichen. Leider :(

10

Verwenden @EntityGraph zusammen mit @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(); 

    ... 

}

+1

Ich mag deine Lösung. Aber ich habe Problem mit NamedEntityGraphs konfrontiert - ich kann nicht benutzerdefinierte Objekt wie diese zurückgeben @Query ("SELECT neue DTO (f) von Entität f") ' –

7

Wir hatten das gleiche Problem und baute eine Spring Data JPA Erweiterung um es zu lösen:

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

Diese Erweiterung ermöglicht es, benannte oder dynamisch erstellte EntityGraph als Argument einer beliebigen Repository-Methode zu übergeben.

Mit dieser Erweiterung, würden Sie diese Methode haben sofort verfügbar:

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

Und in der Lage sein, es zu nennen mit einem EntityGraph zur Laufzeit ausgewählt.

+0

Ich machte meine Antwort genauer. –

+1

Dies ist sehr nützlich. Ich habe ein hervorragendes Spring Data-Ticket zu diesem Problem https://jira.spring.io/browse/DATAJPA-645?filter=-2. Haben Sie darüber nachgedacht, in das Spring Data-Projekt aufzunehmen? Funktioniert es mit QueryDsl-Prädikatmethoden? –

+0

Hallo Alan, vor dem Erstellen von https://github.com/Cosium/spring-data-jpa-entity-graph, habe ich dynamische Entitygraph Aufnahme in Ticket https://jira.spring.io/browse/DATAJPA-749 gefragt . Aber es wurde abgelehnt. QueryDsl-Unterstützung ist enthalten. Dafür gibt es einige Testfälle. Aber ich benutze QueryDsl nicht für meine Projekte. Wenn Sie Fehler finden, zögern Sie nicht, sie auf dem Tracker zu melden;) –

1

Können Sie versuchen, erstellen Sie den Namen EntiyGraph mit Kind, das Sie anfordern und geben Sie den gleichen Namen für die Suche alle Methode. Ex:

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

Für Ihren Fall:

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

Methodenname in Repository

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

diese Weise können Sie den Überblick über verschiedene Methoden findAll halten können.

Verwandte Themen