J'ai une classe Person:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
Avec une relation plusieurs-à-plusieurs qui est paresseuse.
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 le PersonRepository n'est que ce code, écrit selon ce guide
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Cependant, dans ce contrôleur, j'ai réellement besoin des données paresseuses. Comment puis-je déclencher son chargement?
Essayer d'y accéder échouera avec
échec de l'initialisation paresseuse d'une collection de rôles: no.dusken.momus.model.Person.roles, impossible d'initialiser le proxy - pas de session
ou d'autres exceptions selon ce que j'essaye.
Ma description xml , en cas de besoin.
Merci.
Person
objet en fonction d'un paramètre? En celaQuery
, incluez lafetch
clause et chargez leRoles
trop pour la personne.Réponses:
Vous devrez faire un appel explicite sur la collection paresseuse afin de l'initialiser (la pratique courante est d'appeler
.size()
dans ce but). Dans Hibernate, il existe une méthode dédiée pour this (Hibernate.initialize()
), mais JPA n'a pas d'équivalent. Bien sûr, vous devrez vous assurer que l'invocation est effectuée, lorsque la session est encore disponible, alors annotez votre méthode de contrôleur avec@Transactional
. Une alternative consiste à créer une couche de service intermédiaire entre le contrôleur et le référentiel qui pourrait exposer des méthodes qui initialisent des collections différées.Mettre à jour:
Veuillez noter que la solution ci-dessus est simple, mais entraîne deux requêtes distinctes dans la base de données (une pour l'utilisateur, une autre pour ses rôles). Si vous souhaitez obtenir de meilleures performances, ajoutez la méthode suivante à votre interface de référentiel Spring Data JPA:
Cette méthode utilisera la clause fetch join de JPQL pour charger avec empressement l'association de rôles en un seul aller-retour vers la base de données, et atténuera donc la pénalité de performances encourue par les deux requêtes distinctes dans la solution ci-dessus.
la source
join
pas lefetch
, l'ensemble sera retourné avecinitialized = false
; émettant donc toujours une deuxième requête une fois que l'ensemble est accédé.fetch
est la clé pour s'assurer que la relation est complètement chargée et éviter la deuxième requête.Bien qu'il s'agisse d'un ancien article, veuillez envisager d'utiliser @NamedEntityGraph (Javax Persistence) et @EntityGraph (Spring Data JPA). La combinaison fonctionne.
Exemple
puis le repo de printemps comme ci-dessous
la source
@NamedEntityGraph
fait partie de l'API JPA 2.1, qui n'est pas implémentée dans Hibernate avant la version 4.3.0.@EntityGraph(attributePaths = "employeeGroups")
peut être utilisé directement dans un référentiel de données Spring pour annoter une méthode sans avoir besoin d'un@NamedEntityGraph
sur votre @Entity - moins de code, facile à comprendre lorsque vous ouvrez le référentiel.Vous avez quelques options
Plus de travail, meilleures performances.
Moins de travail, généralement acceptable dans les environnements Web.
Moins de travail, utile lorsque OEMIV n'est pas en option, par exemple dans une application Swing, mais peut également être utile sur les implémentations de référentiel pour initialiser toute entité en un seul coup.
Pour la dernière option, j'ai écrit une classe utilitaire, JpaUtils pour initialiser les entités à une certaine profondeur.
Par exemple:
la source
il ne peut être chargé que paresseusement pendant une transaction. Vous pouvez donc accéder à la collection dans votre référentiel, qui a une transaction - ou ce que je fais normalement, c'est un
get with association
, ou définir fetchmode sur eager.la source
Je pense que vous avez besoin d' OpenSessionInViewFilter pour garder votre session ouverte pendant le rendu de la vue (mais ce n'est pas une très bonne pratique).
la source
Données de printemps
JpaRepository
The Spring Data
JpaRepository
définit les deux méthodes suivantes:getOne
, qui renvoie un proxy d'entité adapté à la définition d'une association@ManyToOne
ou@OneToOne
parente lors de la persistance d'une entité enfant .findById
, qui renvoie l'entité POJO après l'exécution de l'instruction SELECT qui charge l'entité à partir de la table associéeCependant, dans votre cas, vous n'avez appelé ni
getOne
nifindById
:Donc, je suppose que le
findOne
méthode est une méthode que vous avez définie dans lePersonRepository
. Cependant, lafindOne
méthode n'est pas très utile dans votre cas. Puisque vous devez récupérer la collectionPerson
avec isroles
, il est préférable d'utiliser unefindOneWithRoles
méthode à la place.Méthodes de données Spring personnalisées
Vous pouvez définir une
PersonRepositoryCustom
interface comme suit:Et définissez sa mise en œuvre comme ceci:
C'est tout!
la source
Vous pouvez faire la même chose comme ceci:
Utilisez simplement faqQuestions.getFaqAnswers (). Size () nin votre contrôleur et vous obtiendrez la taille si la liste initialisée paresseusement, sans récupérer la liste elle-même.
la source