2017-10-05 142 views
1

我正在使用休眠5.1.2休眠@Where註解不與繼承工作

我遇到了意想不到的問題,我似乎無法解決。這是我的數據模型的總結: enter image description here

dfip_project_version是我的父表,dfip_appln_proj_version是我的子類的表。 dfip_application包含dfip_appln_proj_version s的列表。

我制訂具體步驟如下:

@Table(name = "DFIP_PROJECT_VERSION") 
@Entity 
@Inheritance(strategy = InheritanceType.JOINED) 
public abstract class AbstractProjectVersion { 
    @Id @GeneratedValue 
    @Column(name = "PROJECT_VERSION_OID") 
    Long oid; 

    @Column(name = "PROJ_VSN_EFF_FROM_DTM") 
    Timestamp effFromDtm; 

    @Column(name = "PROJ_VSN_EFF_TO_DTM") 
    Timestamp effToDtm; 

    @Column(name = "PROJECT_VERSION_TYPE") 
    @Type(type = "project_version_type") 
    ProjectVersionType projectVersionType; 
} 


@Table(name = "DFIP_APPLN_PROJ_VERSION") 
@Entity 
class ApplicationProjectVersion extends AbstractProjectVersion { 

    @OneToOne 
    @JoinColumn(name = "APPLICATION_OID", nullable = false) 
    Application application; 

    public ApplicationProjectVersion() { 
     projectVersionType = ProjectVersionType.APPLICATION; 
    } 
} 

@Table(name = "DFIP_APPLICATION") 
@Entity 
class Application { 

    @Id @GeneratedValue 
    @Column(name = "APPLICATION_OID") 
    Long oid; 

    @OneToMany(mappedBy="application", orphanRemoval = true, fetch = FetchType.EAGER) 
    @Fetch(FetchMode.SELECT) 
    @Where(clause = "PROJ_VSN_EFF_TO_DTM is null") 
    List<ApplicationProjectVersion> applicationVersions = []; 
} 

我使用@Where註釋,這樣只有當前ApplicationProjectVersionApplication檢索。

問題在於,Hibernate假定我所引用的列在dfip_appl_proj_version表中,當它實際位於超類表(dfip_project_version)中時。

這裏是我試過至今解決此限制:

嘗試1

我試圖把@Where註釋到AbstractProjectVersion超一流的,像這樣:

@Table(name = "DFIP_PROJECT_VERSION") 
@Entity 
@Inheritance(strategy = InheritanceType.JOINED) 
@Where(clause = "PROJ_VSN_EFF_TO_DTM is null") 
public abstract class AbstractProjectVersion { 
    ...etc... 
} 

這沒有做什麼,因爲在檢索Application時WHERE子句似乎沒有被注意到。


嘗試2

我發上Application懶惰applicationVersions列表中,並試圖手動映射latestVersion這樣的:

@Table(name = "DFIP_APPLICATION") 
@Entity 
class Application { 

    @Id @GeneratedValue 
    @Column(name = "APPLICATION_OID") 
    Long oid; 

    @OneToMany(mappedBy="application", orphanRemoval = true, fetch = FetchType.LAZY) 
    @Fetch(FetchMode.SELECT) 
    List<ApplicationProjectVersion> applicationVersions = []; 

    @ManyToOne 
    @JoinColumnsOrFormulas([ 
     @JoinColumnOrFormula(formula = @JoinFormula(value = "(APPLICATION_OID)", referencedColumnName="APPLICATION_OID")), 
     @JoinColumnOrFormula(formula = @JoinFormula(value = "(select apv.PROJECT_VERSION_OID from DFIP_PROJECT_VERSION pv, DFIP_APPLN_PROJ_VERSION apv where apv.PROJECT_VERSION_OID = pv.PROJECT_VERSION_OID and apv.APPLICATION_OID = APPLICATION_OID and pv.PROJ_VSN_EFF_TO_DTM is null)", referencedColumnName="PROJECT_VERSION_OID")), 
    ]) 
    ApplicationProjectVersion latestVersion; 
} 

這導致休眠生成以下SQL(片斷):

from DFIP_APPLICATION this_ 
left outer join DFIP_APPLN_PROJ_VERSION applicatio2_ 
    on (this_.APPLICATION_OID)=applicatio2_.APPLICATION_OID and 
     (select apv.PROJECT_VERSION_OID from DFIP_PROJECT_VERSION pv, DFIP_APPLN_PROJ_VERSION apv 
     where apv.PROJECT_VERSION_OID = pv.PROJECT_VERSION_OID and apv.APPLICATION_OID = this_.APPLICATION_OID 
     and pv.PROJ_VSN_EFF_TO_DTM is null)=applicatio2_.PROJECT_VERSION_OID 

其中產生ORA-01799: a column may not be outer-joined to a subquery

如果我不能指定我加入公式一個子查詢,那麼我不能加入手動超一流...


嘗試3

我注意到,使用@JoinFormula會讓Hibernate在超級類別上注意我的@Where註釋。所以,我試過如下:

@Table(name = "DFIP_PROJECT_VERSION") 
@Entity 
@Inheritance(strategy = InheritanceType.JOINED) 
@Where(clause = "PROJ_VSN_EFF_TO_DTM is null") 
public abstract class AbstractProjectVersion { 
    ...etc... 
} 

@Table(name = "DFIP_APPLICATION") 
@Entity 
class Application { 

