DAO方法中访问延迟加载集合时出现NullPointerException

9

在应用程序中运行多个线程时,我遇到了Spring和Hibernate的一个奇怪问题。我使用的是spring 3.2.0.RELEASE和hibernate 4.1.12.Final。问题是对于某些对象,当它们从数据库检索时,检索成功,但是所有映射的集合都没有被设置。以下是我的repo示例:

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

3
提供完整的堆栈跟踪信息(即使有点冗长)可能有助于更好地解决问题。 - Subhas
你能否发布Foo和CollectionMember类,或者至少展示映射注解的部分代码(如果你使用xml配置,则是hbm.xml)? - Gareth Davis
除非我手动更改一堆包名称,否则我无法发布完整的堆栈跟踪,以便您无法确定它来自哪里。如果您真的认为完整的堆栈跟踪会提供更多信息,那么我会这样做,但我并不认为您需要更多的信息,因此我将添加映射。 - Rockenstein
如果 NPE 在 toString() 中,这是否意味着它由 log.debug(member.getId()); 抛出?如果不是,那么在处理不同异常期间抛出此异常是否可能?另外,关于您的事务管理配置的信息也可能很有趣 - 您是否使用 @EnableTransactionalManagement - Pavel Horal
请提供完整的堆栈跟踪信息。 - Nassim MOUALEK
6个回答

2
我认为你应该使用第三个连接表,并使用条件加载集合。
    @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

问题出在 @NotFound(action = NotFoundAction.IGNORE)

如果没有找到集合记录,则会创建一个返回对象为null而不是空集合。移除并测试它。


0

在Spring环境中使用Hibernate时,不建议直接通过Session工厂获取Session,因为这可能会导致Session泄漏。请尝试使用以下模式进行替换:

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

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;

0
Hibernate默认将所有*ToMany集合映射标记为lazy,除非另有说明。在一个@Transactional方法中,一个线程加载对象但无法读取相关的lazy集合是不可能的。
在你的情况下,什么是多线程?你是手动生成线程还是Web应用程序?你如何设置会话边界?
顺便说一句,如果你真的想解决这个问题,我建议你发布堆栈跟踪...如果这是一个问题,替换商业包名称可能只需要几秒钟的时间。

0

多线程在哪里发挥作用?您是如何确保单个线程始终使用相同的Hibernate会话的?

在我理解的Hibernate和延迟加载的方式中,当遍历集合时,Hibernate将查找当前会话。在多线程环境下,该会话可能已经被另一个线程使用过了。我不确定@Transactional是否足以保留您的同一Hibernate Session。

这可能有一个适用于此的Hibernate配置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接