3

Hibernate3.6から4.1.3Finalへのアップグレードを完了したところですが、最初はすべてうまくいったようです。ただし、最近、私の同僚の1人がこれをテストしました。あるシナリオでは、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を実装すると、すべてが完全に機能することです。これにより、バグはマップされたスーパークラスまたはCommunityModelObjectに適用しているHibernateフィルターのいずれかに関連していると思います。

これを解決する方法について何かアイデアはありますか?これはHibernateで新しく導入されたバグだと思いますが、間違っている可能性があります。重大なバグ修正のために行う必要のあるHibernateSearchをアップグレードするには、できるだけ早くアップグレードする必要があるため、これが大きな問題を引き起こしています。

また、使用しているDBは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」と2つの子クラス「B」と「C」があります。「A」クラスには遅延プロパティがあり、遅延アクセスが機能するようにインストルメント化されています。

しかし、間違いは、子クラス 'B' と 'C' を計測しなかったことです。したがって、'B' と 'C' から計測されたプロパティにアクセスすると、例外が発生しました。

「B」と「C」をインストルメント化すると、問題は解消されました。

于 2014-05-08T07:53:27.703 に答える