使用JPA 2和EJB 3.1“没有关闭会话或会话”

时间:2012-03-08 20:50:17

标签: hibernate ejb-3.0 jpa-2.0

我有这种方法的无状态会话bean:

@Override
public List<Character> getUserCharacters(int userId) {
    User user = em.find(User.class, userId);
    if(user != null)
        return user.getCharacters();
    else
        return null;
}

其中User类如果以这种方式定义:

@Entity
@Table(name="Users")
public class User implements Serializable{

    /**  */

private static final long serialVersionUID = 8119486011976039947L;

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;

@ManyToMany(fetch=FetchType.EAGER)
private Set<Role> roles;

@OneToMany(mappedBy="owner",fetch=FetchType.LAZY)
private List<com.AandP.game.model.characters.Character> characters;

public User() {
    creationDate = new Date();
    roles = new HashSet<Role>();
    }
}

但是当我执行此方法时(来自我的@Named bean),我收到异常:

 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.AandP.game.model.User.characters, no session or session was closed

根据JPA 2.0规范,会话应该为整个事务保持活跃状态​​。在这种情况下,事务(在我看来)最后是一个完整的方法调用(类或方法上没有额外的事务属性)。

所以问题是:这段代码出了什么问题,怎么能以懒惰的方式加载类属性。

4 个答案:

答案 0 :(得分:1)

  

根据JPA 2.0规范,会话应该为整个事务保持活跃状态​​。在这种情况下,事务(在我看来)最后是一个完整的方法调用(类或方法上没有额外的事务属性)。

这是真的,但它不包括返回对象的序列化。

我将会话bean导出为Web服务时遇到了同样的问题。详细了解该问题here

如果您有类似用途,强烈建议您返回普通对象而不是实体。您可以像我们一样使用一些bean映射框架。我们使用了Dozer

答案 1 :(得分:0)

您需要指定@TransactionAttribute,以便您的方法是事务性的。否则,为每个实体管理器操作启动只读事务和新的基础会话。这与惰性集合相结合,意味着当您获取集合时,原始会话将被关闭。

答案 2 :(得分:0)

pwoz:Bozho说的是正确的。我要添加的一个警告是,如果您没有通过接口(本地或远程)进入该方法,那么注释无关紧要。如果这是你的bean的样子:

@Stateless
public class MyUserBean implements UserBeanLocal {
...

  public void doSomeStuffWithUserCharactersById(int id) {
     List<Character>userCharacters = getUserCharacters(id);
  }

  @Override
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public List<Character> getUserCharacters(int userId) {
   User user = em.find(User.class, userId);
   if(user != null)
     return user.getCharacters();
    else
     return null;
  }
}

该属性不会受到尊重,因为在同一个会话bean中,调用的第一个业务方法确定了事务上下文和行为。现在,我无法确定是否在这个实例中发生了什么,因为我没有看到你的其他源代码,但这是一个非常常见的错误。

答案 3 :(得分:0)

2013-07-24 19:47:07,387错误: - 未能懒惰地初始化角色集合:com.xxxx.domain.DenialMaster.denialDetails,没有会话或会话被关闭null

我的配置:

<tx:annotation-driven transaction-manager="transactionManagerDL" />

<bean id="transactionManagerDL" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emfDL" />
</bean>

<bean id="emfDL" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSourceDL" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="mappingResources">
        <value>META-INF/orm.xml</value>
    </property>
    <property name="packagesToScan" value="${dl.entity.packages}" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${catalog.org.hibernate.dialect}</prop>
            <prop key="hibernate.max_fetch_depth">${catalog.org.hibernate.jdbc.max.fetchdepth}</prop>
            <prop key="hibernate.jdbc.fetch_size">${catalog.org.hibernate.jdbc.fetchsize}</prop>
            <prop key="hibernate.jdbc.batch_size">${catalog.org.hibernate.jdbc.batchsize}</prop>
            <prop key="hibernate.show_sql">${catalog.org.hibernate.showsql}</prop>
            <prop key="hibernate.connection.autocommit">${catalog.org.hibernate.connection.autocommit}
            </prop>

        </props>
    </property>
</bean>

解: 在您的Class ServiceImpl中添加@Transactional(readOnly = true) 示例:

@Override
@Transactional(readOnly = true)
public YourBean findById(long id) throws Exception {
    return yourDAO.findOne(id);
}

和@LazyCollection(LazyCollectionOption.FALSE):

  @OneToMany(fetch=FetchType.LAZY)
  @JoinColumn(name = "idMaster")
  @LazyCollection(LazyCollectionOption.FALSE)
  public List<DenialDetail> getDenialDetails() {
    return denialDetails;
  }