Hibernate @Where annotation not working with inheritance

Viewed 5206

I am using Hibernate 5.1.2

I have run into an unexpected problem that I can't seem to work around. Here's the summary of my data model: enter image description here

dfip_project_version is my superclass table, and dfip_appln_proj_version is my subclass table. dfip_application contains a list of dfip_appln_proj_versions.

I have mapped this as follows:

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

I am using the @Where annotation so that only the current ApplicationProjectVersion is retrieved with the Application.

The problem with this is that Hibernate assumes that the column I am referencing is in the dfip_appl_proj_version table, when it's actually on the super-class table (dfip_project_version).

Here's what I tried so far to work around this limitation:

Attempt 1

I tried putting the @Where annotation onto the AbstractProjectVersion super-class, like 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...
}

This did nothing, as the WHERE clause does not seem to be noticed when retrieving the Application.


Attempt 2

I made the applicationVersions list on Application LAZY, and tried to map latestVersion manually like this:

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

This caused Hibernate to generate the following SQL (snippet):

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 

which resulted in ORA-01799: a column may not be outer-joined to a subquery.

If I can't specify a sub-query in my join formula, then I cannot join to the super-class manually...


Attempt 3

I noticed that usage of @JoinFormula makes Hibernate notice my @Where annotation on the super-class. So I tried the following:

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

This generated the following 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 

This is almost correct! Unfortunately it is not valid SQL, since applicatio2_1_ is used before it is declared on the next line :(.


Now I am out of ideas, so any help would be appreciated. Is there a way to specify a WHERE clause that will bring in only the current ProjectVersion, without getting rid of my inheritance structure?

Related Hibernate issue ticket

2 Answers

since you are looking for @Where with inheritance, I assume you are trying to apply some SQL logic globally, maybe hibernate interceptor or SQL inspector would be a better fit for this type of requirement

Related