Hibernate / Spring: сессия все еще закрыта перед извлечением дочерней коллекции, даже если @Transactional для метода Service - PullRequest
1 голос
/ 14 февраля 2012

Привет, Весна / Спящий Гуру!

в такое время я бы хотел, чтобы вы были моими лучшими друзьями. Я работаю над проектом, использующим hibernate 3.6.1. Окончательная реализация JPA с использованием сессии и весны 3.0.5. ВЫПУСК с maven. Так что с проектом maven разделенный на 3 модуля модуль, сервисный модуль и модуль веб-приложения.

где фрагмент приложения контекста

       <!-- 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 поверх метода service позаботится об открытии нового сеанса, когда ни один из них не открыт?

Пожалуйста, сообщите и спасибо за прочтение

Ответы [ 2 ]

1 голос
/ 14 февраля 2012

Объем транзакции (и, следовательно, сеанса) только вокруг метода getContentsbyCategoryID().Как только вы вернетесь из нее, транзакция будет зафиксирована, и сессия будет закрыта.Где именно это исключение?Это на list() операции внутри getContentsbyCategoryID()?Или это на самом деле после того, как вы вернулись из getContentsbyCategoryID() и попытались получить доступ к ленивой коллекции где-то еще в коде?В этом случае вам придется либо

  1. увеличить объем транзакции (и, следовательно, сеанса)
  2. изменить тип выборки на eager
  3. вручнуюзагрузить коллекцию в getContentsbyCategoryID() (например, вызвав size())
  4. Принять шаблон open-session-in-view (либо через OpenSessionInViewFilter, либо через OpenSessionInViewInterceptor)
1 голос
/ 14 февраля 2012

Это происходит при рендеринге вида? Если это так, вы можете рассмотреть возможность использования Spring * OpenSessionInViewFilter. Это свяжет сессию с потоком для всей обработки запроса. При использовании 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>
...