Spring Data JPA Et NamedEntityGraphs
actuellement, je me bats pour ne pouvoir récupérer que les données dont j'ai besoin. La méthode findAll () doit récupérer les données en fonction de l'endroit où elle est appelée. Je ne veux pas finir par écrire des méthodes différentes pour chaque entité graphique. De plus, j'éviterais d'appeler les entitymanagers et de former moi-même les requêtes (répétitives). Bref si je veux utiliser la méthode findAll, mais avec l'entité graphique de mon goût. A tout hasard?
@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
}
et mon 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);
}
4 réponses
nous avons rencontré un problème similaire et avons conçu plusieurs solutions prospectives, mais il ne semble pas y avoir de solution élégante pour ce qui semble être un problème commun.
1) préfixes. Données jpa permet plusieurs préfixes (find, get, ...) pour un nom de méthode. Une possibilité est d'utiliser différents préfixes avec différents graphes nommés. C'est le moins de travail, mais cache la signification de la méthode du développeur et a beaucoup de potentiel pour causer certains non-évidence problèmes avec le mauvais chargement des entités.
@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. Une autre solution possible est de créer des méthodes de requête personnalisées et d'injecter L'EntityManager. Cette solution vous donne l'interface la plus propre à votre dépôt parce que vous pouvez nommer vos méthodes quelque chose de significatif, mais il est une quantité significative de complexité à ajouter à votre code pour fournir la solution et vous êtes manuellement accaparant le gestionnaire d'entité au lieu d'utiliser la magie de printemps.
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. Il s'agit essentiellement d'abandonner sur les graphiques d'entity nommés et D'utiliser JPQL pour gérer vos jointures pour vous. Non-idéal, à mon avis.
@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);
}
nous sommes allés avec l'option 1 parce que c'est la plus simple dans la mise en œuvre, mais cela signifie que lorsque nous utilisons nos dépôts, nous devons regarder les méthodes fetch pour nous assurer que nous utilisons celui avec le graphe d'entité correct. Bonne chance.
Sources:
- JPA EntityGraph avec différents points de vue à l'aide de Printemps
- https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
Je n'ai pas assez de réputation pour publier toutes mes sources. Désolé: (
utiliser @EntityGraph
avec @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();
...
}
nous avons eu le même problème et construit une extension JPA de données de ressort pour le résoudre:
https://github.com/Cosium/spring-data-jpa-entity-graph
cette extension permet de passer L'EntityGraph nommé ou construit dynamiquement comme argument de n'importe quelle méthode de dépôt.
avec cette extension, vous auriez cette méthode immédiatement disponible:
List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
et pouvoir l'appeler avec un EntityGraph sélectionné à l'exécution.
pouvez-vous essayer de créer un nom EntiyGraph avec enfant que vous demanderez et donner le même nom à la méthode find all. Ex:
@EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
Employee getProfileAddressRecordById(long id);
pour votre affaire:
@NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre")
})
nom de la méthode dans le dépôt
@EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
findAllCustomerHandlingEmployeeGenre
de cette façon, vous pouvez garder la trace de différentes méthodes findAll.