2015-08-11 17 views
22

al momento sto lottando con il recupero dei soli dati necessari. Il metodo findAll() deve recuperare i dati in base a dove viene richiamato. Non voglio finire di scrivere metodi diversi per ogni grafico di entità. Inoltre, eviterei di chiamare i gestori di entità e di formulare le domande (ripetitive) da solo. In sostanza, voglio utilizzare la build nel metodo findAll, ma con il grafico delle entità di mio gradimento. Qualche chance?Spring Data JPA e 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 
} 

E il mio 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

Penso che la soluzione dei metodi con nomi diversi sarebbe più appropriata. –

risposta

24

ci siamo imbattuti in un problema simile e messo a punto diverse soluzioni potenziali, ma non sembra essere una soluzione elegante per quello che sembra essere un problema comune.

1) Prefissi. Data jpa offre diversi prefissi (find, get, ...) per il nome di un metodo. Una possibilità è quella di utilizzare diversi prefissi con diversi grafici con nome. Questo è il minimo lavoro ma nasconde il significato del metodo dallo sviluppatore e ha un grande potenziale di causare alcuni problemi non ovvi con il caricamento delle entità sbagliate.

@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. Un'altra possibile soluzione è creare metodi di interrogazione personalizzati e iniettare EntityManager. Questa soluzione ti offre l'interfaccia più pulita per il tuo repository perché puoi dare un nome ai tuoi metodi in modo significativo, ma è una notevole quantità di complessità aggiungere al tuo codice per fornire la soluzione E stai afferrando manualmente il gestore di entità invece di usare la magia di 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. In sostanza, si tratta semplicemente di rinunciare ai grafici delle entità con nome e all'utilizzo di JPQL per gestire i tuoi join per te. Non ideale secondo me.

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

Siamo andati con l'opzione 1, perché è il più semplice attuazione, ma questo vuol dire quando usiamo i nostri repository abbiamo dovuto guardare ai metodi di recupero su assicurarsi che stiamo usando quello con il grafico entità corretta . In bocca al lupo.

Fonti:

Io non hanno abbastanza reputazione di inviare tutte le mie fonti. Ci dispiace :(

10

Usa @EntityGraph insieme @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

Mi piace la tua soluzione. Ma ho riscontrato problemi con NamedEntityGraphs - Non posso restituire oggetti personalizzati come questo '@Query (" SELEZIONA nuovo DTO (f) da Entity f ")' –

7

Abbiamo avuto lo stesso problema e costruito un'estensione primavera dati APP a risolverlo:

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

Questo estensione consente di passare EntityGraph denominato o generato dinamicamente come argomento di qualsiasi metodo di repository.

Con questa estensione, si avrebbe questo metodo immediatamente disponibili:

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

ed essere in grado di chiamare con un EntityGraph selezionato in fase di esecuzione.

+0

Ho reso la mia risposta più accurata. –

+1

Questo è molto utile. Ho un eccezionale biglietto Spring Data intorno a questo problema https://jira.spring.io/browse/DATAJPA-645?filter=-2. Hai preso in considerazione l'integrazione nel progetto Spring Data? Funziona con i metodi QueryDsl Predicate? –

+0

Ciao Alan, prima di creare https://github.com/Cosium/spring-data-jpa-entity-graph, ho chiesto l'inclusione di entitygraph dinamico nel ticket https://jira.spring.io/browse/DATAJPA-749 . Ma è stato rifiutato. Il supporto QueryDsl è incluso. Ci sono alcuni casi di test per questo. Ma non uso QueryDsl sui miei progetti. Se trovi bug non esitare a segnalarli sul tracker;) –

1

Si può provare a creare il nome EntiyGraph con il figlio che verrà richiesto e che darà lo stesso nome al metodo find all. Es:

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

Per il vostro caso: nome

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

metodo nella repository

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

In questo modo è possibile tenere traccia dei diversi metodi di findall.

Problemi correlati