Halo 项目中定义了一些实体类,用于存储博客中的关键数据,如用户信息、文章信息等。在深入学习 Halo 的设计理念与实现过程之前,不妨先学习一下一个完整的博客系统都由哪些元素组成。
实体类Halo 中的除 BaseEntity 外的每一个实体类都对应着一个数据表,以 User 类为例,每一个 User 对象都对应 users 表中的一条记录,每一个对象属性的值也等于数据表中对应字段的值。
User 类定义如下:
@Data // Lombok 注解, 自动生成 get()、set()、toString() 等方法
@Entity // JPA 注解, 声明该类为一个实体类, 必须与 @Id 搭配使用
@Table(name = "users") // JAP 注解, 声明该类映射到数据库的 users 数据表
@ToString(callSuper = true) // Lombok 注解, callSuper = true 表示调用 toString() 方法时输出父类的属性
@EqualsAndHashCode(callSuper = true) // 自动生成 equals() 和 hashCode() 方法, 默认 callSuper 为 false, 为 true 表示 equals() 方法比较时会调用父类的 equals() 方法
public class User extends BaseEntity {
@Id // JPA 注解, 声明主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // JPA 注解, 声明主键的生成策略, IDENTITY 表示使用自增 id
@Column(name = "id") // JPA 注解, 声明实体类的属性 id 映射到数据表中的字段 id
private Integer id;
/**
* User name.
*/
@Column(name = "username", columnDefinition = "varchar(50) not null")
private String username;
/**
* User nick name,used to display on page.
*/
@Column(name = "nickname", columnDefinition = "varchar(255) not null")
private String nickname;
/**
* Password.
*/
@Column(name = "password", columnDefinition = "varchar(255) not null")
private String password;
/**
* User email.
*/
@Column(name = "email", columnDefinition = "varchar(127) default ''")
private String email;
/**
* User avatar.
*/
@Column(name = "avatar", columnDefinition = "varchar(1023) default ''")
private String avatar;
/**
* User description.
*/
@Column(name = "description", columnDefinition = "varchar(1023) default ''")
private String description;
/**
* Expire time.
*/
@Column(name = "expire_time", columnDefinition = "timestamp default CURRENT_TIMESTAMP")
@Temporal(TemporalType.TIMESTAMP)
private Date expireTime;
@Override
public void prePersist() {
super.prePersist();
id = null;
if (email == null) {
email = "";
}
if (avatar == null) {
avatar = "";
}
if (description == null) {
description = "";
}
if (expireTime == null) {
expireTime = DateUtils.now();
}
}
}
注解解释:
-
@Data:Lombok 注解,自动生成 get()、set()、toString() 等方法。
-
@Entity:JPA 注解,声明该类为一个实体类, 必须与 @Id 搭配使用。
-
@Table:JAP 注解,声明该类对应数据库中的某个数据表,name 指明表名。
-
@ToString:lombok 注解,callSuper = true 表示调用 toString() 方法时会输出父类的属性。
-
@EqualsAndHashCode:自动生成 equals() 和 hashCode() 方法,默认 callSuper 为 false, 为 true 表示 equals() 在方法比较时会调用父类的 equals()(如果父类的 equals() 返回 false,则直接返回 false,否则继续比较)。
-
@Id:JPA 注解,声明主键。
-
@GeneratedValue:JPA 注解,声明主键的生成策略,IDENTITY 表示使用自增 id。
-
@Column:JPA 注解,声明实体对象的属性映射到数据表中的哪一个字段,name 指定字段名,columnDefinition 指定字段的定义。
User 类中定义了用户名、昵称、邮箱等用户数据,Halo 使用 JPA 将实体对象持久化到数据库中,也就是将 User 对象的各个属性存储到数据表 users 的各个字段中。JPA 支持自动创建数据表,所以启动项目前无需建表,关于 JPA 的使用,可以参考 SpringBoot 整合 Spring Data JPA。
User 类继承了 BaseEntity,BaseEntity 类中定义了一些通用的属性,如 createTime、updateTime 以及 deleted 等,分别指用户的创建时间、修改时间以及是否被删除,users 表中有对应的字段。此外,BaseEntity 类还定义了三个方法,分别为 prePersist()、preUpdate() 和 preRemove():
-
prePersist() 方法在对象持久化到数据库之前被调用。
-
preUpdate() 方法在对象的某个属性发生变动时被调用,如更新实体的 update_time。
-
preRemove() 方法在对象从数据库删除前被调用。
BaseEntity 并没有对应某一个数据表,它被 @MappedSuperclass 修饰, @MappedSuperclass 属于 JPA 注解,应用于实体类的父类中, 该注解作用的类不会映射到数据表,但其属性都将映射到子类所对应的数据表。也就是说不同实体类的通用属性可在相同的父类中定义,子类继承父类后,父类中的这些通用属性会持久化到子类对应的数据表中。
BaseEntity 类定义如下:
@Data
@ToString
@MappedSuperclass // JPA 注解, 应用于实体类的父类中, 该注解作用的类不会映射到数据表,但其属性都将映射到子类的数据表
@EqualsAndHashCode
public class BaseEntity {
/**
* Create time.
*/
@Column(name = "create_time", columnDefinition = "timestamp default CURRENT_TIMESTAMP")
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
/**
* Update time.
*/
@Column(name = "update_time", columnDefinition = "timestamp default CURRENT_TIMESTAMP")
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
/**
* Delete flag.
*/
@Column(name = "deleted", columnDefinition = "TINYINT default 0")
private Boolean deleted = false;
@PrePersist // @PrePersist 事件在实体对象插入到数据库的过程中发生
protected void prePersist() {
deleted = false;
Date now = DateUtils.now();
if (createTime == null) {
createTime = now;
}
if (updateTime == null) {
updateTime = now;
}
}
@PreUpdate // @PreUpdate 事件在实体的状态同步到数据库之前触发
protected void preUpdate() {
updateTime = new Date();
}
@PreRemove // @PreRemove 事件在实体从数据库删除之前触发
protected void preRemove() {
updateTime = new Date();
}
}
数据表
项目启动成功后,JPA 会为实体类自动生成对应的数据表。可以使用 Navicat 查看 MySQL 中库名为 'halodb' 的数据库(自己配置的库名),发现自动创建了如下数据表:
下面介绍不同数据表的作用以及对应的字段含义,由于许多实体类都继承自 BaseEntity,所以不同数据表中会有一些通用的字段,如:
-
id
:主键(虽不在 BaseEntity 中定义,但每个子类中都存在)。 -
crate_time
:创建时间。 -
deleted
:是否已经删除。 -
update_time
:更新时间。
下面介绍各个数据表中,特定字段的具体含义:
1. attachments:附件表,用于存放图片和文件。
-
file_key
:文件的 key,可以根据 file_key 删除文件。 -
height
:图片高度。 -
media_type
:媒体类型,如 text/html、image/jpeg 等。 -
name
:附件的名字。 -
path
:附件的存储路径。 -
size
:附件的大小。 -
suffix
:附件的后缀,如 png、html 等。 -
thumb_path
:缩略图的访问路径,该路径指定的资源可用作为封面图。 -
type
:附件的上传类型,如上传到本地(type 为 0)、阿里云(type 为 4)。 -
width
:图片宽度。
2. categories:文章分类目录表,发布文章时可设置文章所属的分类。
-
decryption
:描述。 -
name
:分类名。 -
parent_id
:父目录 id。 -
password
:密码。 -
slug
:别名。 -
slug_name
:项目中没有用到。 -
thumbnail
:分类的封面图。
3. comment_black_list:评论黑名单表,用于禁止某个 ip 进行评论。
-
ban_time
:封禁时间。 -
ip_address
:封禁的 ip。
4. comments:评论表,可对文章进行评论,也可对评论进行回复,还可以对页面(友情链接、图库、日志等)进行评论。
-
type
:给文章进行评论时 type 为 0,给页面进行评论时 type 为 1。 -
allow_notification
:是否允许通知。 -
author
:评论者的姓名。 -
author_url
:评论者的 url。 -
content
:评论内容。 -
email
:评论者的 email。 -
gravatar_md5
:评论者的头像。 -
ip_address
:评论者的 ip。 -
is_admin
:评论者是否为博主。 -
parent_id
:如果回复某个评论,则 parent_id 为该评论的 id;如果评论文章,则 parent_id 为 0。 -
post_id
:哪篇文章或哪个页面的评论。 -
status
:评论的状态,0 表示已发布,1 表示待发布,2 表示添加到了回收站。 -
top_priority
:是否置顶。 -
user_agent
:用户代理,例如浏览器。
5. journals:用户日志表,在 Halo 中用户可以对外分享日志(记录生活的日志),日志的信息存储在 journals 表中。
-
content
:日志内容。 -
likes
:点赞量。 -
source_content
:原始内容。 -
type
:日志类型,公开日志的 type 为 0,私密日志的 type 为 1。
6. links:友情链接表,用于访问其他博客或资源。
-
description
:描述。 -
logo
:标志。 -
name
:名称。 -
priority
:排序编号。 -
team
:所属分组。 -
url
:链接。
7. logs:系统日志表,记录用户的操作。
-
content
:日志内容。 -
ip_address
:操作者的 ip。 -
log_key
:log_key 通常为操作对象的标识,例如发布文章时 log_key 是文章的 id,用户登录时 log_key 是用户的 userName。 -
type
:日志类型,例如发表文章时 type 为 5,登录时 type 为 25。
8. menus:菜单表,博客的主页有多个菜单,且每个菜单都可以是多级菜单。
-
icon
:图标。 -
name
:名称 -
parent_id
:父级菜单的 id。 -
priority
:优先级,用于博客首页上菜单的排序。 -
target
:可选 _self 和 _blank,_self 表示在当前页面打开菜单所指向的链接;_blank 表示在新的页面打开链接。 -
team
:所属分组。 -
url
:菜单所指向的链接。
9. metas:元数据表,用于设置文章或页面的属性,可在发布文章或页面时的 "高级" 选项中进行操作。
-
type
:设置文章的元数据时 type 为 0,设置页面的元数据时 type 为 1。 -
mate_key
:元数据 key 可以设置文章是否支持点赞、是否支持复制等。 -
mate_value
:key 对应的值 -
post_id
: 文章或页面的 id。
10. options:博客设置表,或者称为选项表,用于存储系统设置的相关信息。用户可在 Dashboard 界面的 "系统" -> "博客设置" 中进行配置。
-
option_key
:博客的选项,例如博客标题 blog_title、主题 theme、是否已安装 is_installed 等。 -
type
:博客的内部选项的 type 为 0,自定义选项的 type 为 1。 -
option_value
:选项对应的值。
11. photos:图片表,Halo 可以设置图库,图库页面的图片存放在 photos 表中。
-
description
:描述。 -
likes
:点赞量。 -
location
:拍摄地点。 -
name
:名称。 -
take_time
:拍摄日期。 -
team
:所属分组。 -
thumbnail
:缩略图地址。 -
url
:图片链接。
12. post_categories:文章 - 分类的关系表,记录每个文章属于哪个分类。
-
category_id
:分类 id。 -
post_id
:文章 id。
13. post_tags:文章标签表,记录文章的标签。
-
post_id
:文章 id。 -
tag_id
:标签 id。
14. posts:文章表,也用来存储页面。
-
type
:文章的 type 为 0,页面的 type 为 1。 -
disallow_comment
:是否关闭评论。 -
edit_time
:编辑时间。 -
editor_type
:编辑器类型。 -
format_content
:格式化后的内容。 -
likes
:点赞量。 -
meta_description
:自定义描述。 -
meta_keywords
:自定义关键词。 -
original_content
:原始内容。 -
password
:密码。 -
slug
:别名。 -
status
:状态,0 表示已发布,1 表示待发布,2 表示位于回收站,3 表示私密文章或页面。 -
summary
:文章摘要。 -
template
:自定义的模板,新增页面时可设置模板。 -
thumbnail
:封面图。 -
title
:标题。 -
top_priority
:是否置顶。 -
url
:链接。 -
visits
:访问量。 -
word_count
:字数统计。
15. tags:标签表,发布文章时,可为文章设置标签。
-
name
:标签名称。 -
slug
:别名。 -
slug_name
:项目中没有用到。 -
thumbnail
:标签的封面图。
16. theme_settings:主题设置表,设置博客主题。
-
setting_key
:主题中可设置的选项,例如文章标题是否大写 post_title_uppper、博客首页的邮箱 email 等。 -
theme_id
:主题的 id。 -
setting_value
:setting_key 对应的值。
17. users:用户表,记录用户信息。
-
avatar
:头像。 -
decription
:描述。 -
email
:邮箱。 -
expire_time
:过期时间,严格来说应该称为账号的起始有效时间,当前时间超过 expire_time 时,该账号才能正常登录。 -
maf_key
:两步验证码的 key。 -
maf_type
:是否开启了两步验证码,0 表示未开启(默认),1 表示开启。 -
nickname
:昵称。 -
password
:密码。 -
username
:用户名。