2012-05-14 3 views
3

Hibernate 3.6에서 4.1.3 Final 로의 업그레이드를 완료했으며 처음에는 모든 것이 정상적으로 진행된 것 같습니다. 그러나 내 동료 중 한 명은 최근 한 시나리오에서 Hibernate 내에서 NullPointer를 가져 오는 시나리오를 테스트했습니다 (이 예외는 동일한 DB를 업그레이드하기 전에 throw되지 않았습니다). 엄청나게 이상한 시나리오입니다. 우리는 다음과 같은 블로그 게시물라는 엔티티를 가지고 있으며 (나는 또한 포함되었는지) 일부는 슈퍼 클래스를 매핑 확장 :Hibernate 4.1.3으로 업그레이드 한 후 NullPointerException 예외가 발생했습니다.

@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(); 
    } 
} 

이상한 일이 일어나고 나는 몇 가지 시나리오에서 블로그 게시물을 쿼리 할 때. 나는 아래의 쿼리를 실행하는 경우, 예를 들어, 고립 그때는 잘 작동하지만 다음 다른 쿼리의 무리 사이를에서 실행하면 나는 아래의 예외를 얻을 :

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에서 새롭게 도입 된 버그라고 추측하고 있지만 잘못된 것일 수있다. 중요한 버그 수정을 위해 수행해야 할 최대 절전 모드 검색을 업그레이드하기 위해 최대한 업그레이드해야하므로 중요한 문제가 발생합니다.

는 또한 우리가 사용하고있는 DB가 우리의 비트 열을 처리하기 위해이 업그레이드를 수행 할 때 내가 쓴 다음과 같은 사용자 정의 방언과 MySQL의 대상이므로주의 :

public class MySQL5InnoDBDialectExt extends MySQL5InnoDBDialect { 

    private static final String BIT_STRING = "bit"; 

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

감사합니다, 브렌트

+0

참고로, 나는 이것을 Hibernate 포럼에도 게시했다 : https://forum.hibernate.org/viewtopic.php?f=1&t=1015428 – brent777

답변

3

나는이 정렬 문제를 해결하고, 우연히 그 문제를 발견했습니다. 내가 Hibernate 포럼에 게시 한 해결책은 다음과 같다.

문제점을 발견했다. 이는 캐싱이나 계측 대신에 인터셉터와 관련이있는 것 같지 않습니다 ( ). 기본적으로 우리의 응용 프로그램 은 매우 구체적인 패키지 내의 모든 엔티티를 자동으로 캐싱 체계 인 에 포함하고 계측에 같은 클래스를 포함합니다. 우리는 에 일반적으로이 패키지에있는 모든 엔티티가 있지만이 은이 패키지에 포함되지 않은 유일한 패키지입니다. 이전 버전의 EhCache/Hibernate는 우리가 을 사용하는 것으로 보였지만 업그레이드 후 문제가 발생했습니다.

어쨌든 엔티티가 올바르지 않은 패키지에 있었는데 리팩토링 한 후 을 올바른 패키지로 옮기면 모든 것이 작동했습니다! 그래서 은 최대 절전 모드의 버그가 아니며, 은이 문제를 추적하기가 어렵습니다. (기본적으로 완전 우연으로 해결되었습니다).

0

희망이 있으면 누군가에게 도움이되지만 내 경우에는 잘못된 계측기에 문제가있는 것입니다.

클래스 'A'와 두 개의 하위 클래스 'B'와 'C'가 있습니다. 'A'클래스는 게으른 속성을 가지고 있으며 게으른 액세스 작업을하기 위해 사용됩니다.

하지만 실수로 'B'와 'C'라는 하위 클래스를 계측하지 않았으므로 'B'와 'C'의 계측 된 속성에 액세스하면 예외가 발생했습니다.

'B'와 'C'를 계측 할 때 문제가 사라졌습니다.

관련 문제