    @Id @GeneratedValue 
    @Column(name = "APPLICATION_OID") 
    Long oid; 

    @OneToMany(mappedBy="application", orphanRemoval = true, fetch = FetchType.LAZY) 
    @Fetch(FetchMode.SELECT) 
    List<ApplicationProjectVersion> applicationVersions = []; 

    @ManyToOne 
    @JoinFormula(value = "(APPLICATION_OID)", referencedColumnName="APPLICATION_OID") 
    ApplicationProjectVersion latestVersion; 
} 

這產生了下面的SQL(片段):

from DFIP_APPLICATION this_ 
left outer join DFIP_APPLN_PROJ_VERSION applicatio2_ 
    on (this_.APPLICATION_OID)=applicatio2_.APPLICATION_OID and (applicatio2_1_.PROJ_VSN_EFF_TO_DTM is null) 
left outer join DFIP_PROJECT_VERSION applicatio2_1_ on applicatio2_.PROJECT_VERSION_OID=applicatio2_1_.PROJECT_VERSION_OID 

這幾乎是正確的!不幸的是它不是有效的SQL,因爲applicatio2_1_它在下一行:(聲明之前使用。


現在我的想法,所以任何幫助,將不勝感激。有沒有指定的方式WHERE子句將僅在當前ProjectVersion帶來的,沒有擺脫我的繼承結構的?

Related Hibernate issue ticket

回答

0

我有一個解決這個問題。我必須承認,它結束了一個小比更麻煩什麼我希望,但它確實工作得很好。在發佈之前,我等了幾個月,讓sur e沒有問題,迄今爲止,我沒有遇到任何問題。

我的實體仍然映射完全一樣的問題描述,但不是使用有問題的@Where註釋,我不得不使用@Filter註解來替代:

public class Application { 

    @OneToMany(mappedBy="application", orphanRemoval = true, fetch = FetchType.EAGER) 
    @Cascade([SAVE_UPDATE, DELETE, MERGE]) 
    @Fetch(FetchMode.SELECT) 

    // Normally we'd just use the @Where(clause = "PROJ_VSN_EFF_TO_DTM is null"), but that doesn't work with collections of 
    // entities that use inheritance, as we have here. 
    // 
    // Hibernate thinks that PROJ_VSN_EFF_TO_DTM is a column on DFIP_APPLN_PROJ_VERSION table, but it is actually on the "superclass" 
    // table (DFIP_PROJECT_VERSION). 
    // 
    // B/c of this, we have to do the same thing with a Filter, which is defined on AbstractProjectVersion. 
    // NOTE: This filter must be explicitly enabled, which is currently achieved by HibernateForceFiltersAspect 
    // 
    @Filter(name="currentProjectVersionOnly", 
     condition = "{pvAlias}.PROJ_VSN_EFF_TO_DTM is null", 
     deduceAliasInjectionPoints=false, 
     aliases=[ @SqlFragmentAlias(alias = "pvAlias", table = "DFIP_PROJECT_VERSION") ] 
    ) 
    List<ApplicationProjectVersion> projectVersions = []; 

} 

由於我們使用的過濾器,我們也必須定義它:

// NOTE: This filter needs to be explicitly turned on with session.enableFilter("currentProjectVersionOnly"); 
// This is currently achieved with HibernateForceFiltersAspect 
@FilterDef(name="currentProjectVersionOnly") 

@Table(name = "DFIP_PROJECT_VERSION") 
@Inheritance(strategy = InheritanceType.JOINED) 
public abstract class AbstractProjectVersion { 

} 

當然,我們必須啓用它,因爲Hibernate沒有自動打開所有過濾器的設置。

要做到這一點我創建了一個全系統的看點,他們的工作是每次調用任何DAO前啓用指定的過濾器:

/** 
* Enables provided Hibernate filters every time a Hibernate session is openned. 
* 
* Must be enabled and configured explicitly from Spring XML config (i.e. no auto-scan here) 
* 
* @author Val Blant 
*/ 
@Aspect 
public class HibernateForceFiltersAspect { 

    List<String> filtersToEnable = []; 

    @PostConstruct 
    public void checkConfig() throws Exception { 
     if (filtersToEnable.isEmpty()) { 
      throw new IllegalArgumentException("Missing required property 'filtersToEnable'"); 
     } 
    } 

    /** 
    * This advice gets executed before all method calls into DAOs that extend from <code>HibernateDao</code> 
    * 
    * @param jp 
    */ 
    @Before("@target(org.springframework.stereotype.Repository) && execution(* ca.gc.agr.common.dao.hibernate.HibernateDao+.*(..))") 
    public void enableAllFilters(JoinPoint jp) { 
     Session session = ((HibernateDao)jp?.getTarget())?.getSession(); 

     if (session != null) { 
      filtersToEnable.each { session.enableFilter(it) } // Enable all specified Hibernate filters 
     } 
    } 

} 

以及相應的Spring配置:

<!-- This aspect is used to force-enable specified Hibernate filters for all method calls on DAOs that extend HibernateDao --> 
<bean class="ca.gc.agr.common.dao.hibernate.HibernateForceFiltersAspect"> 
    <property name="filtersToEnable"> 
     <list> 
      <value>currentProjectVersionOnly</value>   <!-- Defined in AbstractProjectVersion --> 
     </list> 
    </property> 
</bean> 

而且你有它 - 多態@Where條款:)。