前言:由于导师在我的毕设项目里加了消息系统(本来想水水就过的..),没办法...来稍微研究研究吧..简单简单...

需求分析

我的毕设是一个博客系统,类似于简书这样的,所以消息系统也类似,在用户的消息里包含了有:喜欢和赞、评论、关注、私信这样的一类东西,这样的一个系统应该包含以下的功能:

    1. 当用户评论/关注/点赞时能够通知到被评论/关注/点赞的用户,并生成像如下格式的提示信息(允许取消关注/点赞但不收到通知):

    我没有 关注了 你

    三颗 喜欢了你的文章 《Java消息系统简单设计与实现》

    心脏 评论了你的文章 《Java消息系统简单设计与实现》

    1. 用户之间能够发送/接受私信,不需要像QQ那样建立长连接实现实时通信,但刷新列表能看到新消息,并且界面类似QQ聊天界面一左一右,允许删除私信
    1. 管理员能发送通告,其实就像是用管理员的账号给每一个用户发送私信;
    1. 可以查看关注的用户最新发表的文章,得到类似推送的效果;
    1. 所有消息当然也要标注好消息已读or未读,登录就能得到消息提醒标识好有多少未读消息,像是QQ消息右上角的小红点那样类似;

OK,大致就是以上的功能,那么问题来了:这要怎么设计啊?

进一步分析

其实可以根据上面的需求分析,把上面的消息大致可以分为公告(Announcement)、提醒(Remind)、私信(Message)三类,我们可以大致抽象出一个 通知(Notify) 模型:

发送者 接受者 信息类型 动作类型 通知内容 是否已读 消息创建时间
粉丝1号 我没有三颗心脏 提醒 关注 粉丝1号 关注了 你 xx:xx:xx
粉丝1号 我没有三颗心脏 提醒 喜欢和赞 粉丝1号 喜欢了你的文章 《Java消息系统简单设计与实现》 xx:xx:xx
粉丝1号 我没有三颗心脏 提醒 评论 粉丝1号 评论了你的文章 《Java消息系统简单设计与实现》 xx:xx:xx
粉丝2号 我没有三颗心脏 私信 你收到了来自 粉丝2号 的 1 条私信 xx:xx:xx

上面加了一些数据以便理解,不过话说粉丝1号果然是真爱粉,又关注又喜欢又评论,嘻嘻嘻嘻...

emm.这样的模型能够胜任我们的工作吗?我也不知道..不过根据这个模型能够想出大概的这样的创建通知的逻辑:

似乎看上去也没有什么大问题..不过既然消息内容都可以根据动作类型自动生成的了,加上私信和公告的内容因为长度问题也肯定不保存在这张表里的好,所以我们在设计数据库时干脆把通知内容这条去掉不要,当信息类型是公告或者私信时可以根据这条通知的 id 在相应的表中找到相应的数据就可以了,emm..我觉得可以

顺下去想想其实脑中有了一个大概,这样的模型还容易设计和想到,其实主要的问题还是下面的那些

问题一:单表数据大了怎么办?

如果当用户量上去到一定量的时候,那么这张 通知表 势必会变得巨大,因为不管是我们的公告、提醒还是私信都会在这个通知表上创建一条数据,到时候就会面临查询慢的问题,问题的答案是:我也不知道..

所以我们的规定是:不考虑像简书这样超大用户量,能够应付毕设就好啦..简单设计,嘻嘻嘻..不过也不要太不相信MySQL的性能,还是有一定容纳能力的!

问题二:用户要怎样正确得到自己的未读消息呢?

暴力一点方法是,反正通知表里有用户所有的消息,直接读取完,然后通过是否已读字段就能够找到正确的所有未读消息了,这..这么简单吗?

其实有思考过使用时间或者另建一张保存有最新已读到哪条消息的表,但用户可以选择有一些读有一些不读,这两个似乎都很难达到目的...还是暴力吧

问题三:私信消息该怎么设计?

发送者 接受者 内容 发送时间
粉丝1号 我没有三颗心脏 我是你的真爱粉啊!我要给你生猴子! 2019年1月7日11:34:23
我没有三颗心脏 粉丝1号 已阅...下一个... 2019年1月7日11:34:53

