JPA如何处理视图?

时间:2014-04-29 15:24:42

标签: java-ee jpa

假设我们创建以下视图

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是否必须为视图执行上述整个查询,然后获取当前结果?

2 个答案:

答案 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实体实例的完整列表。