我有这种方法的无状态会话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规范,会话应该为整个事务保持活跃状态。在这种情况下,事务(在我看来)最后是一个完整的方法调用(类或方法上没有额外的事务属性)。
所以问题是:这段代码出了什么问题,怎么能以懒惰的方式加载类属性。
答案 0 :(得分:1)
根据JPA 2.0规范,会话应该为整个事务保持活跃状态。在这种情况下,事务(在我看来)最后是一个完整的方法调用(类或方法上没有额外的事务属性)。
这是真的,但它不包括返回对象的序列化。
我将会话bean导出为Web服务时遇到了同样的问题。详细了解该问题here。
如果您有类似用途,强烈建议您返回普通对象而不是实体。您可以像我们一样使用一些bean映射框架。我们使用了Dozer。
答案 1 :(得分:0)
您需要指定@TransactionAttribute
,以便您的方法是事务性的。否则,为每个实体管理器操作启动只读事务和新的基础会话。这与惰性集合相结合,意味着当您获取集合时,原始会话将被关闭。
答案 2 :(得分:0)
@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;
}