就像 QQ消息 一样嘛,包含一个内容、时间、发送者和接受者,然后前端直接根据时间或者 id 排序生成一左一右的消息对话框,不过比较特殊的一点就是私信是一个双向交流的过程,在一个对话框中我可能既是接受者也是发送者,这也无所谓嘛,稍微分析分析场景:

  • 读取私信列表时:按照接受者和发送者一起查询的原则,也就是查询接受者是自己和发送者是自己的数据,然后根据时间和已读未读来建立私信列表;
  • 读取私信时:这时已经有了明确的接受者和发送者,那就查询所有 发送者是对方接受者是自己 Or 发送者是自己接受者是对方 的数据,然后在前端拼凑出一左一右的聊天框;
  • 发送私信时:先查询之前是否有记录,然后同上建立聊天框,点击发送之后把发送方设为自己接收方设为私信对象,然后在通知表中新建一条未读数据通知私信对象有私信来了;

这完全能满足要求,只不过感觉查询多了些..

数据库设计

简单弄了弄弄..看着挺难受的,不过能简单实现功能,并且为了演示,这里是做了一张user_follow表,表示用户之间的关联关系,点赞和评论与这个类似,就不多弄了..下面给一下建表语句吧:

user表:

  1. CREATE TABLE `user` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `username` varchar(50) NOT NULL COMMENT '用户姓名',
  4. PRIMARY KEY (`id`)
  5. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

user_follow表:

  1. CREATE TABLE `user_follow` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `uid` bigint(20) NOT NULL COMMENT '用户ID',
  4. `follow_uid` bigint(20) NOT NULL COMMENT '关注的用户id',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户关注表,记录了所有用户的关注信息';

notify表:

  1. CREATE TABLE `notify` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `sender_id` bigint(20) NOT NULL COMMENT '发送者用户ID',
  4. `reciver_id` bigint(20) NOT NULL COMMENT '接受者用户ID',
  5. `type` varchar(50) NOT NULL COMMENT '消息类型:announcement公告/remind提醒/message私信',
  6. `is_read` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已读,0未读,1已读',
  7. `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间:按当前时间自动创建',
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户通知表,包含了所有用户的消息';

message表:

  1. CREATE TABLE `message` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `notify_id` bigint(20) NOT NULL COMMENT '对应通知消息的id',
  4. `sender_id` bigint(20) NOT NULL COMMENT '发送者用户ID',
  5. `reciver_id` bigint(20) NOT NULL COMMENT '接受者用户ID',
  6. `content` varchar(1000) NOT NULL COMMENT '消息内容,最长长度不允许超过1000',
  7. `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间:按当前时间自动创建',
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='私信信息表,包含了所有用户的私信信息';

根据《Java开发手册》5.3 第六条 没有使用任何级联和外键,bingo!

Spring Boot + MyBatis 实例

第一步:基础环境搭建

SpringBoot项目怎么搭就不说了吧,给一给几个关键的配置文件:

pom包依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.mybatis.spring.boot</groupId>
  7. <artifactId>mybatis-spring-boot-starter</artifactId>
  8. <version>1.3.2</version>
  9. </dependency>
  10. <!-- SpringBoot - MyBatis 逆向工程 -->
  11. <dependency>
  12. <groupId>org.mybatis.generator</groupId>
  13. <artifactId>mybatis-generator-core</artifactId>
  14. <version>1.3.6</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>mysql</groupId>
  18. <artifactId>mysql-connector-java</artifactId>
  19. <scope>5.1.18</scope>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-test</artifactId>
  24. <scope>test</scope>
  25. </dependency>

这里有一个巨坑,耗费了我好半天的时间,不知道为什么我明明引入的 5.1.18 版本的 mysql-connector-java,可 Maven 就是非要给我比较新版本的 8.0.13,这导致了在我使用 MyBatis 逆向工程生成 domain 和 mapper 的过程中出现了以下的问题:

  • 1、提示我数据库连接的驱动名称需要改成com.mysql.cj.jdbc.Driver而不是之前的com.mysql.jdbc.Driver,不然就报错:

Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class is com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

  • 2、还需要设置 mysql 的时区,也就是需要将connectionURL属性写成"jdbc:mysql://localhost:3306/test?serverTimezone=UTC"。如果不指定serverTimezone=UTC(还必须大写),将报错:

