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

发布于 2025-01-04 23:15:17 字数 3370 浏览 1 评论 0原文

Spring/Hibernate 专家您好!

在这样的时刻,我希望你成为我最好的朋友。我正在使用 hibernate 3.6.1 开发一个项目。使用 session 和 spring 3.0.5 最终 JPA 实现。使用 maven 发布。所以使用 maven 项目分为 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

真的让我很困扰,我所做的唯一临时修复是将这个关联标记为急切的获取,这是不可以的。我认为将 @transactional 放在服务方法之上会在没有打开任何会话时负责打开新会话?

请提供建议并感谢您阅读本文

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蛮可爱 2025-01-11 23:15:17

渲染视图时会发生这种情况吗?
如果是这种情况,您可能需要考虑使用 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>

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>
静水深流 2025-01-11 23:15:17

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

  1. 增加事务(以及会话)的范围,
  2. 要么更改 fetch-type 以
  3. getContentsbyCategoryID() 中手动加载集合(通过调用 size () 例如)
  4. 采用 open-session-in-view 模式(通过 OpenSessionInViewFilter 或 OpenSessionInViewInterceptor)

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文