3

我刚刚完成了从 Hibernate 3.6 到 4.1.3 Final 的升级,起初一切似乎都很好。但是,我的一位同事最近对此进行了测试,在一个场景中,他从 Hibernate 中抛出了一个 NullPointer(并且在我们为完全相同的数据库升级之前没有抛出这个异常)。这是一个非常奇怪的场景。我们有一个名为 BlogPost 的实体,如下所示,它扩展了一些映射的超类(我也包括在内):

@Entity
@Table(name = "blog_post")
public class BlogPost extends CommunityModelObject implements HasFeedPost {

    @Lob
    private String title;
    @Lob
    private String content;
    @Enumerated
    @Column(nullable = false)
    private CBlogPost.Status status = CBlogPost.Status.UNPUBLISHED;
    // Reference to the feed post that indicates that this blog post has been published
    @OneToOne
    @JoinColumn(name = "feed_post_id")
    private FeedPost feedPost;
    @ManyToOne
    @JoinColumn(name = "posted_by_employee_id")
    private Employee postedBy;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public CBlogPost.Status getStatus() {
        return status;
    }

    public void setStatus(CBlogPost.Status status) {
        this.status = status;
    }

    @Override
    public FeedPost getFeedPost() {
        return feedPost;
    }

    @Override
    public void setFeedPost(FeedPost feedPost) {
        this.feedPost = feedPost;
    }

    public Employee getPostedBy() {
        return postedBy;
    }

    public void setPostedBy(Employee postedBy) {
        this.postedBy = postedBy;
    }
}


@Filter(name = "tenantFilter", condition = "(tenant_id = :tenantId or tenant_id is null)")
@MappedSuperclass
public abstract class CommunityModelObject extends ModelObject {

    @IndexedEmbedded(prefix = "tenant", indexNullAs = IndexedEmbedded.DEFAULT_NULL_TOKEN)
    @ManyToOne
    @JoinColumn(name = "tenant_id")
    protected Tenant tenant;

    public Tenant getTenant() {
        return tenant;
    }

    public void setTenant(Tenant tenant) {
        this.tenant = tenant;
    }

    /**
     * If the Tenant is null then it can be accessed / viewed by the entire "community" / user base
     */
    public boolean isCommunityObject() {
        return tenant == null;
    }
}


@MappedSuperclass
public abstract class ModelObject extends BaseModelObject {

    @Id
    @GeneratedValue
    private Long id;

    @Override
    public long getId() {
        return (id == null ? 0 : id);
    }

    public void setId(long id) {
        this.id = (id == 0 ? null : id);
    }
}


@MappedSuperclass
public abstract class BaseModelObject implements java.io.Serializable {

    // This annotation ensures that a column is not associated with this member (simply omitting the @Column annotation is not enough since
    // that annotation is completely optional)
    @Transient
    private boolean doNotAutoUpdateDateUpdated = false;

    @Version
    protected int version;
    @Column(name = "date_created")
    protected Date dateCreated;
    @Column(name = "date_updated")
    protected Date dateUpdated;

    public abstract long getId();

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public Date getDateCreated() {
        return dateCreated;
    }

    public Date getDateUpdated() {
        return dateUpdated;
    }

    /**
     * This will set the dateUpdated to whatever is passed through and it will cause the auto update (pre-update) to NOT occur
     *
     * @param dateUpdated
     */
    public void setDateUpdated(Date dateUpdated) {
        doNotAutoUpdateDateUpdated = true;
        this.dateUpdated = dateUpdated;
    }

    public void touch() {
        // By setting date updated to null this triggers an update which results in onUpdate being called and the nett
        // result is dateUpdated = new Date()
        dateUpdated = null;
    }

    @PrePersist
    protected void onCreate() {
        dateCreated = new Date();
    }

    @PreUpdate
    protected void onUpdate() {
        if (!doNotAutoUpdateDateUpdated) {
            dateUpdated = new Date();
        }
    }

    @Override
    public boolean equals(Object obj) {
        long id = getId();

        if (id == 0) {
            return this == obj;
        }
        //Use Hibernate.getClass() because objects might be proxies
        return obj != null &&
                obj instanceof BaseModelObject &&
                Hibernate.getClass(this) == Hibernate.getClass(obj) &&
                getId() == ((BaseModelObject)obj).getId();
    }

    @Override
    public int hashCode() {
        Long id = getId();
        return id == 0 ? super.hashCode() : id.intValue();
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "-" + getId();
    }
}

当我在某些情况下查询 BlogPost 时,会发生最奇怪的事情。例如,如果我单独运行下面的查询,那么它可以正常工作,但是如果我在一堆其他查询中运行它,那么我会得到以下异常:

select b from BlogPost b

java.lang.NullPointerException
   at org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:240)
   at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:163)
   at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
   at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
   at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:55)
   at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1153)
   at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1208)
   at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
   at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:256)

现在更重要的是,如果我从上面列出的所有映射超类中获取所有字段并将它们直接放入 BlogPost 并使 BlogPost 不扩展任何内容并实现 java.io.Serializable ,那么一切都会完美运行。这使我相信该错误与映射的超类或我应用于 CommunityModelObject 的 Hibernate 过滤器有关。

关于如何解决这个问题的任何想法?我假设这是 Hibernate 中新引入的错误,但我可能错了。这给我们带来了重大问题,因为我们需要尽快升级以升级 Hibernate Search,我们需要这样做来修复关键错误。

另请注意,我们使用的数据库是 MySQL,具有以下自定义方言,这是我在进行此升级以处理我们的 BIT 列时编写的:

public class MySQL5InnoDBDialectExt extends MySQL5InnoDBDialect {

    private static final String BIT_STRING = "bit";

    public MySQL5InnoDBDialectExt() {
        super();
        registerColumnType(Types.BOOLEAN, BIT_STRING);
    }
}

谢谢,布伦特

4

2 回答 2

3

我整理了这个问题,侥幸找到了问题。这是我在 Hibernate 论坛上发布的解决方案:

我发现了这个问题。它似乎与拦截器无关,而与缓存或检测无关。基本上,我们的应用程序会自动将所有实体包含在我们的缓存方案中一个非常特定的包中,并在我们的检测中包含相同的类。我们通常将所有实体都包含在此包中,但是导致问题的实体是唯一未包含在此包中的实体。我们使用的以前版本的 EhCache / Hibernate 似乎还可以,但升级后它会导致问题。

无论如何,实体位于不正确的包中,当我对其进行重构并将其移动到正确的包中时,一切正常!所以这不是 Hibernate 中的错误,只是一个信息异常,使得很难追踪这个问题(我基本上通过完全侥幸解决了它)。

于 2012-05-15T16:30:58.057 回答
0

希望这对某人有所帮助,但就我而言,这是错误仪器的问题。

我有“A”类和两个子类“B”和“C”。“A”类有一个惰性属性,它被用来使惰性访问工作。

但错误是我没有对子类“B”和“C”进行检测,因此从“B”和“C”对检测属性的任何访问都会导致异常。

当我检测“B”和“C”时,问题就消失了。

于 2014-05-08T07:53:27.703 回答