我有一个自我连接的查询,如下所示
选择t1.,t2。从表t1左出t2.LFT < t1.LFT和t2.RGT > t1.RGT和t2.REG_CODE_PAR = 'ALL‘和t1.STATUS_CODE = 'A’和t2.STATUS_CODE = 'A‘上的表t2
我使用@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,而不是让两个EntityResults使用相同的entityClass,我得到相同的
结果。
有人能指出配置有什么问题吗?
发布于 2010-07-23 06:01:39
让我看看我是否理解你的问题。您希望从每个本机查询结果行中捕获两个Tree实体。第一个实体应由t1的列组成,第二个实体应由t2的列组成。与预期相反,您实际上收到了两个由t1构成的实例。没有t2的实例出现。在调试过程中,您为Tree创建了一个名为TreeSelf的二重实体,但TreeSelf最终是不必要的,您想摆脱它。如果有任何错误,请停止我的操作。
我认为问题是由于列名模棱两可。本机查询中的每个列名出现两次,一次来自t1,一次来自t2。结果映射器似乎任意选择两个Tree实体的每个模糊列名的第一次出现。我很惊讶那能起作用。我本以为SQLException会抱怨列引用含糊不清。
另外,你确定你想要左外接吗?如果没有找到t1行的匹配项,怎么办?它将与t2列中的所有NULL配对。然后您有一个空值Tree实体。我想。我甚至不知道结果映射器在这种情况下会做什么。也许您想要一个内部连接?
考虑将此原生查询转换为JPQL查询。(JPA标准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属性必须相应地更改。因此,第一个EntityResult下的EntityResult属性都应该从t1_开始,第二个属性都应该从t2_开始。
我建议删除本机查询和sql结果映射器,并使用JPA查询语言或条件API (如果可以找到方法)。
Update:正如您的注释中所确认的,对您的问题的有用回答必须保留左(外)连接语义。不幸的是,JPQL和标准API不支持复杂的左连接条件。没有办法用显式ON条件限定JPQL左联接。
据我所知,在规范下进行左外部连接的唯一方法是遍历实体关系。然后,JPA实现生成一个测试标识相等性的ON条件。相关的规范位是4.4.5“联接”和4.4.5.2“左外接”。
为了满足这个约束,您想要留给它的最终父节点的每个Tree都必须有一个额外的列来存储最终父的id。您可能能够以多种方式(视图?)绕过这个约束。但是阻力最小的路径似乎是修改本机查询以使用别名参数,删除TreeSelf,并相应地更新结果映射器。更聪明的解决方案欢迎,尽管..。
https://stackoverflow.com/questions/3314822
复制相似问题