java.sql.SQLException: The server time zone value '?й???????' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

  at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:695)

   at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:663)

  • 3、逆向工程会去找 MySQL 其他库的相同表名的表,然后生成一堆乱七八糟的东西,还由于找不到主键 id 生成了只含 inser() 方法而不含删除、更新方法的 Mapper 文件;

解决方法就只有自己手动去调低 mysql-connector-java 的版本到 5.xx,还找到一个跟我情况类似:https://blog.csdn.net/angel_xiaa/article/details/52474022

application.properties:

  1. ## 数据库连接配置
  2. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
  3. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/message_system?characterEncoding=UTF-8
  4. spring.datasource.username=root
  5. spring.datasource.password=123456
  6. ## MyBatis相关配置
  7. mybatis.type-aliases-package=com.wmyskxz.demo.messagesystem.domain
  8. mybatis.mapper-locations=classpath:mapper/*.xml

在启动类上加上注解:

  1. @EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
  2. @MapperScan("com.wmyskxz.demo.messagesystem.dao")
  3. @SpringBootApplication
  4. public class MessageSystemApplication {
  5. ....
  6. }

第二步:MyBatis 逆向工程

新建【util】包,在下面新建两个类:

MybatisGenerator类:

  1. public class MybatisGenerator {
  2. public static void main(String[] args) throws Exception {
  3. String today = "2019-1-7";
  4. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
  5. Date now = sdf.parse(today);
  6. Date d = new Date();
  7. if (d.getTime() > now.getTime() + 1000 * 60 * 60 * 24) {
  8. System.err.println("——————未成成功运行——————");
  9. System.err.println("——————未成成功运行——————");
  10. System.err.println("本程序具有破坏作用,应该只运行一次,如果必须要再运行,需要修改today变量为今天,如:" + sdf.format(new Date()));
  11. return;
  12. }
  13. if (false)
  14. return;
  15. List<String> warnings = new ArrayList<String>();
  16. boolean overwrite = true;
  17. InputStream is = MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();
  18. ConfigurationParser cp = new ConfigurationParser(warnings);
  19. Configuration config = cp.parseConfiguration(is);
  20. is.close();
  21. DefaultShellCallback callback = new DefaultShellCallback(overwrite);
  22. MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
  23. myBatisGenerator.generate(null);
  24. System.out.println("生成代码成功,只能执行一次,以后执行会覆盖掉mapper,pojo,xml 等文件上做的修改");
  25. }
  26. }

OverIsMergeablePlugin类:

  1. /**
  2. * 解決 MyBatis 逆向工程重复生成覆盖问题的工具类
  3. */
  4. public class OverIsMergeablePlugin extends PluginAdapter {
  5. @Override
  6. public boolean validate(List<String> warnings) {
  7. return true;
  8. }
  9. @Override
  10. public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
  11. try {
  12. Field field = sqlMap.getClass().getDeclaredField("isMergeable");
  13. field.setAccessible(true);
  14. field.setBoolean(sqlMap, false);
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. return true;
  19. }
  20. }

