Je reçois l'exception suivante:
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.Java:167)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.Java:215)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.Java:190)
at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.Java)
at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.Java:139)
at JSON_to_XML.main(JSON_to_XML.Java:84)
quand j'essaie d'appeler depuis la ligne principale les lignes suivantes:
Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());
J'ai d'abord implémenté la méthode getModelByModelGroup(int modelgroupid)
comme ceci:
public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {
Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();
Transaction tx = null;
if (openTransaction)
tx = session.getTransaction();
String responseMessage = "";
try {
if (openTransaction)
tx.begin();
Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
query.setParameter("modelGroupId", modelGroupId);
@SuppressWarnings("unchecked")
List<Model> modelList = (List<Model>)query.list();
Model model = null;
// Cerco il primo Model che è in esercizio: idwf_model_type = 3
for (Model m : modelList)
if (m.getModelType().getId() == 3) {
model = m;
break;
}
if (model == null) {
Object[] arrModels = modelList.toArray();
if (arrModels.length == 0)
throw new Exception("Non esiste ");
model = (Model)arrModels[0];
}
if (openTransaction)
tx.commit();
return model;
} catch(Exception ex) {
if (openTransaction)
tx.rollback();
ex.printStackTrace();
if (responseMessage.compareTo("") == 0)
responseMessage = "Error" + ex.getMessage();
return null;
}
et a l'exception. Ensuite, un ami m'a suggéré de toujours tester la session et de récupérer la session en cours pour éviter cette erreur. Alors j'ai fait ça:
public static Model getModelByModelGroup(int modelGroupId) {
Session session = null;
boolean openSession = session == null;
Transaction tx = null;
if (openSession){
session = SessionFactoryHelper.getSessionFactory().getCurrentSession();
tx = session.getTransaction();
}
String responseMessage = "";
try {
if (openSession)
tx.begin();
Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
query.setParameter("modelGroupId", modelGroupId);
@SuppressWarnings("unchecked")
List<Model> modelList = (List<Model>)query.list();
Model model = null;
for (Model m : modelList)
if (m.getModelType().getId() == 3) {
model = m;
break;
}
if (model == null) {
Object[] arrModels = modelList.toArray();
if (arrModels.length == 0)
throw new RuntimeException("Non esiste");
model = (Model)arrModels[0];
if (openSession)
tx.commit();
return model;
} catch(RuntimeException ex) {
if (openSession)
tx.rollback();
ex.printStackTrace();
if (responseMessage.compareTo("") == 0)
responseMessage = "Error" + ex.getMessage();
return null;
}
}
mais obtenez toujours la même erreur. J'ai beaucoup lu pour cette erreur et j'ai trouvé quelques solutions possibles. L'un d'eux consistait à définir lazyLoad sur false mais je ne suis pas autorisé à le faire, c'est pourquoi on m'a suggéré de contrôler la session
Ce qui est faux ici, c'est que votre configuration de gestion de session est configurée pour fermer la session lorsque vous validez une transaction. Vérifiez si vous avez quelque chose comme:
<property name="current_session_context_class">thread</property>
dans votre configuration.
Afin de résoudre ce problème, vous pouvez modifier la configuration de la fabrique de sessions ou ouvrir une autre session en demandant simplement à ces objets chargés paresseux. Mais ce que je suggérerais ici est d’initialiser cette collection paresseuse dans getModelByModelGroup lui-même et d’appeler:
Hibernate.initialize(subProcessModel.getElement());
quand vous êtes encore en session active.
Et une dernière chose. Un conseil amical. Vous avez quelque chose comme ça dans votre méthode:
for (Model m : modelList)
if (m.getModelType().getId() == 3) {
model = m;
break;
}
S'il vous plaît, installez ce code pour filtrer les modèles dont le type id est égal à 3 dans l'instruction de requête, juste quelques lignes ci-dessus.
Un peu plus de lecture:
Vous pouvez essayer de définir
<property name="hibernate.enable_lazy_load_no_trans">true</property>
dans hibernate.cfg.xml ou persistence.xml
Le problème à garder à l'esprit avec cette propriété sont bien expliqués ici
Si vous utilisez Spring, marquez la classe comme @Transactional , Spring se chargera de la gestion de session.
@Transactional
public class My Class {
...
}
En utilisant @Transactional
, de nombreux aspects importants, tels que la propagation de transaction, sont gérés automatiquement. Dans ce cas, si une autre méthode transactionnelle est appelée, la méthode aura l'option de rejoindre la transaction en cours en évitant l'exception "aucune session".
La meilleure façon de gérer la LazyInitializationException
est d'utiliser la directive JOIN FETCH
:
Query query = session.createQuery(
"from Model m " +
"join fetch m.modelType " +
"where modelGroup.id = :modelGroupId"
);
Quoi qu’il en soit, n’utilisez PAS les anti-motifs suivants comme suggéré par certaines des réponses:
Parfois, une projection DTO est un meilleur choix que d'extraire des entités, et de cette façon, vous n'obtiendrez pas LazyInitializationException
.
Je recevais la même erreur pour une à plusieurs relations pour l'annotation ci-dessous.
@OneToMany(mappedBy="department", cascade = CascadeType.ALL)
Modifié comme suit après l'ajout de fetch = FetchType.EAGER, cela a fonctionné pour moi.
@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Cette exception est due à la date de fermeture de la session lorsque vous appelez session.getEntityById()
. Vous devez donc reconnecter l'entité à la session. Ou La solution facile consiste simplement à configurer default-lazy="false"
avec votre entity.hbm.xml
ou, si vous utilisez des annotations, ajoutez simplement @Proxy(lazy=false)
à votre classe d'entité.
si vous utilisez spring data jpa, vous pouvez ajouter cette ligne dans application.properties
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
J'ai rencontré le même problème. Je pense qu’un autre moyen de résoudre ce problème consiste à modifier la requête afin qu’elle se joigne à votre élément, comme suit:
Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
Il existe plusieurs bonnes réponses ici qui traitent cette erreur dans une large portée. J'ai rencontré une situation spécifique avec Spring Security qui avait un correctif rapide, mais probablement pas optimal.
Pendant l'autorisation de l'utilisateur (immédiatement après la connexion et le passage de l'authentification), je testais une entité d'utilisateur pour une autorité spécifique dans une classe personnalisée qui étend SimpleUrlAuthenticationSuccessHandler.
Mon entité utilisateur implémente UserDetails et possède un ensemble de rôles chargés différemment qui lève l'exception "org.hibernate.LazyInitializationException - impossible d'initialiser le proxy - pas de session". Changer cet ensemble de "fetch = FetchType.LAZY" à "fetch = FetchType.EAGER" a corrigé cela pour moi.
Si vous utilisez JPQL, JOIN FETCH est la méthode la plus simple: http://www.objectdb.com/Java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
Cela signifie que l'objet auquel vous essayez d'accéder n'est pas chargé. Écrivez donc une requête qui crée un join fetch de l'objet auquel vous essayez d'accéder.
Par exemple:
Si vous essayez d'obtenir ObjectB à partir d'ObjectA, ObjectB est une clé étrangère dans ObjectA.
Requête:
SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
Confronté à la même exception dans des cas d'utilisation différents.
Cas d'utilisation: Essayez de lire les données de la base de données avec projection DTO.
Solution: Utilisez la méthode get au lieu de charge .
Opération générique
public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
Object o = null;
Transaction tx = null;
try {
Session session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction();
o = session.load(cls, s); /*change load to get*/
tx.commit();
session.close();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
}
Classe de persistance
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;
@Column(name = "Name")
private String customerName;
@Column(name = "City")
private String city;
//constructors , setters and getters
}
Interface CustomerDAO
public interface CustomerDAO
{
public CustomerTO getCustomerById(int cid);
}
Classe d'objet de transfert d'entité
public class CustomerTO {
private int customerId;
private String customerName;
private String city;
//constructors , setters and getters
}
Classe d'usine
public class DAOFactory {
static CustomerDAO customerDAO;
static {
customerDAO = new HibernateCustomerDAO();
}
public static CustomerDAO getCustomerDAO() {
return customerDAO;
}
}
DAO spécifique à une entité
public class HibernateCustomerDAO implements CustomerDAO {
@Override
public CustomerTO getCustomerById(int cid) {
Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
return cto;
}
}
Récupération de données: Classe de test
CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());
Données actuelles
Requête et sortie générées par le système Hibernate
Hibernate: sélectionnez customer0_.Id comme Id1_0_0_, customer0_.City comme City2_0_0_, customer0_.Name comme Nom3_0_0_ de CustomerLab31 customer0_ à customer0_.Id =?
CustomerName -> Cody, CustomerCity -> LA
Si vous utilisez Grail's
Framework, il est simple de résoudre l'exception d'initialisation lazy à l'aide du mot clé Lazy
dans un champ spécifique de la classe de domaine.
Par exemple:
class Book {
static belongsTo = [author: Author]
static mapping = {
author lazy: false
}
}
Trouvez des informations supplémentaires ici
Dans mon cas, un session.clear()
égaré causait ce problème.
Cela signifie que vous utilisez JPA ou hibernate dans votre code et que vous effectuez une opération de modification sur la base de données sans effectuer la transaction de logique métier . La solution la plus simple pour cela est de marquer votre morceau de code
Merci.