升级到Hibernate 4.1.3 Final后,在flush上抛出NullPointerException

时间:2012-05-14 17:01:49

标签: java hibernate

我刚刚完成了从Hibernate 3.6到4.1.3 Final的升级,起初一切似乎都没问题。但是,我的一位同事最近在一个场景中对它进行了测试,他从Hibernate中抛出了一个NullPointer(并且在我们升级到完全相同的DB之前没有抛出这个异常)。这是一个非常奇怪的场景。我们有一个名为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,那么一切都很完美。这让我相信这个bug与映射的超类或我应用于CommunityModelObject的Hibernate过滤器有关。

关于如何解决这个问题的任何想法?我假设它是Hibernate中新引入的错误,但我可能错了。这对我们造成了重大问题,因为我们需要升级asap以升级Hibernate Search,而我们需要针对关键错误修复进行升级。

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

public class MySQL5InnoDBDialectExt extends MySQL5InnoDBDialect {

    private static final String BIT_STRING = "bit";

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

谢谢, 布伦特

2 个答案:

答案 0 :(得分:3)

我把这个问题整理出来,发现了侥幸的问题。以下是我在Hibernate论坛上发布的解决方案:

  

我发现了这个问题。它似乎与拦截器没有关系,   而不是缓存或仪表。基本上我们的应用程序   自动包含特定包中的所有实体   我们的缓存方案和我们的仪器中的相同类。我们   通常我们所有的实体都在这个包中,不过这个   引起这个问题的唯一一个没有包括在内   包。我们以前的EhCache / Hibernate版本   使用似乎没问题,但升级后会导致问题。

     

无论如何,当我重构它时,实体是在错误的包中   并将其移动到正确的包中然后一切正常!所以   这不是Hibernate中的一个错误,只是一个提供信息的例外   很难跟踪这个问题(我基本上完成了解决它   吸虫)。

答案 1 :(得分:0)

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

我上课' A'和两个孩子班级' B'和' C'。 ' A' class有一个惰性属性,它可以使延迟访问工作。

但错误的是,我并没有在儿童班上工作。' B'和' C'因此,任何人都可以从' B'和' C'引起了例外。

当我配备B' B'并且' C',问题就消失了。