在【resrouces】资源文件下新建逆向工程配置文件【generatorConfig.xml】:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE generatorConfiguration
  3. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  4. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
  5. <generatorConfiguration>
  6. <context id="DB2Tables" targetRuntime="MyBatis3">
  7. <!--避免生成重复代码的插件-->
  8. <plugin type="com.wmyskxz.demo.messagesystem.util.OverIsMergeablePlugin"/>
  9. <!-- 是否去除自动生成的代码中的注释 true:是 false:否-->
  10. <commentGenerator>
  11. <property name="suppressDate" value="true"/>
  12. <property name="suppressAllComments" value="true"/>
  13. </commentGenerator>
  14. <!--数据库链接地址账号密码-->
  15. <jdbcConnection driverClass="com.mysql.jdbc.Driver"
  16. connectionURL="jdbc:mysql://localhost:3306/message_system?serverTimezone=UTC"
  17. userId="root" password="123456">
  18. </jdbcConnection>
  19. <!-- 默认 false,把 JDBC DECIMAL 和 NUMERIC 类型解析为 Integer
  20. 为 true 时解析为 java.math.BigDecimal -->
  21. <javaTypeResolver>
  22. <property name="forceBigDecimals" value="false"/>
  23. </javaTypeResolver>
  24. <!--生成pojo类存放位置-->
  25. <javaModelGenerator targetPackage="com.wmyskxz.demo.messagesystem.domain" targetProject="src/main/java">
  26. <!-- enableSubPackages:是否让 schema 作为包的后缀-->
  27. <property name="enableSubPackages" value="true"/>
  28. <!-- trimStrings:从数据库返回的值被清理前后的空格 -->
  29. <property name="trimStrings" value="true"/>
  30. <!-- 是否对model添加 构造函数 -->
  31. <property name="constructorBased" value="true"/>
  32. </javaModelGenerator>
  33. <!--生成xml映射文件存放位置-->
  34. <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
  35. <property name="enableSubPackages" value="true"/>
  36. </sqlMapGenerator>
  37. <!--生成mapper类存放位置-->
  38. <javaClientGenerator type="XMLMAPPER" targetPackage="com.wmyskxz.demo.messagesystem.dao"
  39. targetProject="src/main/java">
  40. <property name="enableSubPackages" value="true"/>
  41. </javaClientGenerator>
  42. <!--生成对应表及类名
  43. tableName:要生成的表名
  44. domainObjectName:生成后的实例名
  45. enableCountByExample:Count语句中加入where条件查询,默认为true开启
  46. enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
  47. enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
  48. enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
  49. selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
  50. -->
  51. <table tableName="user" domainObjectName="User" enableCountByExample="false"
  52. enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
  53. selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
  54. <property name="my.isgen.usekeys" value="true"/>
  55. <property name="useActualColumnNames" value="false"/>
  56. <generatedKey column="id" sqlStatement="JDBC"/>
  57. </table>
  58. <table tableName="notify" domainObjectName="Notify" enableCountByExample="false"
  59. enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
  60. selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
  61. <property name="my.isgen.usekeys" value="true"/>
  62. <property name="useActualColumnNames" value="false"/>
  63. <generatedKey column="id" sqlStatement="JDBC"/>
  64. </table>
  65. <table tableName="user_follow" domainObjectName="UserFollow" enableCountByExample="false"
  66. enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
  67. selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
  68. <property name="my.isgen.usekeys" value="true"/>
  69. <property name="useActualColumnNames" value="false"/>
  70. <generatedKey column="id" sqlStatement="JDBC"/>
  71. </table>
  72. <table tableName="message" domainObjectName="Message" enableCountByExample="false"
  73. enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
  74. selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
  75. <property name="my.isgen.usekeys" value="true"/>
  76. <property name="useActualColumnNames" value="false"/>
  77. <generatedKey column="id" sqlStatement="JDBC"/>
  78. </table>
  79. </context>
  80. </generatorConfiguration>

运行我们的【MybatisGenerator】类中的 main 方法就能看到自动生成的实体、Xml文件以及 Mapper 类

第三步:Service 层

不给接口了,直接给实现吧,方法都很简单,而且没有做任何的安全限制,只是为了实现简单的消息系统,看效果

UserServiceImpl:

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Resource
  4. UserMapper userMapper;
  5. @Override
  6. public void addUserByUsername(String username) {
  7. userMapper.insert(new User(null, username));// 主键自增长.
  8. }
  9. @Override
  10. public User findUserById(Long id) {
  11. return userMapper.selectByPrimaryKey(id);
  12. }
  13. }

