web-dev-qa-db-fra.com

Chargement paresseux JPA

J'ai un problème avec la propriété de chargement paresseux dans l'entité JPA. J'ai lu de nombreuses questions similaires, mais elles sont liées au printemps ou à l'hibernation et leurs réponses ne sont ni applicables ni utiles.

L'application est JEE avec JPA2.1 s'exécutant sur le serveur d'applications Wildfly. Il y a deux entités, le bean de session DAO et le servlet qui le rassemblent:

@Entity
@Table(name = "base_user")
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;

    @OneToMany(fetch=FetchType.LAZY, mappedBy="user")
    List<OAuthLogin> oauthLogins;

}


@Entity
@Table(name = "oauth_login")
public class OAuthLogin implements Serializable {
    @ManyToOne
    @JoinColumn(name="user_id", nullable=false)
    User user;
}


@Stateless(name = "UserDAOEJB")
public class UserDAO {
    @PersistenceContext(unitName="OAUTHDEMO")
    EntityManager em;

    public User findById(int id) {
        User entity;
    entity = em.find(User.class, id);
        return entity;
    }
}


public class SaveUserServlet extends HttpServlet {
    @EJB
    UserDAO userDAO;

    @Transactional
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        User user = new User(name);
        user.setEmail(email);
        System.out.println("Persisting user " + user);
        userDAO.persist(user);

        OAuthLogin fbLogin1 = new OAuthLogin(user, OAuthProvider.FACEBOOK, "1501791394");
        loginDAO.persist(fbLogin1);

        User user2 = userDAO.findById(user.getId());
        List<OAuthLogin> oauthLogins = user2.getOauthLogins();

Lorsque j'exécute ce code, il échoue avec:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cz.literak.demo.oauth.model.entity.User.oauthLogins, could not initialize proxy - no Session
org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.Java:572)
org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.Java:212)
org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.Java:551)
org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.Java:140)
org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.Java:294)
cz.literak.demo.oauth.servlets.SaveUserServlet.doPost(SaveUserServlet.Java:66)

J'ai utilisé un modèle très similaire avec WebLogic/JPA1 et cela s'est bien passé. Une idée? Merci

PS. il s'agit d'une application JPA, je n'ai pas de session d'hibernation, etc.

11
Leos Literak

Il existe peu d'alternatives que vous pouvez utiliser:

Utilisez la persistance en cascade:

@OneToMany(fetch=FetchType.LAZY, mappedBy="user", cascade = {CascadeType.PERSIST})
List<OAuthLogin> oauthLogins;

Dans votre Servlet, faites:

User user = new User(name);
user.setEmail(email);
OAuthLogin fbLogin = new OAuthLogin(user, OAuthProvider.FACEBOOK, "1501791394");      
user.getOauthLogins().add(fbLogin) // this is enough assuming uni-directional association
userDAO.persist(user);
List<OAuthLogin> oauthLogins = user.getOauthLogins();

Cela devrait faire l'affaire, plus vous avez une seule transaction et moins d'appels JDBC.

Ceci est utile pour ce cas d'utilisation spécifique où cet appel de méthode Servlet spécifique.

collection de prélecture dans EJB

public User findById(int id, boolean prefetch) {
    User entity = em.find(User.class, id);
    if (prefetch) {
        // Will trigger 1 or size JDBC calls depending on your fetching strategy
        entity.getOauthLogins().size() 
    }
    return entity;
}

Alternativement, Remplacer le mode de récupération en utilisant un critère

Cela est utile pour chaque cas où vous souhaitez récupérer la collection OAuthLogin avec le User tout en conservant un FetchType.LAZY et évitez LazyInitializationException pour cette collection spécifique uniquement.

Utiliser Open Entity Manager dans le filtre d'affichage

Sur Google, vous trouverez de nombreux exemples

Cela empêchera essentiellement LazyInitializationException, pour chaque association récupérée paresseusement, pour chaque application d'entité à l'échelle transversale

PS:

  1. Si vous n'utilisez pas Spring, pourquoi utilisez-vous @Transactional (par défaut même ne s'applique pas à HttpServlet)
  2. Cela avait fonctionné pour WebLogic en utilisant probablement une sorte de solution sur mesure
15
Ori Dar

LazyInitializationException signifie que vous accédez à une collection paresseuse APRÈS la fermeture de la session associée.

Comme votre code semble correct, votre problème peut être le même que dans cette question LazyInitializationException dans JPA et Hibernate

Pouvez-vous vérifier que votre transaction est ouverte au début de la méthode?

3
Andreas Aumayr

Pour initialiser laze dans JPA, vous devez appeler une bibliothèque jar et la démarrer, si c'est avec maven ou un exemple manuel, si vous avez besoin de laze, utilisez jar dans maven
jar de.empulse.eclipselink Plus d'infoshttp://wiki.Eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Maven_plugin

1
GuapoSasa