2012-03-08 133 views
0

我有这种方法无状态会话bean:'没有会话或会话关闭' 与JPA 2和EJB 3.1

@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豆)我收到异常:

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

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

所以问题是:这段代码有什么问题,以及如何以懒惰的方式加载类属性。

回答

0

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

+0

我应该使用哪一种'TransactionAttributeType'?Javadocs说:_如果没有指定TransactionAttribute注解,并且bean使用容器管理的事务分界,则假定REQUIRED事务属性的语义。因此,我认为没有'TransactionAttribute'注释的注释方法是可以的。 – pWoz 2012-03-09 07:55:53

+0

Bozho能解释吗? – pWoz 2012-03-15 15:03:17

+0

否:)可能会有另一个子句提到只读事务。但是添加一个TransactionAttribute的工作? – Bozho 2012-03-15 16:05:52

1

According to the JPA 2.0 specification session should stay alive for a whole transaction. In this situation transaction (in my opinion) last for a whole method call (there is no additional transaction attributes on class or method).

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

我有同样的问题导出会话bean作为Web服务。详细了解问题here

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

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的兑现,调用的第一个业务方法确定事务上下文和行为。现在,我无法确定在这种情况下发生了什么,因为我没有看到其他源代码,但这是一个非常常见的错误。

+0

我正在通过一个接口进入这个方法。 'getUserCharacters(int userId)'是这个类中唯一的方法,这个类是无状态的会话bean。 – pWoz 2012-05-08 19:25:40

+0

我还做了一些调查,并且在'return user.getCharacters()'之前添加'user.getCharacters()。size()'时,一切正常。所以,我认为,这个例子中的return语句不会初始化'User'类中的'characters'集合。我必须调用这个集合的任何方法来强制Hibernate初始化列表。但是这个例外很具误导性。 – pWoz 2012-05-08 19:33:32

0

2013年7月24日19:47:07387错误: - 未能懒洋洋地初始化角色的集合:com.xxxx.domain.DenialMaster.denialDetails,没有会话或会话关闭空

我的配置:

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

SOLUTION: 在类ServiceImpl添加@Transactional(readOnly的=真) 实施例:

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