UserFollowServiceImpl:

  1. @Service
  2. public class UserFollowServiceImpl implements UserFollowService {
  3. @Resource
  4. UserFollowMapper userFollowMapper;
  5. @Autowired
  6. NotifyService notifyService;
  7. @Override
  8. public void userAFollowUserBById(Long userAId, Long userBId) {
  9. // 先要创建一条提示消息
  10. notifyService.addNotify(userAId, userBId, "follow");// 关注信息
  11. UserFollow userFollow = new UserFollow();
  12. userFollow.setUid(userAId);
  13. userFollow.setFollowUid(userBId);
  14. userFollowMapper.insertSelective(userFollow);
  15. }
  16. @Override
  17. public void userAUnfollowUserBById(Long userAId, Long userBId) {
  18. // 首先查询到相关的记录
  19. UserFollowExample example = new UserFollowExample();
  20. example.or().andUidEqualTo(userAId).andFollowUidEqualTo(userBId);
  21. UserFollow userFollow = userFollowMapper.selectByExample(example).get(0);
  22. // 删除关注数据
  23. userFollowMapper.deleteByPrimaryKey(userFollow.getId());
  24. }
  25. }

NotifyServiceImpl:

  1. @Service
  2. public class NotifyServiceImpl implements NotifyService {
  3. @Resource
  4. NotifyMapper notifyMapper;
  5. @Override
  6. public int addNotify(Long senderId, Long reciverId, String type) {
  7. Notify notify = new Notify(null, senderId, reciverId, type, false, null);
  8. return notifyMapper.insertSelective(notify);// id和creatTime自动生成.
  9. }
  10. @Override
  11. public void readNotifyById(Long id) {
  12. Notify notify = notifyMapper.selectByPrimaryKey(id);
  13. notify.setIsRead(true);
  14. notifyMapper.updateByPrimaryKey(notify);
  15. }
  16. @Override
  17. public List<Notify> findAllNotifyByReciverId(Long id) {
  18. List<Notify> notifies = new LinkedList<>();
  19. NotifyExample example = new NotifyExample();
  20. example.setOrderByClause("`id` DESC");// 按id倒叙,也就是第一个数据是最新的.
  21. example.or().andReciverIdEqualTo(id);
  22. notifies.addAll(notifyMapper.selectByExample(example));
  23. return notifies;
  24. }
  25. @Override
  26. public List<Notify> findAllUnReadNotifyByReciverId(Long id) {
  27. List<Notify> notifies = new LinkedList<>();
  28. NotifyExample example = new NotifyExample();
  29. example.setOrderByClause("`id` DESC");// 按id倒叙,也就是第一个数据是最新的.
  30. example.or().andReciverIdEqualTo(id).andIsReadEqualTo(false);
  31. notifies.addAll(notifyMapper.selectByExample(example));
  32. return notifies;
  33. }
  34. }

MessageServiceImpl:

  1. @Service
  2. public class MessageServiceImpl implements MessageService {
  3. @Resource
  4. MessageMapper messageMapper;
  5. @Resource
  6. NotifyService notifyService;
  7. @Override
  8. public void addMessage(Long senderId, Long reciverId, String content) {
  9. // 先创建一条 notify 数据
  10. Long notifyId = (long) notifyService.addNotify(senderId, reciverId, "message");// message表示私信
  11. // 增加一条私信信心
  12. Message message = new Message(null, notifyId, senderId, reciverId, content, null);
  13. messageMapper.insertSelective(message);// 插入非空项,id/createTime数据库自动生成
  14. }
  15. @Override
  16. public void deleteMessageById(Long id) {
  17. messageMapper.deleteByPrimaryKey(id);
  18. }
  19. @Override
  20. public Message findMessageByNotifyId(Long id) {
  21. // 触发方法时应把消息置为已读
  22. notifyService.readNotifyById(id);
  23. MessageExample example = new MessageExample();
  24. example.or().andNotifyIdEqualTo(id);
  25. return messageMapper.selectByExample(example).get(0);
  26. }
  27. }

第四步:Controller 层

也很简单,只是为了看效果

