ssm实现多表联查之三表联查以及从po->vo的数据返回

引入

在实际项目的开发中,有时候需要对两个表以及多个表中的数据处理,这时处理方法其实也不只一种,我们可以分开好几次来查询,然后再整合数据,也可以用mybatis中的高级功能通过处理配置文件和pojo层的数据实现一次性的多表联查。


难点

1.其实在实现时,sql语句并不是很复杂,难点在于mybatis中对于ResultMap中配置文件的设置和pojo层数据的设置,这需要了解mybatis实例化pojo层对象的原理及过程。
2.当我们通过mybatis将需要的数据筛选出来之后,最重要的还是将数据封装成json数据返回到前端。但是在实际的项目需求中,往往一条返回的数据来自好几个数据库中表的数据整合,但又不全部包括,这时需要再建立一个专门返回到前端的对象bean供Controller层调用。


什么是vo,bo,pojo?

vo: view object

bo: business object

pojo: plain ordinary java object

理解这三种不同的javabean,首先要理解java分层思想中的Controller+Service +Dao三层的功能划分。
这三层的功能分别为:
Controller: 用来处理业务(Service)调度和管理跳转。执行的过程为:引用对应的Service层,结合SpringMVC注释,跳转到指定的页面,当然也能接受页面传递的请求数据,也可以做些计算处理.
Service: 用来管理具体功能。执行的过程为:引用对应的dao层的数据库操作。
Dao: 只完成数据库的增删改查,可以1对多,多对多,一对一(多对一)关联,模糊,动态,子查询。但是无论多么复杂的查询,dao只负责封装增删改查。对于具体如何增删改查,dao层是不管的。
了解了这三层的执行流程后,就可以分别对应理解vo,bo,pojo三者之间的关系:
ssm实现多表联查之三表联查以及从po->vo的数据返回_第1张图片
当业务处理较为复杂时,需要分别创建vo,bo和pojo来处理数据,但是当数据controller层和service层需要的数据差别不大时,有时候可以将vo供controller层和service层公用。而pojo则是一直在持久层被封装的数据,可以根据业务需,截取部分pojo中对于前端有意义的数据封装,也可以集合好几个pojo中有意义的数据封装起来一并返给前端。


情景引入

不管是微博还是pyq,我们经常会查看一个人的主页,比如现在正在开发一个社交类型的app,有一个需求是查看用户的主页。
以微博为例,一个人的主页信息包括些什么?

可以看到,一个用户的主页包含:用户的基本信息+用户发过的所有动态信息列表
用户的基本信息需要对应到用户表来反映,用户的每一条动态信息包含:用户头像,用户昵称,发表时间,动态内容,点赞数,评论数(转发数这里不予实现)。而动态内容包含:动态的文字内容,图片内容/视频内容。这里又涉及到两个表:动态信息表和动态内容表。
所以,这一个页面的呈现涉及到了是哪个表中的信息,首先要明确三个表之间的关联信息:一个用户对应多条动态(一对多),用户一条动态信息对应一个内容(一对一)。

怎样实现?

方法一:可以分别对用户表,动态信息表,动态内容表进行查询,这需要调用三个dao层的方法,然后自行将数据关联起来。该方法的优点是:不需要进行较麻烦的文件配置,可以直接查询并得到结果;缺点:需要多次调用dao层的方法,当业务逻辑更麻烦时,会比较费力。
方法二: 配置resultMap以及pojo层数据,进行三表联查。
该方法的优点: 只需调用一次dao层,并且得到的数据不需要在进行整合,数据之间的联系更加紧密;缺点: 配置文件的时候容易出错,resultMap中的数据必须和pojo层的数据一一对应,这样才能正确封装数据,不然很容易出错。(我就出错了好多次。。)
方法一不予讨论,这里主要给出方法二的过程:


实现过程

pojo层的相关类:(geter和seter方法省略)

