次のような例外があります。
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)
私がmainから以下の行を呼び出そうとすると:
Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());
最初にgetModelByModelGroup(int modelgroupid)
メソッドを実装しました。
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;
}
そして例外がありました。それから友人は私にいつもセッションをテストしてこのエラーを避けるために現在のセッションを取得するように勧めました。だから私はこれをしました:
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;
}
}
それでも同じエラーになります。私はこのエラーについてたくさん読んでいて、いくつかの可能な解決策を見つけました。そのうちの1つはlazyLoadをfalseに設定することでしたが、これを行うことは許可されていませんので、セッションを制御するように提案されました。
ここで間違っているのは、トランザクションをコミットしたときにセッション管理設定がセッションを閉じるように設定されていることです。次のようなものがあるかどうか確認してください。
<property name="current_session_context_class">thread</property>
あなたの設定で。
この問題を克服するためには、セッションファクトリの設定を変更するか、別のセッションを開くことができますが、それらの遅延ロードされたオブジェクトを要求するだけです。しかしここで私が提案するのはgetModelByModelGroup自体でこの遅延コレクションを初期化して呼び出すことです:
Hibernate.initialize(subProcessModel.getElement());
まだアクティブなセッションにいるとき。
そして最後にもう一つ。フレンドリーなアドバイスです。あなたのメソッドにはこのようなものがあります。
for (Model m : modelList)
if (m.getModelType().getId() == 3) {
model = m;
break;
}
このコードは、2行上のクエリステートメントで、タイプidが3のモデルをフィルタリングするためのものです。
もう少し読んで:
あなたが設定しようとすることができます
<property name="hibernate.enable_lazy_load_no_trans">true</property>
hibernate.cfg.xmlまたはpersistence.xmlにあります。
このプロパティに関して覚えておくべき問題はよく説明されています ここ
Springを使用している場合は、クラスに @Transactional のマークを付けて、Springはセッション管理を処理します。
@Transactional
public class MyClass {
...
}
@Transactional
を使用することによって、トランザクション伝播などの多くの重要な側面が自動的に処理されます。この場合、他のトランザクションメソッドが呼び出された場合、そのメソッドには「セッションなし」例外を回避して進行中のトランザクションに参加するオプションがあります。
LazyInitializationException
を処理する最善の方法は、JOIN FETCH
ディレクティブを使用することです。
Query query = session.createQuery(
"from Model m " +
"join fetch m.modelType " +
"where modelGroup.id = :modelGroupId"
);
とにかく、いくつかの答えで示唆されているように、次のアンチパターンを使用しないでください。
時々、 DTO射影 はエンティティをフェッチするよりも良い選択です、そしてこのように、あなたはどんなLazyInitializationException
も手に入れないでしょう。
下の注釈について、1対多の関係で同じエラーが発生していました。
@OneToMany(mappedBy="department", cascade = CascadeType.ALL)
Fetch = FetchType.EAGERを追加した後、以下のように変更されました、それは私のために働きました。
@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
session.getEntityById()
を呼び出したときにセッションが閉じられるため、この例外が発生します。そのため、エンティティをセッションに再アタッチする必要があります。または簡単な解決策は、単にあなたのdefault-lazy="false"
にentity.hbm.xml
を設定することです。あるいは、アノテーションを使用しているならば、あなたのエンティティクラスに@Proxy(lazy=false)
を追加するだけです。
spring data jpa、spring bootを使用している場合は、application.propertiesにこの行を追加できます。
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
私は同じ問題に遭遇しました。これを修正するもう1つの方法は、次のようにクエリを変更してモデルから要素を取得することです。
Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
広い範囲でこのエラーを処理するいくつかの良い答えがここにあります。私はSpring Securityで特定の状況に遭遇しました。それはおそらく最適ではありませんが、迅速な修正を行いました。
ユーザー認証中(ログインして認証に合格した直後)、SimpleUrlAuthenticationSuccessHandlerを拡張するカスタムクラスで特定の権限についてユーザーエンティティをテストしていました。
私のユーザーエンティティはUserDetailsを実装し、 "org.hibernate.LazyInitializationException - プロキシを初期化できませんでした - セッションなし"という例外を投げた遅延ロードされたロールのセットを持ちます。そのSetを "fetch = FetchType.LAZY"から "fetch = FetchType.EAGER"に変更すると、これが修正されました。
JPQLを使用している場合は、JOIN FETCHを使用するのが最も簡単な方法です。 http://www.objectdb.com/Java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
つまり、アクセスしようとしているオブジェクトはロードされていないので、アクセスしようとしているオブジェクトの 結合フェッチ を作成するクエリを作成します。
例えば:
ObjectBがObjectAの外部キーであるObjectAからObjectBを取得しようとしている場合。
クエリ:
SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
異なるユースケースで同じ例外に直面しました。
ユースケース:DTOプロジェクションを使用して、DBからデータを読み取ろうとします。
Solution:loadの代わりにgetメソッドを使用します。
一般的な操作
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;
}
}
永続クラス
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インターフェース
public interface CustomerDAO
{
public CustomerTO getCustomerById(int cid);
}
エンティティ転送オブジェクトクラス
public class CustomerTO {
private int customerId;
private String customerName;
private String city;
//constructors , setters and getters
}
ファクトリークラス
public class DAOFactory {
static CustomerDAO customerDAO;
static {
customerDAO = new HibernateCustomerDAO();
}
public static CustomerDAO getCustomerDAO() {
return customerDAO;
}
}
エンティティ固有の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;
}
}
データの取得:テストクラス
CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());
現在のデータ
Hibernate Systemによって生成されたクエリと出力
Hibernate:customer0_.IdをId1_0_0_として、customer0_.CityをCity2_0_0_として、customer0_.NameをName3_0_0_としてCustomerLab31 customer0_から選択します。ここでcustomer0_.Id =?
CustomerName-> Cody、CustomerCity-> LA
Grail's
フレームワークを使用している場合は、ドメインクラスの特定のフィールドにLazy
キーワードを使用することで、 遅延初期化例外 を簡単に解決できます。
例えば:
class Book {
static belongsTo = [author: Author]
static mapping = {
author lazy: false
}
}
さらに情報を探す ここ
私の場合、session.clear()
の置き忘れがこの問題を引き起こしていました。
つまり、ビジネスロジックトランザクションを実行せずに、コード内でJPAまたは休止状態を使用し、DBに対して変更操作を実行します。これに対するとても簡単な解決策はあなたのコードに@Transactionalという印をつけることです
ありがとう。