2017-10-05 2 views
1

Ich bin mit Hibernate 5.1.2Hibernate Annotations @Where nicht mit Vererbung arbeiten

Ich habe in ein unerwartetes Problem führen, dass ich nicht um zu arbeiten scheinen. Hier ist die Zusammenfassung meiner Datenmodell: enter image description here

dfip_project_version ist mein Super Tisch und dfip_appln_proj_version ist meine Unterklasse Tabelle. dfip_application enthält eine Liste von dfip_appln_proj_version s.

Ich habe dies wie folgt abgebildet:

@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 = []; 
} 

ich die @Where Anmerkung verwende, so dass nur der aktuelle ApplicationProjectVersion mit dem Application abgerufen wird.

Das Problem dabei ist, dass Hibernate davon ausgeht, dass die Spalte, die ich referenziere, in der dfip_appl_proj_version-Tabelle ist, wenn es tatsächlich in der Superklasse-Tabelle ist (dfip_project_version).

Hier ist, was ich, um diese Einschränkung zu arbeiten bisher versucht:

Versuch 1

ich die @Where Anmerkung auf die AbstractProjectVersion Super-Klasse setzen versucht, etwa so:

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

Dies hat nichts bewirkt, da die WHERE-Klausel beim Abrufen der Application nicht bemerkt zu werden scheint.


Versuch 2

Ich habe die applicationVersions Liste auf Application LAZY und versuchte latestVersion manuell wie folgt zuzuordnen:

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

Diese Hibernate verursacht die folgende SQL (Snippet zu generieren):

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 

, die in ORA-01799: a column may not be outer-joined to a subquery resultierte.

Wenn ich nicht eine Unterabfrage in meiner beitreten Formel angeben kann, dann kann ich nicht von Hand in der Super-Klasse beitreten ...


Versuch 3

Ich bemerkte, dass Verwendung von @JoinFormula macht Hibernate bemerken meine @Where Annotation auf der Super-Klasse. Also versuchte ich folgendes:

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

Dies erzeugt die folgende SQL (Snippet):

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 

Diese fast richtig ist!Leider ist es nicht gültig SQL, da applicatio2_1_ verwendet wird, bevor es in der nächsten Zeile deklariert ist :(.


Jetzt habe ich aus Ideen bin, so würde jede mögliche Hilfe geschätzt. Gibt es eine Möglichkeit ein angeben WHERE-Klausel, die nur in der aktuellen ProjectVersion bringen wird, ohne meiner Vererbungsstruktur loszuwerden?

Related Hibernate issue ticket

Antwort

0

ich eine Lösung für dieses Problem. ich muss zugeben, es endete ein wenig umständlicher sein als das, was Ich habe gehofft, aber es klappt ganz gut, ich habe ein paar Monate gewartet, bevor ich gepostet habe e dass es keine Probleme gibt und bis jetzt habe ich keine Probleme erfahren.

Meine Entitäten genau noch abgebildet werden, wie in der Frage beschrieben, sondern die problematische @Where Annotation zu verwenden, hatte ich stattdessen @Filter Annotation zu verwenden: auch

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 = []; 

} 

Da wir einen Filter verwenden, müssen wir definieren sie:

// 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 { 

} 

Und natürlich wir es ermöglichen müssen, da Hibernate dreht sich nicht um eine Einstellung automatisch auf alle Filter.

Um dies zu tun, ich eine systemweite Aspect geschaffen, deren Aufgabe es ist spezifizierten Filter vor jedem Anruf auf eine beliebige DAO zu aktivieren:

/** 
* 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 
     } 
    } 

} 

und die entsprechenden Spring-Konfiguration:

<!-- 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> 

Und Da hast du es - polymorphe @Where Klausel :).

Verwandte Themen