Comment charger paresseux récupéré des éléments de Hibernate/JPA, dans mon contrôleur
J'ai une classe de Personne:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
//etc
}
Avec un plusieurs-à-plusieurs relation qui est paresseux.
Dans mon contrôleur, j'ai
@Controller
@RequestMapping("/person")
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/get")
public @ResponseBody Person getPerson() {
Person person = personRepository.findOne(1L);
return person;
}
}
Et la PersonRepository est juste de ce code, rédigé selon ce guide
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Toutefois, dans ce contrôleur j'ai vraiment besoin de la lazy-données. Comment puis-je déclencher son chargement?
De la tentative d'accès, elle échouera avec
échoué paresseusement initialiser une collection de rôle:
pas de.brunante.momus.de modèle.Personne.les rôles, n'a pas pu initialiser proxy - pas de
Session
ou d'autres exceptions en fonction de ce que j'ai essayer.
Mon xml-description, en cas de besoin.
Grâce.
- Pouvez-vous écrire une méthode qui permettra de créer une requête pour récupérer un
Person
objet compte tenu de certains paramètres? Dans ceQuery
, comprennent lafetch
clause de charge et de laRoles
trop pour la personne.
Vous devez vous connecter pour publier un commentaire.
Vous aurez à faire un appel explicite sur la collection en différé afin de l'initialiser (pratique courante est d'appeler
.size()
à cet effet). En mode veille prolongée, il est une méthode dédiée à cet effet (Hibernate.initialize()
), mais JPA n'a pas d'équivalent. Bien sûr, vous devrez veiller à ce que l'invocation est le cas, lorsque la session est toujours disponible, afin d'annoter votre méthode de contrôleur avec@Transactional
. Une alternative est de créer un intermédiaire de la couche de Service entre le Contrôleur et le Référentiel qui pourrait exposer les méthodes initialize paresseux collections.Mise à jour:
Veuillez noter que la solution ci-dessus est facile, mais les résultats de deux requêtes distinctes à la base de données (pour l'utilisateur, un autre de ses rôles). Si vous souhaitez obtenir une meilleure performance ajoutez la méthode suivante à votre source de Données JPA référentiel d'interface:
Cette méthode utilisera JPQL de chercher rejoindre clause avec impatience charge le rôle de l'association dans un seul voyage aller-retour à la base de données, et donc d'atténuer la performance peine encourue par les deux requêtes dans la solution ci-dessus.
join
sansfetch
, le jeu sera retourné avecinitialized = false
; donc encore la délivrance d'un deuxième requête une fois que l'ensemble est accessible.fetch
est la clé pour faire en sorte que la relation est complètement chargé et en évitant de la deuxième requête.findByIdAndFetchRolesEagerly
solution fonctionne très bien. @MU pourmails
essayer qqch comme:@Query("SELECT p FROM Person p JOIN FETCH p.roles WHERE p.email = (:email)") public Person findByEmailAndFetchRolesEagerly(@Param("email") String email);
Si c'est un vieux post, considérez l'utilisation de @NamedEntityGraph (Javax Persistance) et @EntityGraph (Spring Data JPA). La combinaison fonctionne.
Exemple
et puis le printemps repo comme ci-dessous
@NamedEntityGraph
est une partie de la JPA 2.1 de l'API, ce qui n'est pas mis en œuvre en veille prolongée avant la version 4.3.0.Vous avez quelques options
Plus de travail, de meilleures performances.
Moins de travail, généralement acceptable dans les environnements web.
Moins de travail, utile quand OEMIV n'est pas en option, par exemple dans une application Swing, mais il peut être utile de trop sur le référentiel des implémentations pour initialiser toute entité d'un seul coup.
Pour la dernière option, j'ai écrit une classe utilitaire, JpaUtils à initilize entités à certains deph.
Par exemple:
il ne peut être paresseusement chargé tandis qu'à l'intérieur d'une transaction. Donc, vous pouvez accéder à la collection dans votre référentiel, qui a une transaction ou de ce que J'ai l'habitude de faire, c'est un
get avec l'association
, ou de définir fetchmode à la hâte.Je pense que vous avez besoin OpenSessionInViewFilter pour garder votre session ouverte pendant le rendu de l'affichage (mais il n'est pas trop de bonne pratique).
Vous pouvez faire la même chose comme ceci:
Suffit d'utiliser faqQuestions.getFaqAnswers().size()nin votre manette et vous obtiendrez la taille si paresseusement intialised liste, sans aller chercher de la liste elle-même.