Grails/Hibernate:版本控制上的空指针异常

Posted

技术标签:

【中文标题】Grails/Hibernate:版本控制上的空指针异常【英文标题】:Grails/Hibernate: Null Pointer Exception on versioning 【发布时间】:2011-04-10 12:07:38 【问题描述】:

在 Grails 中使用遗留代码库。在某些情况下(我们不清楚具体是什么),我们会得到一个神秘的 NPE,堆栈跟踪如下,同时执行 findBy。

到目前为止,我们有点受阻;这出现在 Hibernate 的几个论坛中,但响应似乎归结为“您的架构有问题”。如果能找到一些额外的细节来帮助我们追踪问题,那就太好了。

更新

感谢您的回答。是的,很明显,在这个 NPE 发生时,versionnull。麻烦的是,当我们在代码中查看时,它不是 null

我们开始怀疑存在线程问题。

Caused by: java.lang.NullPointerException
    at org.hibernate.type.LongType.next(LongType.java:79)
    at org.hibernate.engine.Versioning.increment(Versioning.java:131)
    at org.hibernate.event.def.DefaultFlushEntityEventListener.getNextVersion(DefaultFlushEntityEventListener.java:387)
    at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:279)
    at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:151)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:49)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
    at org.codehaus.groovy.grails.plugins.quartz.listeners.SessionBinderJobListener.jobWasExecuted(SessionBinderJobListener.java:58)
    at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1910)

【问题讨论】:

当然,我不知道所有的细节,但是......线程问题怎么能抵消一些东西?完全排除具有空版本列的记录? 哦,不,正好相反:很明显,此时版本列 为空。很难弄清楚 why 因为在周围的代码中它似乎不是。不过,我们对线程问题有一些确认,因为我们将其强制为单线程并停止获取 NPE。仍然不明白根本原因,'tho. 【参考方案1】:
@Version
@Column(name = "xxx")
private Integer xxx;

在你的VO中找到@Version注解,并在数据库中将此列设置为默认值;

【讨论】:

事实证明(已经很久了)这实际上是一个并发问题,两个线程在处理同一个条目。【参考方案2】:

我遇到了同样的问题。这是旧数据库记录的问题。您已经在数据库表中添加了版本列,并在 VO 中添加了 @Version 注释,它适用于在该 chagne 之后创建的新对象,但是它对于旧对​​象失败 - 此列中的值为 null。解决方案是更新所有旧对象 - 它们的版本列为 0(或某些不同的值,具体取决于数据类型)。希望对你有帮助。

【讨论】:

这对我有用。我为已经存在的对象添加了@Version,我必须全部更新它们并设置版本= 0。现在工作正常。【参考方案3】:
<version name="versionID" type="java.lang.Long" unsaved-value="null">
  <column name="version" precision="10" scale="0"/>
</version>

尝试删除 unsaved-value="null" 或添加 unsved-value="null"

【讨论】:

【参考方案4】:

这是我对trace的理解:

你或某事做一个findBy 这个triggers一个flush 会话包含一个脏对象,其版本字段(Long 类型)需要更新 Hibernate 尝试获取版本字段的下一个值以进行更新 这就是您获得 NPE 的地方

org.hibernate.engine.Versioning.increment(Versioning.java:131) 的正文是:

public static Object increment(Object version, VersionType versionType, SessionImplementor session) 
    Object next = versionType.next( version, session ); // line 131
    if ( log.isTraceEnabled() ) 
        log.trace(
                "Incrementing: " +
                versionType.toLoggableString( version, session.getFactory() ) +
                " to " +
                versionType.toLoggableString( next, session.getFactory() )
        );
    
    return next;

以及org.hibernate.type.LongType.next(LongType.java:79)的主体(提供了上面versionType.next的实现):

public Object next(Object current, SessionImplementor session) 
    return new Long( ( (Long) current ).longValue() + 1 ); // line 79

很明显,传递给incrementversionnull

因此,我将在数据库中查找其版本列中具有NULL 值的记录。激活 SQL 日志记录可能有助于缩小搜索范围。

【讨论】:

以上是关于Grails/Hibernate:版本控制上的空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

grails - org.hibernate.QueryException(无法解析属性)

Grails (Hibernate) java.time.ZoneId 到数据库的映射

Grails/Hibernate 数据库在负载下崩溃:无法连接(即使在池中)

Grails hibernate/Searchable 通过给出以下异常来停止服务器启动

无法将数据库状态与 Grails Hibernate 中的会话错误同步

只读模式下不允许写操作(FlushMode.MANUAL):使用 grails hibernate