在应用程序中运行多个线程时,我遇到了Spring和Hibernate的奇怪问题。我正在使用spring 3.2.0.RELEASE和hibernate 4.1.12.Final。问题是,对于某些对象,当从db检索它们时,检索成功,但未设置所有映射的集合。这是我的回购的一个例子:
@Repository("fooRepository")
public class FooRepository {
private static final Logger log = Logger.getLogger(FooRepository.class);
@Autowired
private SessionFactory sessionFactory;
@Override
@Transactional
public Foo retrieve(final Long id) {
Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id);
for (CollectionMember member : instance.getCollectionMembers()) {
log.debug(member.getId());
}
return instance;
}
对于某些对象,此方法在尝试访问CollectionMember列表时始终会引发以下错误:
Caused by: java.lang.NullPointerException
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:501)
问题是session.get()调用为所有延迟加载的集合创建PersistentBag对象,但从不设置内容。仅在启用多线程时才会发生这种情况。任何人都可以解释为什么会这样吗?
编辑: 这是foo类的相关部分:
@Entity
@Table(name = "FOO")
@XmlRootElement(name = "foo")
public class Foo {
@EmbeddedId
private FooPK id;
@OneToMany
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumns({
@JoinColumn(name = "COLLECTION_ID", referencedColumnName = "COLLECTION_ID"),
@JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")})
private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>();
CollectionMember类:
@Entity
@Table(name = "COLLECTION_MEMBER")
@XmlRootElement(name = "collectionMember")
public class CollectionMember {
@EmbeddedId
private CollectionMemberPK primaryKey;
@ManyToOne
@JoinColumn(name = "COLL_CODE")
private CollectionCode collectionCode;
答案 0 :(得分:2)
问题是@NotFound(action = NotFoundAction.IGNORE)
如果没有找到集合记录,这将创建一个返回的对象null而不是一个空集合。删除并测试它
答案 1 :(得分:2)
我认为您应该使用第三个连接表,并使用Criteria来加载集合。
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "table", joinColumns = {@JoinColumn(name = "COLLECTION_ID", nullable = false)}, inverseJoinColumns = { @JoinColumn(name = "FOO_ID", nullable = true) })
private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>();
@ManyToOne(mappedBy="collectionMembers")
private CollectionCode collectionCode;
答案 2 :(得分:0)
多线程在哪里发挥作用?如何确保单个线程始终使用相同的休眠会话?
我理解hibernate和延迟加载的方式,当你遍历集合时,Hibernate会查看CurrentSession。在多线程环境中,该会话可能已被另一个线程使用。我不确定@Transactional是否足以保留相同的Hibernate会话。
可能有一个Hibernate配置。
答案 3 :(得分:0)
请尝试以下操作。
Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id);
Hibernate.initialize(instance.getCollectionMembers())
for (CollectionMember member : instance.getCollectionMembers()) {
log.debug(member.getId());
}
return instance;
答案 4 :(得分:0)
除非另有说明,否则Hibernate默认将所有* ToMany集合映射标记为延迟。一个线程无法加载对象,也无法在一个@Transactional方法中读取关联的延迟集合。
您的情况下多线程是什么?你手动生成线程,还是web应用程序?你如何设置会话界限?
顺便说一下,如果你真的想解决问题,我建议你发布堆栈跟踪...如果需要关注,可能只需几秒钟就可以替换你的商业软件包名称。答案 5 :(得分:0)
在spring环境下使用hibernate时,不建议直接通过会话工厂获取会话,是否会导致会话泄露。 尝试使用以下模式替换:
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Foo instance = (Foo) session.get(Foo.class, id);
//whatever logic goes here
return instance;
}
});