2020-01-17 从零开始用Spring Boot开发一个个

2020-01-17  本文已影响0人  NoelleMu

实体类

根据需求,我们可以抽象出以下实体类:

实体关系

不是很会画ER图,就画张草图简单示意一下:

可能有人会问了,为什么博客和用户之间没有关系呢?其实很简单,就是因为这个网站真正可以发布文章的只有我一个人,用户只是用来发表评论的,所以用户和博客(文章)之间不需要建立关系。

实体类

重构之后我们没有选择MongoDB,而是用了MySQL——原因也只有一个:成熟,解决方案多。而且数据量其实不是很大,关系也不是很复杂,这种情况下用MySQL不会带来特别大的麻烦。

实体类其实没有太多好讲的,主要是在实体关系这方面需要多关注一些。

文章

这次使用@NotNull@NotEmpty注解加上了判空操作,避免因为参数校验不足而产生一个NullPointerException把后端代码整炸了。

使用懒加载是为了提高性能。

这次遵循阿里Java编码规范,使用文档注释来写每个字段的注释。

Lombok还是要用的,对提高开发效率有很高的帮助。

@Temporal注解可以用于指定时间的格式,这里就用TIMESTAMP时间戳类型了。

文章内容这个字段需要使用TEXT类型,使用方法就是加上两个注解:@Lob@Column,并且指定它为TEXT类型。

/**
 * 文章实体类
 *
 * @author jiangwen
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
@Entity
@Table(name = "wendev_article")
public class Article {
    /**
     * 主键,值为自动生成
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 文章标题
     */
    @NotNull(message = "标题不能为空")
    @NotEmpty(message = "标题不能为空字符串")
    @Column
    private String title;

    /**
     * 文章内容,因为太长所以映射为TEXT类型
     */
    @NotNull(message = "文章内容不能为空")
    @NotEmpty(message = "文章内容不能为空字符串")
    @Lob
    @Column(columnDefinition = "TEXT")
    private String content;

    /**
     * 文章阅读量
     */
    @Column
    private Integer views;

    /**
     * 是否已发布
     * 这其实是一个保留字段,目前还用不到
     */
    @Column
    private Boolean published;

    /**
     * 是否开启评论功能
     */
    @Column
    private Boolean commentEnabled;

    /**
     * 文章的创建时间
     */
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    /**
     * 文章的更新时间
     */
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date updateTime;

    /**
     * 该文章所属的类型
     */
    @ManyToOne
    private Type type;

    /**
     * 该文章所拥有的标签,设置级联新增
     */
    @ManyToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
    private List<Tag> tags = new ArrayList<>();

    /**
     * 该文章对应的评论
     */
    @OneToMany(mappedBy = "article", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();

    /**
     * 乐观锁
     */
    @Version
    private Long version;
}

用户

为了以后可能对接Spring Security而保留了一个role字段。

/**
 * @author jiangwen
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
@Entity
@Table(name = "wendev_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    @NotNull(message = "用户名不能为空")
    @NotEmpty(message = "用户名不能为空字符串")
    private String username;

    @Column
    @NotNull(message = "用户昵称不能为空")
    @NotEmpty(message = "用户昵称不能为空字符串")
    private String nickname;

    @Column
    @NotNull(message = "邮箱不能为空")
    @NotEmpty(message = "邮箱不能为空字符串")
    private String email;

    @Column
    @NotNull(message = "密码不能为空")
    @NotEmpty(message = "密码不能为空字符串")
    private String password;

    /**
     * 用户权限
     * 这个字段是为了以后可能对接Spring Security保留的
     */
    @Column
    @NotNull(message = "用户角色不能为空")
    @NotEmpty(message = "用户角色不能为空字符串")
    private String role;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Temporal(TemporalType.TIMESTAMP)
    private Date updateTime;

    /**
     * 该用户所发表的全部评论
     */
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();

    @Version
    private Long version;
}

文章类型

类型与文章是一对多关系,由类型端来维护。

/**
 * 文章类型实体类
 *
 * @author jiangwen
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
@Entity
@Table(name = "wendev_type")
public class Type {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 类型名称
     */
    @NotNull(message = "类型名称不能为空")
    @NotEmpty(message = "类型名称不能为空字符串")
    @Column
    private String name;

    /**
     * 该分类下属的文章
     * 关系的被维护端
     */
    @OneToMany(mappedBy = "type", fetch = FetchType.LAZY)
    private List<Article> articles = new ArrayList<>();

    @Version
    private Long version;
}

文章标签

标签与文章有多对多关系,由标签端来维护。

/**
 * 文章标签实体类
 *
 * @author jiangwen
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
@Entity
@Table(name = "wendev_tag")
public class Tag {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 拥有该tag的文章列表
     * 关系的维护端
     */
    @ManyToMany(mappedBy = "tags", fetch = FetchType.LAZY)
    private List<Article> articles = new ArrayList<>();

    /**
     * 标签名称
     */
    @Column
    @NotNull(message = "标签名称不能为空")
    @NotEmpty(message = "标签类型不能为空")
    private String name;

    @Version
    private Long version;
}

评论

评论也没什么好说的。但是由于它有一个自关联一对多的关系,所以需要注意一下。

其实也没有什么好注意的,当成两个实体类来写就行了。

/**
 * @author jiangwen
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@ToString
@Entity
@Table(name = "wendev_comment")
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 该评论的发表者
     */
    @ManyToOne
    private User user;

    /**
     * 该评论所属于的文章
     */
    @ManyToOne
    private Article article;

    /**
     * 该评论下的子评论
     */
    @OneToMany(mappedBy = "parentComment", fetch = FetchType.LAZY)
    private List<Comment> replyComments = new ArrayList<>();

    /**
     * 该评论的父评论
     */
    @ManyToOne
    private Comment parentComment;

    @Column
    @NotNull(message = "评论内容不能为空")
    @NotEmpty(message = "评论内容不能为空字符串")
    private String content;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Version
    private Long version;
}

这样实体类和实体关系就构建完成了。

启动项目让JPA帮我们创建好表,再从DataGrip里查看一下表关系图就可以很清楚地看出这些实体类之间的关系了:

wendev_article_tags是维护文章与标签多对多关系的关联表,是自动帮我们创建好的。当然也可以在实体类里使用@JoinTable指定它的信息。

上一篇下一篇

猜你喜欢

热点阅读