我有一个自我联接的查询,看起来像这样,
从表t1中选择t1。,t2。 左外连接表t2在t2.LFT< t1.LFT 和t2.RGT> t1.RGT AND t2.REG_CODE_PAR ='ALL' AND t1.STATUS_CODE ='A' AND t2.STATUS_CODE ='A'
我正在使用@NamedNativeQuery和结果集映射来获得结果。
@NamedNativeQuery(
name="findTree",
query="..... the query above",
resultSetMapping = "regionT")
使用以下结果集映射
@SqlResultSetMapping(name = "regionT" , entities ={
@EntityResult(
entityClass = Tree.class
fields = {
@FieldResult(name = "regCode", column = "REG_CODE")
@FieldResult(name = "rgt", column = "RGT"),
@FieldResult(name = "lft", column = "LFT"),
@FieldResult(name = "name", column = "NAME"),
@FieldResult(name = "regCodePar", column = "REG_CODE_PAR"),
@FieldResult(name = "statusCode", column = "STATUS_CODE")
}
),
@EntityResult(
entityClass = TreeSelf.class
fields = {
@FieldResult(name = "regCode1", column = "REG_CODE")
@FieldResult(name = "rgt1", column = "RGT"),
@FieldResult(name = "lft1", column = "LFT"),
@FieldResult(name = "name1", column = "NAME"),
@FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"),
@FieldResult(name = "statusCode1", column = "STATUS_CODE")
}
)
})
实体类包含这样的内容。
@NamedNativeQuery(...)
@SqlResultSetMapping(...)
@Entity
@Table(name = "table")
public class Tree implements Serializable {
@Id
@Column(name = "REG_CODE")
private String regCode; ... ..getters and setters...}
当我使用em.createQuery(“findTree”)运行查询时,我在两者中得到完全相同的对象
返回的对象数组的第1个和第2个元素。 即使我创建了一个名为TreeSelf的类,它与Tree相同并将其用作第二个
EntityResult而不是使用相同的entityClass有2个EntityResults,我得到相同的
结果。
有人可以指出配置有什么问题吗?
答案 0 :(得分:1)
让我们看看我是否理解你的问题。您希望从每个本机查询结果行捕获两个Tree
实体。第一个实体应该由t1
列组成。第二个实体应该由t2
列组成。与预期相反,您实际上会收到由t1
组成的两个实例。没有来自t2
的实例。您在调试时为Tree
调用TreeSelf
创建了一个doppelganger实体,但TreeSelf
最终是不必要的,您想要摆脱它。如果有任何错误,请阻止我。
我认为问题是由于列名不明确。原生查询中的每个列名称出现两次,一次来自t1
,一次来自t2
。结果映射器似乎是任意选择两个Tree
实体的每个模糊列名称的第一次出现。我很惊讶这一切都很有效。我原以为SQLException会抱怨列引用歧义。
另外,您确定要进行左外连接吗?如果找不到t1
行的匹配怎么办?它将与t2
列中的所有NULL配对。然后你有一个空值Tree
实体。我认为。在这种情况下,我甚至不知道结果映射器会做什么。也许你想要一个内部联接?
考虑将此本机查询转换为JPQL查询。 (JPA Criteria API也是如此,但我发现它的例子比较麻烦。)这是本机查询的JPQL版本:
SELECT t1, t2
FROM Tree t1, Tree t2
WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt AND t2.regCodePar = 'ALL' AND
t1.statusCode = 'A' AND t2.statusCode = 'A'
N.B。:这会将连接语义更改为内部而不是左外部。
以下是可以运行此查询的代码草图:
EntityManager em = ... // EntityManager by injection, EntityManagerFactory, etc.
String jpql = ... // Like example above
TypedQuery<Object[]> q = em.createQuery(jpql, Object[].class);
for (Object[] oa : q.getResultList()) {
Tree t1 = (Tree)oa[0];
Tree t2 = (Tree)oa[1];
}
如果您因任何原因而无法使用本机查询,请按照以下方法解决列名歧义问题。不是像select t1.*, t2.*
那样启动原生查询,而是使用AS
为每个列添加别名。 SELECT
子句类似于:
SELECT t1.REG_CODE AS t1_REG_CODE, t1.RGT AS t1_RGT, (... rest of t1 cols ...),
t2.REG_CODE AS t2_REG_CODE, t2.RGT AS t2_RGT, (... rest of t2 cols ...)
每个column
中的FieldResult
属性必须相应更改。因此,第一个column
下的EntityResult
属性应始于t1_
,而第二个属性应始于t2_
。
如果你能找到办法的话,我谦虚地建议你删除原生查询和sql结果映射器,并使用JPA查询语言或Criteria API。
更新:正如您的评论中所确认的,对您的问题的有用答案必须保留左(外)连接语义。不幸的是,JPQL和Criteria API不支持复杂的左连接条件。没有办法使用明确的ON
条件限定JPQL左连接。
据我所知,在规范下进行左外连接的唯一方法是遍历实体关系。然后,JPA实现生成一个ON
条件来测试身份相等性。相关的spec位是4.4.5“Joins”和4.4.5.2“Left Outer Joins”。
要满足此约束,您希望左连接到其最终父级的每个Tree
必须有一个存储最终父级ID的附加列。您可以通过各种方式(视图?)来欺骗这种约束。但是阻力最小的路径似乎是修改本机查询以使用别名参数,删除TreeSelf,并相应地更新结果映射器。尽管如此,Cleverer解决方案仍然受到欢迎......