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.
Il existe peu d'alternatives que vous pouvez utiliser:
@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.
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.
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:
@Transactional
(par défaut même ne s'applique pas à HttpServlet
)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?
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