Java訊息系統簡單設計與實現
前言:由於導師在我的畢設專案里加了訊息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單...
需求分析
我的畢設是一個部落格系統,類似於簡書這樣的,所以訊息系統也類似,在使用者的訊息裡包含了有:喜歡和贊、評論、關注、私信這樣的一類東西,這樣的一個系統應該包含以下的功能:
-
- 當用戶評論/關注/點贊時能夠通知到被評論/關注/點讚的使用者,並生成像如下格式的提示資訊(允許取消關注/點贊但不收到通知):
我沒有 關注了 你
三顆 喜歡了你的文章 《Java訊息系統簡單設計與實現》
心臟 評論了你的文章 《Java訊息系統簡單設計與實現》 -
- 使用者之間能夠傳送/接受私信,不需要像QQ那樣建立長連線實現實時通訊,但重新整理列表能看到新訊息,並且介面類似QQ聊天介面一左一右,允許刪除私信;
-
- 管理員能傳送通告,其實就像是用管理員的賬號給每一個使用者傳送私信;
-
- 可以檢視關注的使用者最新發表的文章,得到類似推送的效果;
-
- 所有訊息當然也要標註好訊息已讀or未讀,登入就能得到訊息提醒標識好有多少未讀訊息,像是QQ訊息右上角的小紅點那樣類似;
OK,大致就是以上的功能,那麼問題來了:這要怎麼設計啊?
進一步分析
其實可以根據上面的需求分析,把上面的訊息大致可以分為公告(Announcement)、提醒(Remind)、私信(Message)
傳送者 | 接受者 | 資訊型別 | 動作型別 | 通知內容 | 是否已讀 | 訊息建立時間 |
---|---|---|---|---|---|---|
粉絲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