UserController:

  1. @RestController
  2. public class UserController {
  3. @Autowired
  4. UserService userService;
  5. @PostMapping("/addUser")
  6. public String addUser(@RequestParam String username) {
  7. userService.addUserByUsername(username);
  8. return "Success!";
  9. }
  10. @GetMapping("/findUser")
  11. public User findUser(@RequestParam Long id) {
  12. return userService.findUserById(id);
  13. }
  14. }

UserFollowController :

  1. @RestController
  2. public class UserFollowController {
  3. @Autowired
  4. UserFollowService userFollowService;
  5. @PostMapping("/follow")
  6. public String follow(@RequestParam Long userAId,
  7. @RequestParam Long userBId) {
  8. userFollowService.userAFollowUserBById(userAId, userBId);
  9. return "Success!";
  10. }
  11. @PostMapping("/unfollow")
  12. public String unfollow(@RequestParam Long userAId,
  13. @RequestParam Long userBId) {
  14. userFollowService.userAUnfollowUserBById(userAId, userBId);
  15. return "Success!";
  16. }
  17. }

NotifyController :

  1. @RestController
  2. public class NotifyController {
  3. @Autowired
  4. NotifyService notifyService;
  5. @PostMapping("/addNotify")
  6. public String addNotify(@RequestParam Long senderId,
  7. @RequestParam Long reciverId,
  8. @RequestParam String type) {
  9. notifyService.addNotify(senderId, reciverId, type);
  10. return "Success!";
  11. }
  12. @PostMapping("/readNotify")
  13. public String readNotify(@RequestParam Long id) {
  14. notifyService.readNotifyById(id);
  15. return "Success!";
  16. }
  17. @GetMapping("/listAllNotify")
  18. public List<Notify> listAllNotify(@RequestParam Long id) {
  19. return notifyService.findAllNotifyByReciverId(id);
  20. }
  21. @GetMapping("/listAllUnReadNotify")
  22. public List<Notify> listAllUnReadNotify(@RequestParam Long id) {
  23. return notifyService.findAllUnReadNotifyByReciverId(id);
  24. }
  25. }

MessageController :

  1. @RestController
  2. public class MessageController {
  3. @Autowired
  4. MessageService messageService;
  5. @PostMapping("/addMessage")
  6. public String addMessage(@RequestParam Long senderId,
  7. @RequestParam Long reciverId,
  8. @RequestParam String content) {
  9. messageService.addMessage(senderId, reciverId, content);
  10. return "Success!";
  11. }
  12. @DeleteMapping("/deleteMessage")
  13. public String deleteMessage(@RequestParam Long id) {
  14. messageService.deleteMessageById(id);
  15. return "Success!";
  16. }
  17. @GetMapping("/findMessage")
  18. public Message findMessage(@RequestParam Long id) {
  19. return messageService.findMessageByNotifyId(id);
  20. }
  21. }

第五步:测试

通过 REST 测试工具,可以看到正确的效果,这里就不给出所有的测试了。

总结

以上的项目简单而且没有任何的安全验证,不过能够基本完成我们的需求,还有一些功能没有实现,例如管理员发通告(上面只演示了私信和关注信息),按照上面的系统就直接暴力给每个用户都加一条通知消息,感觉有点自闭..我也不知道怎么设计好..希望有经验的大大能指条路啊!

其实关于这个简单的系统我查了好多好多资料..把自己都看自闭了,后来我干脆把所有网页都关掉,开始用 JPA 自己开始抽象实体,把各个实体写出来并把所有实体需要的数据啊相互之间的关联关系啊写清楚,然后再从自动生成的数据库中找思路...hhh...要不是我 JPA 不是很熟我觉得用 JPA 就能写出来了,不用 JPA 的原因在于一些数据的懒加载不知道怎么处理,还有就是查询语句太复杂,免不了要浪费一些资源...emmm..说到底还是不是特别懂 JPA,下面给一张复杂的用 JPA 建立的 User 实体吧(随手截的..hhh...很乱..):


按照惯例黏一个尾巴:

欢迎转载,转载请注明出处!

简书ID:@我没有三颗心脏

github:wmyskxz

欢迎关注公众微信号:wmyskxz

分享自己的学习 & 学习资料 & 生活

想要交流的朋友也可以加qq群:3382693

