【问题标题】:NullPointerException when accessing lazy loaded collection within DAO method在 DAO 方法中访问延迟加载的集合时出现 NullPointerException
【发布时间】:2013-05-20 13:32:17
【问题描述】:

在应用程序中运行多个线程时,我遇到了 Spring 和 Hibernate 的一个奇怪问题。我正在使用 spring 3.2.0.RELEASE 和休眠 4.1.12.Final。问题是对于某些对象,当从数据库中检索它们时,检索成功,但未设置所有映射集合。这是我的回购的一个例子:

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

【问题讨论】:

  • 提供完整的堆栈跟踪(即使它有点冗长)可能有助于更好地解决问题。
  • 您可以发布 Foo 和 CollectionMember 类,或者至少是显示映射注释的位(或者如果您使用 xml 配置,则发布 hbm.xml)
  • 我无法发布完整的堆栈跟踪,除非我手动更改了一堆包名称,这样你就无法知道它来自哪里。如果你真的认为完整的堆栈跟踪会提供更多信息,我会这样做,但我不相信你需要的比你已经拥有的更多,不过我会添加映射。
  • 如果 NPE 在toString() 中是否意味着它是由log.debug(member.getId()); 抛出的?如果不是,是否有可能在处理不同的异常期间引发此异常?此外,有关您的事务管理配置的信息也可能很有趣 - 您是否使用 @EnableTransactionalManagement
  • 请放完整的堆栈跟踪

标签: java multithreading spring hibernate lazy-loading


【解决方案1】:

我认为您应该使用第三个连接表,并使用 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】:

    问题是@NotFound(action = NotFoundAction.IGNORE)

    如果没有找到集合记录,这将创建一个返回的对象 null 而不是空集合。删除并测试它

    【讨论】:

      【解决方案3】:

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

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

      这可能有一个 Hibernate 配置。

      【讨论】:

        【解决方案4】:

        试试下面的。

        Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id);
        
        Hibernate.initialize(instance.getCollectionMembers())
        
        for (CollectionMember member : instance.getCollectionMembers()) {
        
                log.debug(member.getId());      
        }
        
        return instance;
        

        【讨论】:

          【解决方案5】:

          除非另有说明,否则 Hibernate 默认将所有 *ToMany 集合映射标记为惰性。一个线程不可能加载对象并且无法在一个 @Transactional 方法中读取关联的惰性集合。

          在您的情况下,什么是多线程?您是手动生成线程,还是 Web 应用程序?如何设置会话界限?

          顺便说一句,如果你真的想解决这个问题,我建议你发布堆栈跟踪.. 替换你的商业包名称可能只需要几秒钟,以防万一。

          【讨论】:

            【解决方案6】:

            在spring环境中使用hibernate时,不建议直接通过session factory获取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;
                    }
                });
            

            【讨论】:

              猜你喜欢
              • 2014-03-14
              • 1970-01-01
              • 2011-10-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-12-21
              相关资源
              最近更新 更多