《你不知道的 JAVA》博客系列 💘 失传已久的 SQL Join 查询的独门秘籍

学会这款 全新设计的 Java 脚手架 ,从此面试不再怕!

Trump-mjga-logo.png slogan_long_2.png

从 Left Join 说起

假设你有这样一个 n2n 的关系表,代表用户和角色之间的关系。

table.png

通常通过 left join 去连接这三张表,来查询出用户及其角色的信息。

sql
复制代码
SELECT u.id AS user_id, u.name AS user_name, r.name AS role_name FROM "user" u LEFT JOIN "user_role_map" urm ON u.id = urm.user_id LEFT JOIN "role" r ON urm.role_id = r.id;
user_iduser_namerole_name
1AliceAdmin
1AliceUser
2BobUser
3CharlieGuest

查询出的结果中,Alice 这个用户出现了两次。这是显而易见的,因为这是一个 "Flatten" 的结果。

这样的结果是无法返回给客户端直接使用的。你需要进行处理,把重复的用户归纳到一起以后再返回给客户端进行展示,比如像下面这样:

user_iduser_nameuser's_role_array备注
1Alice[(Admin),(User<List>),(Vip<List>),...(n)]试想任意节点都可能嵌套高度为 n 的子树的情况,各节点需直接返回 List<Map> 的形式供前端在 html 的 <li></li><select></select> 节点中展示。
2BobUser
3CharlieGuest

不幸的是这样的处理非常麻烦,你 join 的表越多这个代码越不好写,不相信你可以试试。

Group_contact

看到这里,你可能会觉得 agg_string 和 group_contact 等聚合函数一定程度上能实现这个需求。但是聚合函数人如其名,作用为「聚合」。 回到上面的例子,不要局限于例子中树的高度,试想任意节点都可能嵌套一颗高度为 n 的子树,并且你的业务逻辑还需要对子树的节点做数据结构的转换。显然,字符串的「聚合」在解决这样复杂树结构的问题时显得力量不足。

这样的复杂树结构是否很常见?不,它不常见,但是它也不少见。因为除了互联网,还有很多行业也在使用数据库支撑他们的业务。

谈谈 ORM

有没有方便的方法来获取这个「嵌套」的结果呢?使用 Hibernate 这样的 ORM 框架是个不错的主意:

java
复制代码
@Entity public class User { @Id private int id; @Column(name = "name", nullable = false) private String name; @ManyToMany @JoinTable( name = "user_role_map", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles; } @Entity public class Role { @Id private int id; @Column(name = "name", nullable = false) private String name; @ManyToMany(mappedBy = "roles") private Set<User> users; }

Hibernate 直接帮你把数据库的结果映射到了嵌套结果集中。现在你可以直接把 List<User> 返回给客户端了,因为这个结果现在展示为:

user_iduser_nameuser's_role_array备注
1Alice[(Admin),(User<List>),(Vip<List>),...(n)]无论树的形状和高度,Hibernate 把各节直接映射为 List<Map> 的形式供前端在 html 的 <li></li><select></select> 节点中展示。
2BobUser
3CharlieGuest

Hibernate 的问题

使用 Hibernate 的代价就是你的心智负担很大。除了要学习很多注解以外,还有很多夸张的概念需要深入理解,才能够写出能正常运行的代码。

那么,有没有一种简单的方法,可以通过 SQL 的方式,直接查询出这种嵌套的结果集, 然后扔给客户端进行处理呢?答案是有的。

全新的解决方案

java
复制代码
public static void main(String[] args) { UserRoleEntity userRoleEntity = select( USER.ID, USER.NAME, array(select(ROLE.ID, ROLE.NAME) .from(ROLE) .join(USER_ROLE_MAP).on(ROLE.ID.eq(USER_ROLE_MAP.ROLE_ID)) .where(USER_ROLE_MAP.USER_ID.eq(USER.ID)) ).as("roles") ).from(USER); System.out.println(userRoleEntity); } class UserRoleEntity { private Long id; private String name; private List<Role> roles; }

是的,如你所见,使用 JOOQ,通过在 Java 的 main 方法里面用 Java 语言来编写「类型安全的 SQL」并通过 Array 方法一键转换为嵌套对象,免去了学习 Hibernate 的烦恼。

这么好的框架,在哪里可以下载? 到编程导航 https://www.codefather.cn/post/1873567361772089345 即可马上下载使用!

写在最后

我是 Chuck1sn,欢迎关注我的账号,今后我将更新更多 Jvm 生态相关的博客和文章。欢迎大家一起心态 open 的讨论技术、分享技术、热爱技术。

在编程导航学编程,就用 MJGA 脚手架!

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
Chuck1sn
下载 APP