Hibernate/Spring: Session still closed before retr

2020-04-22 10:47发布

问题:

Hello Spring/Hibernate Gurus!

in a time like this i would like you to be my best friends.I am working on a project using hibernate 3.6.1.Final JPA implementation using session and spring 3.0.5.RELEASE with maven.So with maven project is split in 3 modules model module, service module and webapp module.

where is snippet of the model 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>

I will also put a snippet of the mapping on the relation giving me nightmares right now so i won't dump everything here:

//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;
}

so when assessing contentService.getContentbyCategoryId(longid) it throws

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

really bothers me the only temporal fix i did is to mark this association to eager fetch which am not ok with.I thought puttin @transactional on top of the service method would take care of opening new session when none is opened?

Please advise and thanks for reading this

回答1:

Does this happen when rendering the view? If it is the case you might want to consider using Spring's OpenSessionInViewFilter. This will bind the Session to the thread for the entire processing of the request. If using JPA you can use OpenEntityManagerInViewFilter.

In your 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>


回答2:

The scope of the transaction (and hence the session) is only around the getContentsbyCategoryID() method. Once you return from it, the transaction is committed and the session is closed. Where, exactly, is it throwing the exception? Is it on the list() operation inside getContentsbyCategoryID()? Or is it, in fact, after you have returned from getContentsbyCategoryID() and trying to access the lazy collection somewhere else in the code? In that case, you either have to

  1. increase the scope of the transaction (and, hence, the session)
  2. change fetch-type to eager
  3. manually load the collection in getContentsbyCategoryID() (by calling size() on it, for instance)
  4. Adopt the open-session-in-view pattern (either through the OpenSessionInViewFilter or the OpenSessionInViewInterceptor)