1.User
说明:因为一个user对应多条动态(message),所以在User类中应加上List的属性。

public class User {
    private Integer id;

    private String username;

    private String headSculpture;

    private String signature;

    private Integer messageCount;

    private Integer fans;

    private Integer concern;

    private Integer readCount;

    private String studentId;

    private List messages;

    public List getMessages() {
        return messages;
    }

    public void setMessages(List messages) {
        this.messages = messages;
    }

    public User(Integer id, String username, String headSculpture, String signature, Integer messageCount, Integer fans, Integer concern, Integer readCount, String studentId) {
        this.id = id;
        this.username = username;
        this.headSculpture = headSculpture;
        this.signature = signature;
        this.messageCount = messageCount;
        this.fans = fans;
        this.concern = concern;
        this.readCount = readCount;
        this.studentId = studentId;
    }

    public User() {
    }
}

2.Message(动态信息表)
说明:每个动态都一对一对应一个内容,所以应该在Message类中加上Content(内容)属性

public class Message {
    private Integer id;

    private Integer userId;

    private Integer pageviews;

    private Integer praisePoints;

    private Integer commentCount;

    private Date time;

    private Boolean isDeleted;

    public Message(Integer id, Integer userId, Integer pageviews, Integer praisePoints, Integer commentCount, Date time) {
        this.id = id;
        this.userId = userId;
        this.pageviews = pageviews;
        this.praisePoints = praisePoints;
        this.commentCount = commentCount;
        this.time = time;
    }

    //导入一对一的对象类型
    private Content content;

  
    public Message(Integer id, Integer userId, Integer pageviews, Integer praisePoints, Integer commentCount, Date time, Boolean isDeleted) {
        this.id = id;
        this.userId = userId;
        this.pageviews = pageviews;
        this.praisePoints = praisePoints;
        this.commentCount = commentCount;
        this.time = time;
        this.isDeleted = isDeleted;
    }

    public Message() {
    }


}

3.Content(动态内容类)

public class Content {
    private Integer id;

    private Integer messageId;

    private String contentText;

    private String contentImages;

    private String contentVideos;


    public Content(Integer id, Integer messageId, String contentText, String contentImages, String contentVideos) {
        this.id = id;
        this.messageId = messageId;
        this.contentText = contentText;
        this.contentImages = contentImages;
        this.contentVideos = contentVideos;
    }

    public Content() {
      }
    }

一些注意事项:
在编写pojo层的类时,一定要加上无参构造器,因为mybatis在实例化对象时,需要通过先通过无参构造器来实例化对象,不然运行会报:NoSuchMethod异常。

mapper文件配置

相关标签:

: 一对多标签,用来表示集合
:一对一标签

resultMap中的配置:
UserMapper:


  
    
      
      
      
      
      
      
      
      
      
    
  


  

    
      
      
      
      
      
      
      
      
      
    
    
    
      
      
      
      
      
      
      

      
      
        
        
        
        
        
      
    

  

MessageMapper:


    
      
      
      
      
      
      
      
    
    
    
      
      
      
      
      
    

  

ContentMapper:


    
      
      
      
      
      
    
  

**注意:**在配置resultMap时,map中的属性必须和pojo层的属性一一对应!不能缺少也不能多,否则实例化对象会报错。

sql语句

  
    u.id,u.username, u.head_sculpture, u.signature,
    u. message_count,u. fans, u.concern, u.read_count,u.student_id
  

  
   m.id as mid, m.user_id,m.pageviews,m.praise_points, m.comment_count, m.time,m.is_deleted
  

  
  c.id,c.message_id,c.content_text,c.content_images,c.content_videos
  

  

