zoukankan      html  css  js  c++  java
  • 从零一起学Spring Boot之LayIM项目长成记(三) 数据库的简单设计和JPA的简单使用。

    前言

      今天是第三篇了,上一篇简单模拟了数据,实现了LayIM页面的数据加载。那么今天呢就要用数据库的数据了。闲言少叙,书归正传,让我们开始吧。

    数据库

      之前有好多小伙伴问我数据库是怎么设计的。我个人用关系型数据库比较多,一般就是根据业务来分析,一对一的关系,一对多的关系,多对多的关系等,那么对于LayIM就根据这几个关系出发。而且先根据业务来设计。它初始化的数据我们都见过了,数据中分别包含以下四个部分。

    • 个人用户信息
    • 好友分组信息
    • 群组信息

      大部分业务都是围绕着用户转的,那么我们一条一条的分析。首先,个人用户信息不必说,这里我们就根据LayIM所需的用户字段设计就好,另外加上createAt字段或者其他字段都行。很简单,用户表SQL如下:

    CREATE TABLE `user` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `avatar` varchar(255) DEFAULT NULL,
      `sign` varchar(255) DEFAULT NULL,
      `user_name` varchar(255) DEFAULT NULL,
      `create_at` bigint(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=984389 DEFAULT CHARSET=utf8;

      好友分组信息,一个用户可以拥有多个分组,而每个分组下又可以有多个好友。 所以,维护好友关系需要两张表,分组表和用户(好友)分组关系表

    CREATE TABLE `friend_group` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `create_at` bigint(20) DEFAULT NULL,
      `name` varchar(255) DEFAULT NULL,
      `uid` bigint(20) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `FKawe6k1o9mujam11lxiwcwbqpu` (`uid`),
      CONSTRAINT `FKawe6k1o9mujam11lxiwcwbqpu` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=770369 DEFAULT CHARSET=utf8;

      群组信息,一个用户可以有多个群,一个群内有多个用户,他们是多对多的关系。所以维护这个关系需要两张表。群组表和群员关系表。

    CREATE TABLE `big_group` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `create_at` bigint(20) DEFAULT NULL,
      `avatar` varchar(255) DEFAULT NULL,
      `create_uid` bigint(20) DEFAULT NULL,
      `description` varchar(255) DEFAULT NULL,
      `group_name` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `FKdw1jlswinwe6gas1tqk1trgia` (`create_uid`),
      CONSTRAINT `FKdw1jlswinwe6gas1tqk1trgia` FOREIGN KEY (`create_uid`) REFERENCES `user` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

      剩下的两个表没有写,分别是 (分组,用户)关系表,(群组,用户)关系表。 没错,他们都是多对多的关系。你可以在张三的好友列表里,也可以在李四的好友列表。同理,你可以在多个群里。

    JPA-表的创建

      上面简单介绍了表的结构,虽然写出了SQL语句,但是呢在项目里我们并不会手动创建表。因为JPA会帮我们实现自动创建。如下图,我使用的是update,(即实体类变化等会重新修改表结构)

      

      下面就用户表写一个对应的类。(例)

    //加上@Entity注解
    @Entity
    public class UserInfo {
        //主键注解
        @Id
        @GeneratedValue
        @Column(nullable = false)
        private long id;
    
        //列 可以自定义列名,长度等,或者是否为空
        @Column(name = "user_name",length = 20)
        private String name;
    
    }

      运行程序,查看一下表,已经成功创建,没有问题。

      

      但是只是简单的表还不够,因为我们知道,表之间是有关系的,于是JPA中的关系注解就派上用场了。

    JPA-关系的映射

      之所以在上文中提到了一对多,多对多等词,其实就是为了在这里具体解释。JPA提供了关系注解。@OneToMany,@OneToOne,@ManyToOne,@ManyToMany等。下面我会根据LayIM所需要的关系一一讲解。

      首先,一个用户对应多个好友分组。而一个分组只属于某个(当前)用户。这里可以理解为用户与好友分组的关系是一对多,即 @OneToMany。代码中是这样体现的。

        //一对多,一个用户有多个好友分组
        @OneToMany(mappedBy = "user")
        private List<FriendGroup> friendGroups;

      在FriendGroup类中

       @ManyToOne
        @JoinColumn(name = "uid",nullable = false)
        private User user;

      其中呢,name ="uid" 代表,FriendGroup表中的uid字段对应的是User表中的主键(id)字段。也就是说,当我们查询一个User实体时,friendGroups字段会带上该用户下的好友分组。当然其中还涉及到了懒加载机制。(具体我也没研究,大体就是说,当调用 getFriendGroups()方法的时候才会根据关系从数据库查询相应的结果,这个后边会有体现)

      其次,LayIM加载中会把当前用户的群组信息直接加载出来。这里我们也可以直接用 @OneToMany实现,原理同上。(刚开始我做错了,我只是列出了该用户创建的群,没有考虑到用户加入的群)

      第三,@OneToOne 。这个注解呢在获取好友分组下的好友列表中用到了。比如,一个好友分组对应多个好友关系。那么每个好友关系从当前登录者的角度来说就是一对一的。这句话要怎么理解呢?就是说从程序上来讲,我们知道每个分组ID对应的多个UserId,因为表中存的是Id,但是我们取数据的时候需要把该用户的其他信息拿出来,那么使用@OneToOne注解,就会帮我们自动关联查询。代码如下:

        @OneToOne
        @JoinColumn(name="friend_uid")
        private User friend;

      代码中的friend_uid字段即用户id。(因为User类中的主键已经确定,所以不用再User类中增加 @OneToOne注解)

    代码实现

      (2017-11-7 日晚:此段代码已重构,为了写出我当时的思路,下文内容不会删掉,重构后代码讲解:http://www.cnblogs.com/panzi/p/7793854.html

      说了这么多,想必大家都有点晕了,下面就是代码实现了。在这里呢,我将拿好友分组举例,这个最简单。写一个单元测试,如下:

       public User getUser(Long userId){
            return userRepository.findOne(userId);
        }
       @Test
        public void testGetUserFriendGroups() {
            User user = userService.getUser(100000L);
            System.out.println("用户好友分组的个数为:"+user.getFriendGroups().size());
        }

      运行结果:

      

    小 tips 

      当你做单元测试的时候,会遇到懒加载没有Session的问题,如果遇到了,在字段上面添加 FetchType.EAGER即可

      @OneToMany(mappedBy = "user",fetch = FetchType.EAGER)

      但是程序运行可以不用加这个,可以在配置文件中增加open-in-view :true 解决

      

    JPA默认方法

      在讲解具体实现之前呢,还要介绍一下JpaRepository这个接口,这个玩意挺好玩的,除了默认的方法,还可以自定义比如 findBy,getBy,findBy..OrderBy..等等方法。这里只介绍当前用到的,至于没有用到的,有兴趣的同学可以自行研究。代码很简单,新建一个UserRepository继承自JpaRepository即可

    public interface UserRepository extends JpaRepository<User,Long> {
        
    }

      这样写了之后,UserRepository默认会有一些方法,增删改查或者分页。这里呢,我们没有用到分页,只是简单的查询。没错就是 findOne方法

      

    LayIM init接口实现

      好了,讲了这么多,可能大家都云里雾里的,没有关系,下面就进入LayIM init接口实现部分。当然作为初学者来说,我就使用了笨方法,为什么说笨方法呢,方法思路如下:

      

      方法笨就笨在里面的for循环。(应该是有优化的,后期在看)

      for(FriendGroup friendGroup : friendGroups) {
               List<RelationShip> relationShips = friendGroup.getRelationShips();
               List<UserViewModel> userViewModels = new ArrayList<UserViewModel>();
               //遍历 relationShips 获取userViewModels的集合
             for(RelationShip relationShip : relationShips){
                   UserViewModel userViewModel = LayimMapper.INSTANCE.mapUser(relationShip.getFriend());
                   userViewModels.add(userViewModel);
               }
               //获取主键
               Long friendGroupId = friendGroup.getId();
               for (FriendGroupViewModel viewModel : friends) {
                   if (viewModel.getId().equals(friendGroupId)) {
                       viewModel.setList(userViewModels);
                   }
               }
           }

      代码当中我使用了 LayimMapper,他依赖于一个实体转换工具:MapStruct。有兴趣大家自行去官网看看。不过我个人觉得有点类似代码生成器,因为在编译过后,target里面会多出一个类来。

      

      其实还是挺方便的,毕竟自己写也是这么写,这样还大大节约了时间。

      好了,本篇内容啰嗦了一大堆,我也不知道讲了些啥,总之就是我在学习之中的一些问题和开发思路。知识点很多,所以在我这里介绍的都是皮毛,要深追究下去,每个知识点都可以写上几篇。最后看一下运行效果。接口同上一篇的一样。

      

      对应数据库:

      

      

      

      后台执行的语句:

      

     总结

      本篇到此为止就结束啦,Jpa部分讲解的比较少,决定下一篇重点学习记录一下。这个基础接口实现了。LayIM还有一个getMembers接口,你是不是也会了呢?在我学习的过程中,包括关系注解,MapStruct应用和其他实践上自己给自己挖了很多坑,也出现过很多莫名奇妙的问题,有些东西自己动手了才知道。愉快的周末结束啦。我们下一篇见。

      下篇预告:从零一起学Spring Boot之LayIM项目长成记(四) Spring Boot  JPA 深入了解

  • 相关阅读:
    AGC037F Counting of Subarrays
    AGC025F Addition and Andition
    CF506C Mr. Kitayuta vs. Bamboos
    AGC032D Rotation Sort
    ARC101F Robots and Exits
    AGC032E Modulo Pairing
    CF559E Gerald and Path
    CF685C Optimal Point
    聊聊Mysql索引和redis跳表
    什么是线程安全
  • 原文地址:https://www.cnblogs.com/panzi/p/7788446.html
Copyright © 2011-2022 走看看