假设我们创建以下视图
CREATE VIEW V_ABC AS
SELECT TABLE_A.id as id, SUM(TABLE_A.a+TABLE_B.b-TABLE_C.c) as total, TABLE_A.a as a,TABLE_B.b as b,TABLE_C.c as c
FROM TABLE_A
join TABLE_B on TABLE_A.id = TABLE_B.fid
join TABLE_C on TABLE_B.id = TABLE_C.fid
GROUP BY TABLE_A.a,TABLE_B.b,TABLE_C.c;
将实体映射到此视图
public class VABC{
private Long id;
private BigDecimal total;
private BigDecimal a;
private BigDecimal b;
private BigDecimal c;
}
我的问题是,在J2EE应用程序中,每次调用来自VABC
实例的任何字段时,如下所示:
VABC vabc = VABC.findById(id);
BigDecimal currentTotal = vabc.getTotal();
JPA是否必须为视图执行上述整个查询,然后获取当前结果?
答案 0 :(得分:2)
JPA不知道它是一个视图,因为它是透明的,即它会尝试选择这样的东西:
SELECT f1,... FROM V_ABC where id=?
并且数据库会将其转换为相应的JOIN
& SUM()
次操作。
PS:为了检查这一点,您可以启用所有SQL查询的日志记录,并查看发送给数据库的内容。
答案 1 :(得分:1)
如果要将视图(结果集)轻松映射到实体类,可以利用所谓的构造函数表达式,即JPQL或Criteria API功能之一。
假设您已定义了JPQL或Criteria API查询,例如:
JPQL
SELECT NEW com.dreamer.jpa.VABC(ta.id, SUM(ta.a + tb.b - tc.c), ta.a, tb.b, tc.c)
FROM TableA ta JOIN ta.b tb JOIN tb.c tc
WHERE ta.id = tb.fid AND tb.id = tc.fid
GROUP BY ta.a, tb.b, tc.c
Criteria API
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<TableA> cq = cb.createQuery(TableA.class);
Root<TableA> ta = c.from(TableA.class);
Join<TableA, TableB> tb = ta.join("b");
Join<TableB, TableC> tc = tb.join("c");
cq.where(cb.and(
cb.equal(ta.get("id"), tb.get("fid")),
cb.equal(tb.get("id"), tc.get("fid"))
)
);
cq.groupBy(ta.get("a"), tb.get("b"), tc.get("c"));
cb.select(cb.construct(com.dreamer.jpa.VABC.class,
ta.get("id"),
cb.diff(cb.sum(ta.get("a"), tb.get("b")), tc.get("c")),
ta.get("a"),
tb.get("b"),
tc.get("c")
)
);
这些查询的结果类型是com.dreamer.jpa.VABC
类,可以定义如下:
package com.dreamer.jpa; //must conform the fully qualified name in the queries
public class VABC {
private int id;
private BigDecimal total;
private BigDecimal a;
private BigDecimal b;
private BigDecimal c;
public VABC(int id,BigDecimal total,BigDecimal a,BigDecimal b,BigDecimal c) {
this.id = id;
// ...
}
}
现在,在执行查询时,持久性提供程序(查询处理器)迭代查询结果,并且对于返回的每个表行,使用与表达式类型匹配的构造函数实例化com.dreamer.jpa.VABC
类的新实例在查询中列出。
这种方法有助于构建粗粒度DTO / VO以用于其他应用层。不是从结果列表中手动构造这样的对象,而是可以使用单个查询来检索完整的对象列表,即已经准备好推入表示层。
另一种选择是创建一个直接映射到现有数据库视图的实体。因此,您可以直接从映射视图中进行选择:
JPQL
SELECT v FROM VABC v
Criteria API
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<VABC> cq = cb.createQuery(VABC.class);
Root<VABC> ta = cq.from(VABC.class);
引用数据库视图的实体类可以定义如下:
@Entity
@Table(name = "V_ABC")
public class VABC {
private Long id;
private BigDecimal total;
private BigDecimal a;
private BigDecimal b;
private BigDecimal c;
public VABC(int id,BigDecimal total,BigDecimal a,BigDecimal b,BigDecimal c) {
this.id = id;
// ...
}
}
与构造函数表达式方法相比,将数据库视图直接映射到实体会产生更简洁的代码。在这种情况下,基于JPQL / Criteria的查询在查询数据库时会生成一个简单的SELECT id, total, a, b, c FROM V_ABC
。
这种方法还有助于构建粗粒度DTO / VO以用于其他应用层。单个查询返回表示V_ABC视图行的VABC实体实例的完整列表。