Hibernate / Spring:在使用@Transactional on Service方法检索子集合之前,Session仍然关闭

时间:2012-02-14 10:01:24

标签: hibernate spring session spring-mvc lazy-loading

Hello Spring / Hibernate Guru!

在这样的时间里,我希望你成为我最好的朋友。我正在使用hibernate 3.6.1.Final JPA实现使用session和spring 3.0.5.RELEASE with maven.So with maven project is a project分为3个模块模型模块,服务模块和webapp模块。

其中是模型applicationContext的片段

       <!-- Transaction Management    -->
<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- Model -->
<bean id="genericDAO" class="com.blabla.blabla.model.dao.hibernate.HibernateGenericDAOImpl" abstract="true">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>



<bean id="content" class="com.blabla.blabla.model.ContentImpl" scope="prototype" />

<bean id="contentDAO" class="com.blabla.blabla.model.dao.hibernate.ContentDAOImpl" parent="genericDAO">
    <constructor-arg>
        <value>com.blabla.blabla.model.ContentImpl</value>
    </constructor-arg>
</bean>

我还会在关系上放一个映射片段,让我现在做恶梦,所以我不会把所有东西都放在这里:

//ContentImpl
@Id
@Column(name = "CONTENT_ID")
private Long ID;
@Version
@Column(name = "OBJ_VERSION")
private int version = 0;
//.... other properties

@OneToMany(targetEntity = ContentImageImpl.class,cascade = {CascadeType.ALL},orphanRemoval = true)
@JoinColumn(name = "CONTENT_ID", referencedColumnName = "CONTENT_ID")
private Set<ContentImage> images = new HashSet<ContentImage>();

//ContentImageImpl
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "CONTENT_IMAGE_ID")
private Long ID;

@Column(name = "PATH")
private String path;

@Column(name ="IS_DEFAULT")
private Boolean isDefault;
//no other relations


//ContentServiceImpl
 public class ContentServiceImpl implements ContentService {
//wired from application context
private ContentDAO contentDao;
private static Logger logger = Logger.getLogger(ContentServiceImpl.class);

@Transactional
public List<Content> getContentsbyCategoryID(Long categoryId) {
    return getContentDao().getbyCategoryID(categoryId);
}


@Transactional
public List<Content> getContentsWithImagesbyCategoryID(Long categoryId) {
//return getContentDao().getbyCategoryID(categoryId);
    return getContentDao().getWithImagesbyCategoryID(categoryId);



//ContentDAOImpl
 public List<Content> getbyCategoryID(Long category_id) {
    Category cat = modelManager.createCategory();
    cat.setID(category_id);
    logger.info("calling  getbyCategoryID");
    logger.debug(category_id);
    List<Content> session =  this.getSessionFactory().getCurrentSession().createCriteria(this.getPersistentClass())
            .add(Restrictions.eq("category",(CategoryImpl)cat))
            .setProjection(Projections.distinct(Projections.id()))
            .list();
    logger.debug(session);
    return session;
}

所以在评估contentService.getContentbyCategoryId(longid)时 它抛出

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of  role: com.bla.bla.model.ContentImpl.contentFiles, no session or session was closed

真的让我感到烦恼的是,我所做的唯一的时间修复是将此关联标记为急切的提取,这是不行的。我认为puttin @transactional在服务方法之上会在没有打开时打开新会话吗? / p>

请告知并感谢您阅读本文

2 个答案:

答案 0 :(得分:1)

渲染视图时会发生这种情况吗? 如果是这种情况,您可能需要考虑使用Spring的OpenSessionInViewFilter。这会将Session绑定到线程以进行整个请求处理。如果使用JPA,您可以使用OpenEntityManagerInViewFilter

在你的web.xml中:

<filter>
    <filter-name>OpenSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>sessionFactoryBeanName</param-name>
        <param-value>sessionFactory</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>OpenSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

答案 1 :(得分:1)

事务的范围(以及会话)仅围绕getContentsbyCategoryID()方法。从中返回后,将提交事务并关闭会话。究竟在哪里抛出异常?它位于list()内的getContentsbyCategoryID()操作吗?或者,事实上,在您从getContentsbyCategoryID()返回并尝试在代码中的其他位置访问惰性集合之后?在这种情况下,你要么

  1. 增加交易范围(因此,会话)
  2. 将fetch-type更改为eager
  3. getContentsbyCategoryID()中手动加载集合(例如,通过调用size()
  4. 采用open-session-in-view模式(通过OpenSessionInViewFilter或OpenSessionInViewInterceptor)