写点什么

将设计模式应用到日常的 curd 中—分离关联查询

用户头像
LSJ
关注
发布于: 2020 年 07 月 13 日

看一个例子:在我们给客户端提供的接口中,有一个是要查询用户发布动态的列表。那么在接口的返回值中就需要包含 userName、*headPhoto* 这两个属性。一种做法是: 通过写一条关联查询(动态表和用户表)的sql语句,一次性返回客户端用到的所有信息。

关联查询带来的问题

数据库表之间的耦合

这个问题要具体场景具体分析。如果两张表处在不同的业务范围,那么就可能造成不同业务之间的耦合。如果在同一个业务范围内,则可以直接连表查询。其实在单体应用中也应该注重业务领域的划分。

代码复用度和复杂度

如果后续又有n个这种类型的接口,那么按照上面的做法,在每一个sql语句中都需要写一次关联用户表的sql语句。再或者查询用户信息的逻辑比较复杂,那这种复杂度势必会引入到不同的业务逻辑里。

性能的考虑

如果一条sql语句写的沉长复杂,就会难以维护又容易引发数据库性能问题

sql语句的分离

基于上面因素的考虑,我更倾向于第二种查询方式:



使用两条单独的查询语句,将查询到的结果放到代码中做数据组装。



这么做虽说开始麻烦一点,但却是好处多多。别小看这一简单的分离,却能够解决上面提到的三个问题。按照这种思路讲一下如何实现:

具体实现(Setter接口)

先来定义一个接口



public interface SetUserBaseInfoInterface {
Long getUserId();
void setUserName(String userName);
void setHeadPhoto(String headPhoto);
}



这个接口是为我们的实体类定义的。所有需要显示用户名和头像的实体类都可以实现此接口



其实这里也可以定义一个父类,然后让子类去继承这个类。只不过当时在我们项目中已经有多个类需要返回用户信息,但是在不同的类中命名却不统一,重构时又不能改变属性命名。所以只能用接口做适配了



此接口的目的就是要让赋值用户信息的操作变得更通用化。



修改实体类



现在有一个用于表示动态列表数据的实体类,然后需要实现SetUserBaseInfoInterface接口



@Data
public class Info implements SetUserBaseInfoInterface{
//自有属性 省略
private Long uId;private String uname;private String headImg;//用户属性
public void setHeadPhoto(String headPhoto) {this.headImg = headPhoto}
public void setUserName(String userName) {this.uname = userName;}
public Long getUserId() {return uId;}
}



注意到在Info中的属性的命名和接口中定义的方法名并不匹配,如果统一的话,其实只有加一个 @Data 注解就不需要去手写 SetUserBaseInfoInterface 接口的实现了



赋值操作



再看如何给实体赋值,我们可以在UserService中定义一个方法:如下



void setUserBaseInfo(List<? extends SettUserBaseInfoInterface> list) {
final Set<Long> userIdSet = list.stream()
.filter(o-> o.getUserId() != null)
.map(SetUserBaseInfoInterface::getUserId).collect(Collectors.toSet());
// 拿userIdSet去用户表做in操作 批量获取用户信息
Map<Long, UserBean> userMap = queryUser(userIdSet);
list.forEach(info->{
info.setUserName(userMap.get(info.getUserId()).getUserName());
info.setHeadPhoto(userMap.get(info.getUserId()).getHeadPhoto());
});
}



有了这个方法,以后任何想要显示用户信息的实体都可以实现 SetUserBaseInfoInterface 接口 然后调用 UserService.setUserBaseInfo 方法来设置属性值。所以业务代码大概可以这么写:



List<Info> list = query(req); //查询用户动态数据
userService.setUserBaseInfo(list);//赋值用户信息的操作被封装到UserService中



从此以后,只要涉及到查询用户名和头像的地方,都可以使用上面的代码,而不需要在xml中写既无聊有蹩脚的sql语句了。可以给它起个名字叫做: Setter接口。在不同的业务中,可以定义很多Setter接口。



发布于: 2020 年 07 月 13 日阅读数: 89
用户头像

LSJ

关注

微笑面对每一天 2018.11.11 加入

一个具有N年编程功力却早已拥有2N年工作经验的boy

评论 (1 条评论)

发布
用户头像
查询头像昵称可以加层缓存
2020 年 08 月 11 日 19:19
回复
没有更多了
将设计模式应用到日常的curd中—分离关联查询