Java消息系统简单设计与实现的更多相关文章

  1. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

  2. java联系人管理系统简单设计

    本文实例为大家分享了java联系人管理系统毕业设计,供大家参考,具体内容如下 要求:  请使用XML保存数据,完成一个联系人管理系统.      用户必须经过认证登录后方可以使用系统.      注册 ...

  3. Java登录界面简单设计

    package cn.com.view; import java.awt.Color; import java.awt.Font; import java.awt.SystemColor; impor ...

  4. 分布式开放消息系统(RocketMQ)的原理与实践

    分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一 ...

  5. 使用Vert.x构建Web服务器和消息系统

    如果你对Node.js感兴趣,Vert.x可能是你的下一个大事件:一个建立在JVM上一个类似的架构企业制度. 这一部分介绍Vert.x是通过两个动手的例子(基于Vert.x 2.0). 当Node.j ...

  6. 分布式开放消息系统(RocketMQ)的原理与实践(转)

    转自:http://www.jianshu.com/p/453c6e7ff81c 分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回 ...

  7. Kafka不只是个消息系统

    作者丨 Jay Kreps Confluent 联合创始人兼 CEO Jay Kreps 发表了一篇博文,给出了 Kafka 的真正定位——它不只是个消息系统,它还是个存储系统,而它的终极目标是要让流 ...

  8. 分布式开放消息系统RocketMQ的原理与实践(消息的顺序问题、重复问题、可靠消息/事务消息)

    备注:1.如果您此前未接触过RocketMQ,请先阅读附录部分,以便了解RocketMQ的整体架构和相关术语2.文中的MQServer与Broker表示同一概念 分布式消息系统作为实现分布式系统可扩展 ...

  9. 迄今为止最硬核的「Java8时间系统」设计原理与使用方法

    为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...

随机推荐

  1. Python3实现ICMP远控后门(中)之“嗅探”黑科技

    ICMP后门 前言 第一篇:Python3实现ICMP远控后门(上) 第二篇:Python3实现ICMP远控后门(上)_补充篇 在上两篇文章中,详细讲解了ICMP协议,同时实现了一个具备完整功能的pi ...

  2. Python《学习手册:第二章-习题》

    什么是Python解释器? Python解释器是运行Python程序的程序. 什么是源代码? 源代码是为程序所写的语句:它包括文本文件(通常以.py为文件名结尾)的文件. 什么是字节码? 字节码是Py ...

  3. PAT1028:List Sorting

    1028. List Sorting (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Excel ca ...

  4. EasyUI集成Kindeditor使用

    在实际的项目中,我们需要在项目中集成富文本编辑器,而kindeditor作为一款优良的编辑器,在项目中或多或少都会用到! 实际效果图     使用方法: 1.首先下载Kindeditor编辑器,我这里 ...

  5. RedHat Linux下iptables防火墙设置

    一般情况下iptables已经包含在Linux发行版中.运行 # iptables --version 来查看系统是否安装iptables 启动iptables:# service iptables ...

  6. springboot中配置tomcat的access log

    在tomcat的access中打印出请求的情况可以帮助我们分析问题,通常比较关注的有访问IP.线程号.访问url.返回状态码.访问时间.持续时间. 在Spring boot中使用了内嵌的tomcat, ...

  7. Exchanger兄弟线程间数据信息交换

    一.简述 Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据.当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用 ...

  8. 十七、Hadoop学记笔记————Hbase入门

    简而言之,Hbase就是一个建立在Hdfs文件系统上的数据库(mysql,orecle等),不同的是Hbase是针对列的数据库 Hbase和普通的关系型数据库区别如下: Hbase有一些基本的术语,主 ...

  9. SSM-Spring-06:Spring的自动注入autowire的byName和byType

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- di的注入上次讲了一些,这次主要阐述域属性的自动注入 先讲byType方式 看名字就知道是根据类型进行自动注入 ...

  10. adb常用操作命令

    1.adb简介:    adb,即 Android Debug Bridge.通过这个工具和android进行交互操作 2.adb命令格式:    adb [-d|-e|-s <serialNu ...