注意:在编写sql语句时,由于涉及到了表之间额关联关系,在这个问题中,因为查看用户的主页时,某个用户可能存在动态,也可能不存在动态,但是用户的基本信息是必须展示的,所以,在关联用户表和动态表时需要进行左外连接。左外连接也就是返回主表的全部信息以及从表中符合要求的数据
补充:
ssm实现多表联查之三表联查以及从po->vo的数据返回_第2张图片
还有需要注意的是,因为动态信息和动态内容是高度关联的,即每个动态信息必然对应一个动态内容,因为一个人不能发送内容为空的动态。所以,二者是“共存亡的”,某一条动态一旦删除,则动态内容也应删除,所以这里可以设置外键来限制他们数据的更新。

pojo数据到vo数据的转换

首先需要创建一个vo类来封装主页信息的数据:

public class UserMainPageVo {

    private Integer userId;

    private String username;

    private String headSculpture;

    private String signature;

    private Integer messageCount;

    private Integer fans;

    private Integer concern;

//    private Integer readCount;

    private String studentId;

    private List messageVos;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getHeadSculpture() {
        return headSculpture;
    }

    public void setHeadSculpture(String headSculpture) {
        this.headSculpture = headSculpture;
    }

    public String getSignature() {
        return signature;
    }

    public void setSignature(String signature) {
        this.signature = signature;
    }

    public Integer getMessageCount() {
        return messageCount;
    }

    public void setMessageCount(Integer messageCount) {
        this.messageCount = messageCount;
    }

    public Integer getFans() {
        return fans;
    }

    public void setFans(Integer fans) {
        this.fans = fans;
    }

    public Integer getConcern() {
        return concern;
    }

    public void setConcern(Integer concern) {
        this.concern = concern;
    }

    public String getStudentId() {
        return studentId;
    }

    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }

    public List getMessageVos() {
        return messageVos;
    }

    public void setMessageVos(List messageVos) {
        this.messageVos = messageVos;
    }

    public UserMainPageVo(Integer userId, String username, String headSculpture, String signature, Integer messageCount, Integer fans, Integer concern, String studentId, List messageVos) {
        this.userId = userId;
        this.username = username;
        this.headSculpture = headSculpture;
        this.signature = signature;
        this.messageCount = messageCount;
        this.fans = fans;
        this.concern = concern;
        this.studentId = studentId;
        this.messageVos = messageVos;
    }

    public UserMainPageVo(){
        super();
    }
}

转化数据代码

private UserMainPageVo assembleUserVo(User user){
        UserMainPageVo userMainPageVo = new UserMainPageVo();
        List list = new ArrayList<>();
        MessageVo messageVo = null;
        //填充主页下的动态信息
        for(Message m: user.getMessages()){
            messageVo = new MessageVo();
            messageVo.setUserId(user.getId());
            messageVo.setUsername(user.getUsername());
            messageVo.setHeader(user.getHeadSculpture());
            messageVo.setTime(DateTimeUtil.dateToStr(m.getTime(),DateTimeUtil.STANDARD_FORMAT));
            messageVo.setCommentCount(m.getCommentCount());
            messageVo.setPraiseCount(m.getPraisePoints());
            messageVo.setMessageId(m.getId());
            if(m.getContent().getContentText()!=null){
                messageVo.setContentText(m.getContent().getContentText());
            }
            if(m.getContent().getContentImages()!=null){
                List imagesVo = new ArrayList<>();
               String[] images = m.getContent().getContentImages().split(",");
               for(int i =1;i

Controller层代码

  @ResponseBody
    @RequestMapping("get_info_and_message.do")
    public ServletResponse getUserInfoAndMessages(String studentId){
        if(StringUtils.isBlank(studentId)){
            return ServletResponse.createByErrorMessage("无效参数");
        }
        User user = iUserService.getUserInfoAndMessages(studentId).getData();
        UserMainPageVo userMainPageVo = assembleUserVo(user);

        return ServletResponse.createBySuccess(userMainPageVo);
    }


总结

当处理较为复杂的数据时,应该根据情况选择处理的方法,不管是多表联查还是分别查询,争取用最简单的方法将问题得到解决。

你可能感兴趣的:(java)