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允许你关联那些没有显式关联关系或者关联到没有关系但是等同的信息的实体。

INNER JOIN

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

 

[INNER] JOIN join_association_path_expression [AS] identification_variable				

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

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 联合查询的回复的标题。

LEFT OUTER JOIN

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

 

LEFT [OUTER] JOIN join_association_path_expression [AS] identification_variable					

[OUTER]是可选的,因为在JPQLLEFT JOINLEFT OUTER JOIN 以认为是等同的。使用上图中所示的Forum/Post 实体关系,下面的left join操作可以读取所有的Forum 和任何与Forum 关联的Topic 。如果找不到TopicObject 数组的第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

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 对象关系图

Post/User对象关系图


现在假设你想知道创建的agoraBB 系统中每个用户的发帖数量。从上图中可以看,没有从UserPost 的关联关系。因为UserPost 继承ModelBase MappedSuperclass 的,我们知道每个Post 对象有一个createdByUserupdatedByUser 字段。

使用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	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值