java - Hibernate @Where 注释不适用于继承

标签 java hibernate inheritance where-clause

我正在使用 hibernate 5.1.2

我遇到了一个我似乎无法解决的意外问题。这是我的数据模型的摘要:
enter image description here
dfip_project_version是我的父类(super class)表,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 中。表,当它实际上在父类(super class)表上时( dfip_project_version )。

到目前为止,这是我尝试解决此限制的方法:

尝试 1

我试着把 @Where注释到 AbstractProjectVersion super 类,像这样:
@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

我做了 applicationVersions列表于 Application LAZY,并试图映射 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;
}

这导致 Hibernate 生成以下 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 .

如果我不能在我的连接公式中指定一个子查询,那么我就不能手动加入父类(super class)...

尝试 3

我注意到 @JoinFormula 的用法让 Hibernate 注意到我 @Where父类(super class)上的注释。所以我尝试了以下方法:
@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

最佳答案

我有一个解决这个问题的方法。我必须承认,它最终比我希望的要麻烦一些,但它确实工作得很好。我等了几个月才发帖,以确保没有问题,到目前为止,我还没有遇到任何问题。

我的实体仍然完全按照问题中的描述进行映射,但没有使用有问题的 @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条款:)。

关于java - Hibernate @Where 注释不适用于继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46577615/

相关文章:

java - 如何使类(class)特化有哪些选择或最佳实践?

java - 为什么这里不允许转换为 "GenericType<?>"?

java - 除了 tomcat 之外,有没有像 XAMPP 这样的东西?

java - JSoup 解析 HTML 问题

Hibernate 正常,但 ElipseLink 失败并显示 : the attribute [x] of class [Y] is mapped to a primary key column in the database updates not Allowed

mysql - UUID 作为实体 ID

c++ - 抽象基类调用父类的纯虚函数

python - python抽象基类可以继承自C扩展吗?

Java 程序(class > .jar)作为 Unix shell(代替 sh/bash)?

java - 我可以使用 Hibernate 只更新一个关系(一个字段)吗