Ich bekomme die folgende Ausnahme:
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)
wenn ich versuche, von main aus die folgenden leitungen anzurufen:
Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());
Ich habe die getModelByModelGroup(int modelgroupid)
Methode zuerst so implementiert:
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;
}
und bekam die Ausnahme. Dann schlug mir ein Freund vor, die Sitzung immer zu testen und die aktuelle Sitzung abzurufen, um diesen Fehler zu vermeiden. Also habe ich das gemacht:
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;
}
}
bekomme aber trotzdem den gleichen fehler. Ich habe viel für diesen Fehler gelesen und einige mögliche Lösungen gefunden. Einer von ihnen war, LazyLoad auf false zu setzen, aber ich darf das nicht, deshalb wurde mir vorgeschlagen, die Sitzung zu kontrollieren
Was hier falsch ist, ist, dass Ihre Sitzungsverwaltungskonfiguration so eingestellt ist, dass die Sitzung geschlossen wird, wenn Sie die Transaktion festschreiben. Prüfen Sie, ob Sie etwas haben:
<property name="current_session_context_class">thread</property>
in deiner Konfiguration.
Um dieses Problem zu überwinden, können Sie die Konfiguration der Session Factory ändern oder eine andere Session öffnen und erst danach nach den Lazy-Load-Objekten fragen. Ich würde jedoch vorschlagen, diese Lazy-Collection in getModelByModelGroup selbst zu initialisieren und Folgendes zu nennen:
Hibernate.initialize(subProcessModel.getElement());
wenn Sie sich noch in einer aktiven Sitzung befinden.
Und noch ein letztes. Ein freundlicher Rat. Sie haben so etwas in Ihrer Methode:
for (Model m : modelList)
if (m.getModelType().getId() == 3) {
model = m;
break;
}
Bitte installieren Sie diesen Code, indem Sie die Modelle mit der Typ-ID 3 in der Abfrageanweisung nur ein paar Zeilen darüber filtern.
Noch etwas lesen:
Sie können versuchen zu setzen
<property name="hibernate.enable_lazy_load_no_trans">true</property>
in hibernate.cfg.xml oder persistence.xml
Das Problem, das bei dieser Eigenschaft zu beachten ist, ist gut erklärt hier
Wenn Sie die Klasse mit Spring als @Transactional markieren, übernimmt Spring die Sitzungsverwaltung.
@Transactional
public class My Class {
...
}
Durch die Verwendung von @Transactional
werden viele wichtige Aspekte wie Transaktionsweitergabe automatisch behandelt. Wenn in diesem Fall eine andere Transaktionsmethode aufgerufen wird, hat die Methode die Option, der laufenden Transaktion beizutreten, wobei die Ausnahme "keine Sitzung" vermieden wird.
Der beste Weg, die LazyInitializationException
zu behandeln, ist die JOIN FETCH
-Direktive:
Query query = session.createQuery(
"from Model m " +
"join fetch m.modelType " +
"where modelGroup.id = :modelGroupId"
);
Verwenden Sie auf keinen Fall die folgenden Anti-Patterns, wie in einigen Antworten vorgeschlagen:
Manchmal ist eine DTO-Projektion eine bessere Wahl als das Abrufen von Entitäten, und auf diese Weise erhalten Sie keine LazyInitializationException
.
Ich habe den gleichen Fehler für eine zu viele Beziehungen für die unten stehende Anmerkung erhalten.
@OneToMany(mappedBy="department", cascade = CascadeType.ALL)
Nach dem Hinzufügen von fetch = FetchType.EAGER wie folgt geändert, hat es für mich funktioniert.
@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Diese Ausnahme wird beim Aufruf von session.getEntityById()
geschlossen, und die Sitzung wird geschlossen. Sie müssen also die Entität erneut an die Sitzung anhängen. Oder Die einfache Lösung ist, default-lazy="false"
einfach in Ihren entity.hbm.xml
zu konfigurieren, oder wenn Sie Anmerkungen verwenden, fügen Sie @Proxy(lazy=false)
Ihrer Entitätsklasse hinzu.
wenn Sie Spring Data jpa verwenden, können Sie diese Zeile in application.properties hinzufügen
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Ich bin auf das gleiche Problem gestoßen. Ich denke, eine andere Möglichkeit, dies zu beheben, besteht darin, dass Sie die Abfrage so ändern können, dass Sie Ihr Element vom Modell abrufen:
Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
Es gibt mehrere gute Antworten, die diesen Fehler in einem breiten Umfang behandeln. Ich bin auf eine spezielle Situation mit Spring Security gestoßen, die eine schnelle, wenn auch wahrscheinlich nicht optimale Lösung hatte.
Während der Benutzerautorisierung (unmittelbar nach dem Anmelden und dem Bestehen der Authentifizierung) habe ich eine Benutzerentität für eine bestimmte Berechtigung in einer benutzerdefinierten Klasse getestet, die SimpleUrlAuthenticationSuccessHandler erweitert.
Meine Benutzerentität implementiert UserDetails und verfügt über eine Gruppe von faul geladenen Rollen, die die Ausnahme "org.hibernate.LazyInitializationException - Proxy konnte keine Sitzung initialisieren" ausgelöst hat. Das Ändern dieses Satzes von "fetch = FetchType.LAZY" in "fetch = FetchType.EAGER" hat dies für mich behoben.
Wenn Sie JPQL verwenden, ist JOIN FETCH der einfachste Weg: http://www.objectdb.com/Java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
Das bedeutet, dass das Objekt, auf das Sie zugreifen möchten, nicht geladen ist. Schreiben Sie daher eine Abfrage, die einen Join-Abruf des Objekts abfragt, auf das Sie zugreifen möchten.
Z.B:
Wenn Sie versuchen, ObjectB von ObjectA abzurufen, wobei ObjectB ein Fremdschlüssel in ObjectA ist.
Abfrage:
SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
Konfrontiert die gleiche Ausnahme in verschiedenen Anwendungsfällen.
Anwendungsfall: Versuchen Sie, Daten mit DTO-Projektion aus der Datenbank zu lesen.
Lösung: Verwenden Sie get anstelle von Laden .
Generische Operation
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;
}
}
Persistenzklasse
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
}
CustomerDAO-Schnittstelle
public interface CustomerDAO
{
public CustomerTO getCustomerById(int cid);
}
Entitätsübertragungsobjektklasse
public class CustomerTO {
private int customerId;
private String customerName;
private String city;
//constructors , setters and getters
}
Fabrikklasse
public class DAOFactory {
static CustomerDAO customerDAO;
static {
customerDAO = new HibernateCustomerDAO();
}
public static CustomerDAO getCustomerDAO() {
return customerDAO;
}
}
Mandantenspezifisches DAO
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;
}
}
Daten abrufen: Testklasse
CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());
Vorhandene Daten
Abfrage und Ausgabe von Hibernate System
Ruhezustand: Wählen Sie customer0_.Id als Id1_0_0_, customer0_.City als City2_0_0_, customer0_.Name als Name3_0_0_ aus CustomerLab31 customer0_, wobei customer0_.Id =?
Kundenname -> Cody, Kundenstadt -> LA
Wenn Sie Grail's
Framework verwenden, können Sie einfach die lazy-Initialisierungsausnahme mit dem Schlüssel Lazy
für ein bestimmtes Feld in der Domänenklasse auflösen.
Zum Beispiel:
class Book {
static belongsTo = [author: Author]
static mapping = {
author lazy: false
}
}
Weitere Informationen hier
In meinem Fall verursachte eine falsch platzierte session.clear()
dieses Problem.
Dies bedeutet, dass Sie in Ihrem Code JPA oder einen Ruhezustand verwenden und eine Änderungsoperation für die DB durchführen, ohne die Geschäftslogik-Transaktion auszuführen. Dies ist eine einfache Lösung, um Ihren Code @Transactional zu markieren
Vielen Dank.