Spring(3.2.2)に沿ってJPA(永続プロバイダーとしてHibernate 4.3.3を使用)を使用していますが、すべてのフィールドは正常に読み込まれていますが、コレクションにアクセスしようとするとエラーがスローされます。
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.br.common.catalog.entity.Category.allParentCategoryXrefs, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.Java:572)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.Java:212)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.Java:551)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.Java:140)
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.Java:526)
at Java.lang.String.valueOf(String.Java:2827)
at Java.io.PrintStream.println(PrintStream.Java:771)
at test.Test.main(Test.Java:30)
これをデバッグすると、エンティティクラスで定義されているすべてのコレクションでエラーが発生していました-com.Sun.jdi.InvocationException occurred invoking method.
Collection.size()とHibernate.initialize()を使用してみましたが、どれも機能しませんでした。インターネットで検索すると、Persitenceを拡張すると問題が解決されることがわかりました。
@PersistenceContext(type=PersistenceContextType.EXTENDED)
protected EntityManager em;
これはうまくいきましたが、これによってemは常に開かれたままであることがわかりました、今では春はこれを処理しません。 Springを使用してこの問題を解決する方法はありますか。ヘルプは大歓迎です。
私のエンティティは-
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name="CATEGORY")
public class Category implements Serializable {
@Id
@GeneratedValue(generator= "CategoryId")
@Column(name = "CATEGORY_ID")
protected Long id;
@ManyToOne(targetEntity = Category.class)
@JoinColumn(name = "DEFAULT_PARENT_CATEGORY_ID")
@Index(name="CATEGORY_PARENT_INDEX", columnNames={"DEFAULT_PARENT_CATEGORY_ID"})
protected Category defaultParentCategory;
@OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.category")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region="test")
@OrderBy(value="displayOrder")
@BatchSize(size = 50)
protected List<Categoryref> childCategoryRefs = new ArrayList<Categoryref>(10);
@OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.subCategory",fetch=FetchType.LAZY)
@Cascade(value={org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.PERSIST})
@OrderBy(value="displayOrder")
@BatchSize(size = 50)
protected List<Categoryref> parentCategoryRefs = new ArrayList<Categoryref>(10);
}
@Entity
@Polymorphism(type = PolymorphismType.EXPLICIT)
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "CATEGORY_REF")
public class Categoryref implements Serializable {
/** The category id. */
@EmbeddedId
CategoryrefPK categoryrefPK = new CategoryrefPK();
public CategoryrefPK getCategoryrefPK() {
return categoryrefPK;
}
public void setCategoryrefPK(final CategoryrefPK categoryrefPK) {
this.categoryrefPK = categoryrefPK;
}
}
@Embeddable
public class CategoryrefPK implements Serializable {
@ManyToOne(targetEntity = Category.class, optional=false)
@JoinColumn(name = "CATEGORY_ID")
protected Category category = new Category();
@ManyToOne(targetEntity = Category.class, optional=false)
@JoinColumn(name = "SUB_CATEGORY_ID")
protected Category subCategory = new Category();
}
私のXml構成は次のとおりです-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.br" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
....
</bean>
<!-- this is also used we can used this also -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="abc" />
<property name="packagesToScan" value="com.br.common.*" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
</beans>
Persitence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://Java.Sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://Java.Sun.com/xml/ns/persistence http://Java.Sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="abc">
transaction-type="RESOURCE_LOCAL">
<mapping-file>META-INF/category.orm.xml</mapping-file>
<class>com.br.common.Category</class>
<class>com.br.common.Categoryref</class>
<class>com.br.common.CategoryrefPK</class>
<properties>
<property name="javax.persistence.jdbc.user" value="user"
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test>
<property name="javax.persistence.jdbc.password" value="...">
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.transaction.flush_before_completion"
value="false" />
<property name="hibernate.connection.autocommit" value="true" />
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.generate_statistics" value="false" />
<property name="hibernate.archive.autodetection" value="false" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
</persistence>
これは私のdaoです。サービスレイヤーを介してdaoを呼び出しています
@Repository("categoryDaoImpl")
public class CategoryDaoImpl implements ICategoryDAO {
@PersistenceContext
protected EntityManager em;
public Category save(Category category) {
Category category2= em.merge(category);
em.flush();
return category2;
}
public Category readCategoryById(Long categoryId) {
return em.find(Category.class, categoryId);
}
}
サービス層
@Service("blCatalogService")
@Transactional(propagation=Propagation.REQUIRED)
public class CatalogServiceImpl implements ICatalogService {
@Resource(name="categoryDaoImpl")
protected ICategoryDAO categoryDao;
@Transactional
public Product saveProduct(Product product) {
return productDao.save(product);
}
public Category findCategoryById(Long categoryId) {
return categoryDao.readCategoryById(categoryId);
}
}
これがメインです
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext-persistence.xml");
ICatalogService serviceCategory= (ICatalogService) context
.getBean("blCatalogService");
Category parentCategory=serviceCategory.findCategoryById(2l);
System.out.println(parentCategory.getAllParentCategoryrefs());//here error is coming while accessing collection
}
}
問題は、このメソッド呼び出しが以下を返すときです:
_Category parentCategory=serviceCategory.findCategoryById(2l);
_
ここでは、もはや_@Transactional
_コンテキストにありません。これは、parentCategoryにリンクされたセッションが閉じられたことを意味します。閉じたセッションにリンクされているコレクションにアクセスしようとすると、_No Session
_エラーが発生します。
注目すべきことの1つは、mainメソッドがSpring Beanの外部で実行され、永続コンテキストの概念がないことです。
解決策は、parentCategory.getAllParentCategoryrefs()
をトランザクションコンテキストから呼び出すことです。これは、アプリケーションのメインメソッドになることはありません。
次に、parentCategoryを新しい永続コンテキストに再アタッチし、ゲッターを呼び出します。
たとえば、parentCategoryを同じサービスのトランザクションメソッドに渡すようにしてください。
_serviceCategory.nowItWorks(parentCategory);
_
サービスのメソッドがトランザクションの場合:
_@Transactional(readOnly=true)
public void nowItWorks(Category category) {
dao.nowItWorks(category);
}
_
そしてDAOでは:
_public void nowItWorks(Category category) {
Category reattached = em.merge(category);
System.out.println("It works: " + reattached.getAllParentCategoryrefs());
}
_
@Zeusが言ったように、これは何度も何度も回答されてきました。トランザクションはサービス呼び出しで開始および終了するため、テストクラスでこの問題が発生しています。
Category parentCategory=serviceCategory.findCategoryById(2l);
Hibernateのドキュメントから、遅延読み込みはHibernateセッション内でのみ機能することを思い出してください(この場合、hibernateセッションはサービス呼び出しで開始および終了します)。コレクションを初期化するために(単純に)休止状態セッションに再接続することはできません。
「春」にそれを解決したいとき、あなたが何を意味するのか正確にはわかりません。これはSpringの問題ではないためです。基本的に、これを解決する2つの方法は、親をロードするhibernateセッション内でコレクションをロードするか、元のhibernateセッションの外部で別のフェッチを実行することです。
ドメインセットコレクションで@Fetch(FetchMode.SELECT)
および@LazyCollection(LazyCollectionOption.FALSE)
を使用すると、動作します