JPA查询语言(2)

JOINS

join出现在两个或多个实体联合查询产生一个JPQL查询结果。JPQL中join与SQL中的SQL相似。最后,要说明的是,所有的JPQL会转换成SQL查询。出现以下情况时,就可以用上join。

  • 访问集合关联字段的路径表达式出现在

    SELECT

    语句

  • join保留字出现在

    WHERE

    语句中

  • 定义两个或多个界定变量

如果你在一个查询中使用了一个以上的实体,你会获取所有实体的实例。这一结果称为Cartesian product(卡笛尔产品)。假使你的系统中有8个角色和4个用户,下面的查询会得到32个对象。

SELECT r, u FROM Role r, User u

你可能想用某种join来减少查询结果的数量。如果你想通过字段而不是通过主键来关联实体,你可以使用theta-join。例如:

SELECT t FROM Topic t, Forum f WHERE t.postCount = f.forumPostCount

这条查询语句返回的是与论坛回复(Forum)数相同的主题(Topic)。theta-join允许你关联那些没有显式关联关系或者关联到没有关系但是等同的信息的实体。

JPQL中inner join也是用于关系联合。其语法如下:

[INNER] JOIN join_association_path_expression [AS] identification_variable

INNER

AS

都是可选的。虽然它们对查询没有什么影响,你还是可以使用它们来更加清晰的表达你的意图。

join_assocation_path_expression

的意思是你可以访问一个关联实体,不管是单值关联还是一个集合。下图演示了两个inner join查询:

图 3. 

因为你不能在

SELECT

语句中使用一个集合关联字段,JPQL为你提供了

INNER JOIN

操作符。如果你想访问

Forum-Topic-Post

的关联关系(如图2所示),获得所有Post的标题,你可以使用这样一条查询语句:

SELECT p.title FROM Forum f JOIN f.topics AS t JOIN t.posts AS p

这条查询会返回0个或多个

String

对象,封装的是

Forum-Topic-Post

联合查询的回复的标题。

一个outer join会返回一个实体的所有实例和其它与join criteria匹配的其它实体的实例。一个left join的语法如下:

LEFT [OUTER] JOIN join_association_path_expression [AS] identification_variable

[OUTER]是可选的,因为在JPQL中

LEFT JOIN

LEFT OUTER JOIN

以认为是等同的。使用上图中所示的

Forum/Post

实体关系,下面的left join操作可以读取所有的

Forum

和任何与

Forum

关联的

Topic

。如果找不到

Topic

Object

数组的第2项的值就是null。

SELECT f, t FROM Forum f LEFT JOIN f.topics t

使用范例数据库,上面的查询会返回以下结果:

[ Object: [Forum] Object: [Topic] ]

[ Object: [Forum] Object: [null] ]

[ Object: [Forum] Object: [null] ]

[ Object: [Forum] Object: [null] ]

[ Object: [Forum] Object: [null] ]

这个查询返回了所有的

Forum

实例和一个唯一的

Topic

,因为仅有一个

Forum

有一个

Topic

(看起来留言板需要更多的用户参与)。

由于

LEFT JOIN

操作符是预读取的一种有效方式,JPA还提供了

FETCH JOIN

操作。

FETCH JOIN

操作符在下一节介绍。

fetch join允许你创建查询来预读取另外一种lazy关联关系。如果你了解到在所有的实体读取之后并可能处于脱管状态时就有必要使用

LAZY

关联关系,你就可以使用

FETCH JOIN

来读取这种关系。

FETCH JOIN

如下:

[LEFT [OUTER] INNER] JOIN FETCH join_association_path_expression

和前面定义不同的是,

FETCH JOIN

没有范围变量,因为你不能在查询语句中使用隐式引用的实体。下面的查询会读取任何有主题(

Topic

)的论坛(

Forum

)实体,仅仅那有主题(

Topic

)的论坛(

Forum

)实体才会被读取。下面代码演示了如何定义论坛(

Forum

)实体。注意与主题(

Topic

)的关联关系是lazy。

@Entity

public class Forum extends ModelBase implements Serializable {

@OneToMany( fetch=FetchType.LAZY,

cascade={CascadeType.PERSIST,

CascadeType.MERGE},

mappedBy="forum")

@OrderBy("type asc, dateUpdated desc")

Set<Topic> topics = new HashSet<Topic>();

// ...

}

这里有一条查询语句:

SELECT DISTINCT f FROM Forum f JOIN FETCH f.topics

因为数据库中有5个Forum实例,但仅有一个有Topic。上面的查询语句会返回一个Forum实例,Topic的关联关系也立即读取。如果你不使用

DISTINCT

,持久化实现方案(persistence provider)会为系统中每个返回一个Forum实例。使用了

DISTINCT

,重复的实例会被删除。

要读取所有的论坛(

Forum

)实例,并立即读取它们的主题(

Topic

),如下在的查询语句所示,

SELECT DISTINCT f FROM Forum f LEFT JOIN FETCH f.topics

这条返回所有唯一的论坛(

Forum

)实例,并且所有的主题(

topics

)已经预先读取了。使用

JOIN FETCH

的一个缺点是需要知道对象模型。一旦你知道关联关系的类型,就可以用

JOIN FETCH

优化你的查询。

查询中的

WHERE

语句是由条件表达式组成,由它决定返回的实体。只要你将使用

GROUP BY

的字段放到

SELECT

语句中,你可以用

GROUP BY

来统计查询结果。你还可以用

HAVING

过滤查询结果。JPA并没有要求持久化实现在不使用

GROUP BY

支持

HAVING

,可以保持可移植性,你最好不要在

GROUP BY

外使用

HAVING

图 4. 

Post/User

对象关系图

现在假设你想知道创建的

agoraBB

系统中每个用户的发帖数量。从上图中可以看,没有从

User

Post

的关联关系。因为

User

Post

继承

ModelBase MappedSuperclass

的,我们知道每个

Post

对象有一个

createdByUser

updatedByUser

字段。

使用inner join的话,你可以写查询语句:

SELECT count(p) From Post p JOIN p.createdByUser u

问题是,这条查询会返回所有的由

createdByUser

字段指定用户创建的所有帖子的数量。如果你想知道每个用户创建了多少帖子,你必须使用

GROUP BY

操作符:

SELECT u, count(p) From Post p JOIN p.createdByUser u GROUP BY u

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值