Hibernate @Where 注释不适用于继承
Hibernate @Where annotation not working with inheritance
我正在使用 Hibernate 5.1.2
我 运行 遇到了一个我似乎无法解决的意外问题。这是我的数据模型的摘要:
dfip_project_version
是我的超级classtable,dfip_appln_proj_version
是我的子classtable。 dfip_application
包含 dfip_appln_proj_version
的列表。
我将其映射如下:
@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
注释,以便使用 Application
仅检索当前 ApplicationProjectVersion
。
这个问题是 Hibernate 假设我引用的列在 dfip_appl_proj_version
table 中,而实际上它在 super-class table (dfip_project_version
).
以下是我迄今为止尝试解决此限制的方法:
尝试 1
我试着把 @Where
注释放到 AbstractProjectVersion
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...
}
这没有任何作用,因为在检索 Application
.
时似乎没有注意到 WHERE 子句
尝试 2
我在 Application
LAZY 上创建了 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;
}
这导致 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
.
如果我不能在我的连接公式中指定一个子查询,那么我就不能手动连接到超级class...
尝试 3
我注意到 @JoinFormula
的使用使 Hibernate 注意到我在 super-class 上的 @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_
在下一行声明之前被使用了 :(.
现在我没有想法,所以任何帮助将不胜感激。有没有办法指定一个只引入当前 ProjectVersion 的 WHERE 子句,而不会删除我的继承结构?
我有办法解决这个问题。我必须承认,它最终比我希望的要麻烦一些,但它确实工作得很好。我等了几个月才发帖,以确保没有问题,到目前为止,我还没有遇到任何问题。
我的实体仍然完全按照问题中的描述进行映射,但我没有使用有问题的 @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
子句 :).
由于您正在寻找带有继承的@Where,我假设您正在尝试全局应用一些 SQL 逻辑,也许休眠拦截器或 SQL 检查器更适合这种类型要求
我正在使用 Hibernate 5.1.2
我 运行 遇到了一个我似乎无法解决的意外问题。这是我的数据模型的摘要:
dfip_project_version
是我的超级classtable,dfip_appln_proj_version
是我的子classtable。 dfip_application
包含 dfip_appln_proj_version
的列表。
我将其映射如下:
@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
注释,以便使用 Application
仅检索当前 ApplicationProjectVersion
。
这个问题是 Hibernate 假设我引用的列在 dfip_appl_proj_version
table 中,而实际上它在 super-class table (dfip_project_version
).
以下是我迄今为止尝试解决此限制的方法:
尝试 1
我试着把 @Where
注释放到 AbstractProjectVersion
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...
}
这没有任何作用,因为在检索 Application
.
尝试 2
我在 Application
LAZY 上创建了 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;
}
这导致 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
.
如果我不能在我的连接公式中指定一个子查询,那么我就不能手动连接到超级class...
尝试 3
我注意到 @JoinFormula
的使用使 Hibernate 注意到我在 super-class 上的 @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_
在下一行声明之前被使用了 :(.
现在我没有想法,所以任何帮助将不胜感激。有没有办法指定一个只引入当前 ProjectVersion 的 WHERE 子句,而不会删除我的继承结构?
我有办法解决这个问题。我必须承认,它最终比我希望的要麻烦一些,但它确实工作得很好。我等了几个月才发帖,以确保没有问题,到目前为止,我还没有遇到任何问题。
我的实体仍然完全按照问题中的描述进行映射,但我没有使用有问题的 @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
子句 :).
由于您正在寻找带有继承的@Where,我假设您正在尝试全局应用一些 SQL 逻辑,也许休眠拦截器或 SQL 检查器更适合这种类型要求