前言: 由於導師在我的畢設項目裡加了消息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單... 需求分析 我的畢設是一個博客系統 ,類似於簡書這樣的,所以消息系統也類似,在用戶的消息里包含了有:喜歡和贊、評論、關註、私信這樣的一類東西,這樣的一個系統應該包含以下的功能: 1. ...
前言:由於導師在我的畢設項目裡加了消息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單...
需求分析
我的畢設是一個博客系統,類似於簡書這樣的,所以消息系統也類似,在用戶的消息里包含了有:喜歡和贊、評論、關註、私信這樣的一類東西,這樣的一個系統應該包含以下的功能:
- 當用戶評論/關註/點贊時能夠通知到被評論/關註/點贊的用戶,並生成像如下格式的提示信息(允許取消關註/點贊但不收到通知):
我沒有 關註了 你
三顆 喜歡了你的文章 《Java消息系統簡單設計與實現》
心臟 評論了你的文章 《Java消息系統簡單設計與實現》- 用戶之間能夠發送/接受私信,不需要像QQ那樣建立長連接實現實時通信,但刷新列表能看到新消息,並且界面類似QQ聊天界面一左一右,允許刪除私信;
- 管理員能發送通告,其實就像是用管理員的賬號給每一個用戶發送私信;
- 可以查看關註的用戶最新發表的文章,得到類似推送的效果;
- 所有消息當然也要標註好消息已讀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表:
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`username` varchar(50) NOT NULL COMMENT '用戶姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
user_follow表:
CREATE TABLE `user_follow` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`uid` bigint(20) NOT NULL COMMENT '用戶ID',
`follow_uid` bigint(20) NOT NULL COMMENT '關註的用戶id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶關註表,記錄了所有用戶的關註信息';
notify表:
CREATE TABLE `notify` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`sender_id` bigint(20) NOT NULL COMMENT '發送者用戶ID',
`reciver_id` bigint(20) NOT NULL COMMENT '接受者用戶ID',
`type` varchar(50) NOT NULL COMMENT '消息類型:announcement公告/remind提醒/message私信',
`is_read` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已讀,0未讀,1已讀',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間:按當前時間自動創建',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶通知表,包含了所有用戶的消息';
message表:
CREATE TABLE `message` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`notify_id` bigint(20) NOT NULL COMMENT '對應通知消息的id',
`sender_id` bigint(20) NOT NULL COMMENT '發送者用戶ID',
`reciver_id` bigint(20) NOT NULL COMMENT '接受者用戶ID',
`content` varchar(1000) NOT NULL COMMENT '消息內容,最長長度不允許超過1000',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間:按當前時間自動創建',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='私信信息表,包含了所有用戶的私信信息';
根據《Java開發手冊》5.3 第六條 沒有使用任何級聯和外鍵,bingo!
Spring Boot + MyBatis 實例
第一步:基礎環境搭建
SpringBoot項目怎麼搭就不說了吧,給一給幾個關鍵的配置文件:
pom包依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- SpringBoot - MyBatis 逆向工程 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>5.1.18</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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:
## 資料庫連接配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/message_system?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
## MyBatis相關配置
mybatis.type-aliases-package=com.wmyskxz.demo.messagesystem.domain
mybatis.mapper-locations=classpath:mapper/*.xml
在啟動類上加上註解:
@EnableTransactionManagement // 啟註解事務管理,等同於xml配置方式的 <tx:annotation-driven />
@MapperScan("com.wmyskxz.demo.messagesystem.dao")
@SpringBootApplication
public class MessageSystemApplication {
....
}
第二步:MyBatis 逆向工程
新建【util】包,在下麵新建兩個類:
MybatisGenerator類:
public class MybatisGenerator {
public static void main(String[] args) throws Exception {
String today = "2019-1-7";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date now = sdf.parse(today);
Date d = new Date();
if (d.getTime() > now.getTime() + 1000 * 60 * 60 * 24) {
System.err.println("——————未成成功運行——————");
System.err.println("——————未成成功運行——————");
System.err.println("本程式具有破壞作用,應該只運行一次,如果必須要再運行,需要修改today變數為今天,如:" + sdf.format(new Date()));
return;
}
if (false)
return;
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
InputStream is = MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
System.out.println("生成代碼成功,只能執行一次,以後執行會覆蓋掉mapper,pojo,xml 等文件上做的修改");
}
}
OverIsMergeablePlugin類:
/**
* 解決 MyBatis 逆向工程重覆生成覆蓋問題的工具類
*/
public class OverIsMergeablePlugin extends PluginAdapter {
@Override
public boolean validate(List<String> warnings) {
return true;
}
@Override
public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
try {
Field field = sqlMap.getClass().getDeclaredField("isMergeable");
field.setAccessible(true);
field.setBoolean(sqlMap, false);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
在【resrouces】資源文件下新建逆向工程配置文件【generatorConfig.xml】:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--避免生成重覆代碼的插件-->
<plugin type="com.wmyskxz.demo.messagesystem.util.OverIsMergeablePlugin"/>
<!-- 是否去除自動生成的代碼中的註釋 true:是 false:否-->
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--資料庫鏈接地址賬號密碼-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/message_system?serverTimezone=UTC"
userId="root" password="123456">
</jdbcConnection>
<!-- 預設 false,把 JDBC DECIMAL 和 NUMERIC 類型解析為 Integer
為 true 時解析為 java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--生成pojo類存放位置-->
<javaModelGenerator targetPackage="com.wmyskxz.demo.messagesystem.domain" targetProject="src/main/java">
<!-- enableSubPackages:是否讓 schema 作為包的尾碼-->
<property name="enableSubPackages" value="true"/>
<!-- trimStrings:從資料庫返回的值被清理前後的空格 -->
<property name="trimStrings" value="true"/>
<!-- 是否對model添加 構造函數 -->
<property name="constructorBased" value="true"/>
</javaModelGenerator>
<!--生成xml映射文件存放位置-->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--生成mapper類存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.wmyskxz.demo.messagesystem.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!--生成對應表及類名
tableName:要生成的表名
domainObjectName:生成後的實例名
enableCountByExample:Count語句中加入where條件查詢,預設為true開啟
enableUpdateByExample:Update語句中加入where條件查詢,預設為true開啟
enableDeleteByExample:Delete語句中加入where條件查詢,預設為true開啟
enableSelectByExample:Select多條語句中加入where條件查詢,預設為true開啟
selectByExampleQueryId:Select單個對象語句中加入where條件查詢,預設為true開啟
-->
<table tableName="user" domainObjectName="User" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
<property name="my.isgen.usekeys" value="true"/>
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
<table tableName="notify" domainObjectName="Notify" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
<property name="my.isgen.usekeys" value="true"/>
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
<table tableName="user_follow" domainObjectName="UserFollow" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
<property name="my.isgen.usekeys" value="true"/>
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
<table tableName="message" domainObjectName="Message" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
<property name="my.isgen.usekeys" value="true"/>
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
</context>
</generatorConfiguration>
運行我們的【MybatisGenerator】類中的 main 方法就能看到自動生成的實體、Xml文件以及 Mapper 類
第三步:Service 層
不給介面了,直接給實現吧,方法都很簡單,而且沒有做任何的安全限制,只是為了實現簡單的消息系統,看效果
UserServiceImpl:
@Service
public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Override
public void addUserByUsername(String username) {
userMapper.insert(new User(null, username));// 主鍵自增長.
}
@Override
public User findUserById(Long id) {
return userMapper.selectByPrimaryKey(id);
}
}
UserFollowServiceImpl:
@Service
public class UserFollowServiceImpl implements UserFollowService {
@Resource
UserFollowMapper userFollowMapper;
@Autowired
NotifyService notifyService;
@Override
public void userAFollowUserBById(Long userAId, Long userBId) {
// 先要創建一條提示消息
notifyService.addNotify(userAId, userBId, "follow");// 關註信息
UserFollow userFollow = new UserFollow();
userFollow.setUid(userAId);
userFollow.setFollowUid(userBId);
userFollowMapper.insertSelective(userFollow);
}
@Override
public void userAUnfollowUserBById(Long userAId, Long userBId) {
// 首先查詢到相關的記錄
UserFollowExample example = new UserFollowExample();
example.or().andUidEqualTo(userAId).andFollowUidEqualTo(userBId);
UserFollow userFollow = userFollowMapper.selectByExample(example).get(0);
// 刪除關註數據
userFollowMapper.deleteByPrimaryKey(userFollow.getId());
}
}
NotifyServiceImpl:
@Service
public class NotifyServiceImpl implements NotifyService {
@Resource
NotifyMapper notifyMapper;
@Override
public int addNotify(Long senderId, Long reciverId, String type) {
Notify notify = new Notify(null, senderId, reciverId, type, false, null);
return notifyMapper.insertSelective(notify);// id和creatTime自動生成.
}
@Override
public void readNotifyById(Long id) {
Notify notify = notifyMapper.selectByPrimaryKey(id);
notify.setIsRead(true);
notifyMapper.updateByPrimaryKey(notify);
}
@Override
public List<Notify> findAllNotifyByReciverId(Long id) {
List<Notify> notifies = new LinkedList<>();
NotifyExample example = new NotifyExample();
example.setOrderByClause("`id` DESC");// 按id倒敘,也就是第一個數據是最新的.
example.or().andReciverIdEqualTo(id);
notifies.addAll(notifyMapper.selectByExample(example));
return notifies;
}
@Override
public List<Notify> findAllUnReadNotifyByReciverId(Long id) {
List<Notify> notifies = new LinkedList<>();
NotifyExample example = new NotifyExample();
example.setOrderByClause("`id` DESC");// 按id倒敘,也就是第一個數據是最新的.
example.or().andReciverIdEqualTo(id).andIsReadEqualTo(false);
notifies.addAll(notifyMapper.selectByExample(example));
return notifies;
}
}
MessageServiceImpl:
@Service
public class MessageServiceImpl implements MessageService {
@Resource
MessageMapper messageMapper;
@Resource
NotifyService notifyService;
@Override
public void addMessage(Long senderId, Long reciverId, String content) {
// 先創建一條 notify 數據
Long notifyId = (long) notifyService.addNotify(senderId, reciverId, "message");// message表示私信
// 增加一條私信信心
Message message = new Message(null, notifyId, senderId, reciverId, content, null);
messageMapper.insertSelective(message);// 插入非空項,id/createTime資料庫自動生成
}
@Override
public void deleteMessageById(Long id) {
messageMapper.deleteByPrimaryKey(id);
}
@Override
public Message findMessageByNotifyId(Long id) {
// 觸發方法時應把消息置為已讀
notifyService.readNotifyById(id);
MessageExample example = new MessageExample();
example.or().andNotifyIdEqualTo(id);
return messageMapper.selectByExample(example).get(0);
}
}
第四步:Controller 層
也很簡單,只是為了看效果
UserController:
@RestController
public class UserController {
@Autowired
UserService userService;
@PostMapping("/addUser")
public String addUser(@RequestParam String username) {
userService.addUserByUsername(username);
return "Success!";
}
@GetMapping("/findUser")
public User findUser(@RequestParam Long id) {
return userService.findUserById(id);
}
}
UserFollowController :
@RestController
public class UserFollowController {
@Autowired
UserFollowService userFollowService;
@PostMapping("/follow")
public String follow(@RequestParam Long userAId,
@RequestParam Long userBId) {
userFollowService.userAFollowUserBById(userAId, userBId);
return "Success!";
}
@PostMapping("/unfollow")
public String unfollow(@RequestParam Long userAId,
@RequestParam Long userBId) {
userFollowService.userAUnfollowUserBById(userAId, userBId);
return "Success!";
}
}
NotifyController :
@RestController
public class NotifyController {
@Autowired
NotifyService notifyService;
@PostMapping("/addNotify")
public String addNotify(@RequestParam Long senderId,
@RequestParam Long reciverId,
@RequestParam String type) {
notifyService.addNotify(senderId, reciverId, type);
return "Success!";
}
@PostMapping("/readNotify")
public String readNotify(@RequestParam Long id) {
notifyService.readNotifyById(id);
return "Success!";
}
@GetMapping("/listAllNotify")
public List<Notify> listAllNotify(@RequestParam Long id) {
return notifyService.findAllNotifyByReciverId(id);
}
@GetMapping("/listAllUnReadNotify")
public List<Notify> listAllUnReadNotify(@RequestParam Long id) {
return notifyService.findAllUnReadNotifyByReciverId(id);
}
}
MessageController :
@RestController
public class MessageController {
@Autowired
MessageService messageService;
@PostMapping("/addMessage")
public String addMessage(@RequestParam Long senderId,
@RequestParam Long reciverId,
@RequestParam String content) {
messageService.addMessage(senderId, reciverId, content);
return "Success!";
}
@DeleteMapping("/deleteMessage")
public String deleteMessage(@RequestParam Long id) {
messageService.deleteMessageById(id);
return "Success!";
}
@GetMapping("/findMessage")
public Message findMessage(@RequestParam Long id) {
return messageService.findMessageByNotifyId(id);
}
}
第五步:測試
通過 REST 測試工具,可以看到正確的效果,這裡就不給出所有的測試了。
總結
以上的項目簡單而且沒有任何的安全驗證,不過能夠基本完成我們的需求,還有一些功能沒有實現,例如管理員發通告(上面只演示了私信和關註信息),按照上面的系統就直接暴力給每個用戶都加一條通知消息,感覺有點自閉..我也不知道怎麼設計好..希望有經驗的大大能指條路啊!
其實關於這個簡單的系統我查了好多好多資料..把自己都看自閉了,後來我乾脆把所有網頁都關掉,開始用 JPA 自己開始抽象實體,把各個實體寫出來並把所有實體需要的數據啊相互之間的關聯關係啊寫清楚,然後再從自動生成的資料庫中找思路...hhh...要不是我 JPA 不是很熟我覺得用 JPA 就能寫出來了,不用 JPA 的原因在於一些數據的懶載入不知道怎麼處理,還有就是查詢語句太複雜,免不了要浪費一些資源...emmm..說到底還是不是特別懂 JPA,下麵給一張複雜的用 JPA 建立的 User 實體吧(隨手截的..hhh...很亂..):
按照慣例黏一個尾巴:
歡迎轉載,轉載請註明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關註公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693