本文节选自电子书《Netkiller Architect 手札》

出处:http://www.netkiller.cn

作者:netkiller , QQ:13721218, 订阅号:netkiller-ebook

第 12 章 Spring Data 数据库建模最佳实践

目录

12.1. 分类表12.2. 为字段增加索引12.3. 复合索引12.4. 一对多实例12.5. ManyToMany 多对多12.6. 外键级联删除

ORM的出现解决了程序猿学习数据库学历成本,也加快了开发的速度。程序猿无需再学习数据库定义语言DDL以及数据库客户端,也无需关注建表这些繁琐的工作,同时也降低了数据库结构变更管理中与DBA频繁沟通的成本。

在过去的两年中我们采用 Spring Data JPA 定义数据库,访问数据库,积累了很多经验,最终我们发现使用 Spring Data 实体定义完全可以代替 DBA 的建模工作。

下面我们采用案例,一个一个讲解,各种数据库实体关系的定义。相关数据库建模知识请先阅读 《Netkiller Architect 手札》 以及 《Netkiller Spring 手札》

12.1. 分类表

这是一个通用分类表,常见的父子关系加上path路径

        +-----------+| category  ||-----------||id         | <---+|name       |     ||description|    1:n|status     |     ||pid        | o---+|path       ||status     ||ctime      ||mtime      |+-----------+
package common.domain;import java.util.Date;
import java.util.Set;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;import org.springframework.format.annotation.DateTimeFormat;import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;@Entity
public class Category {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)public int id;public String name;public String description;public String path;@Column(columnDefinition = "enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '状态'")public String status;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")public Date ctime;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更改时间'")public Date mtime;@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })@JoinColumn(name = "pid", referencedColumnName = "id")private Category categorys;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "category", fetch = FetchType.EAGER)private Set<Category> category;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public Date getCtime() {return ctime;}public void setCtime(Date ctime) {this.ctime = ctime;}public Date getMtime() {return mtime;}public void setMtime(Date mtime) {this.mtime = mtime;}public Category getCategorys() {return categorys;}public void setCategorys(Category categorys) {this.categorys = categorys;}public Set<Category> getCategory() {return category;}public void setCategory(Set<Category> category) {this.category = category;}@Overridepublic String toString() {return "Category [id=" + id + ", name=" + name + ", description=" + description + ", path=" + path + ", status="+ status + ", ctime=" + ctime + ", mtime=" + mtime + ", categorys=" + categorys + ", category="+ category + "]";}}

期望结果

CREATE TABLE `category` (`id` int(11) NOT NULL AUTO_INCREMENT,`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',`description` varchar(255) DEFAULT NULL,`mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '',`name` varchar(255) DEFAULT NULL,`path` varchar(255) DEFAULT NULL,`status` enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '',`pid` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

12.2. 为字段增加索引

我们希望为 name 和 path 字段增加普通索引

package common.domain;import java.util.Date;
import java.util.Set;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;import org.springframework.format.annotation.DateTimeFormat;import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;@Entity
@Table(indexes = { @Index(name = "name", columnList = "name DESC"), @Index(name = "path", columnList = "path") })
public class Category {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)public int id;public String name;public String description;public String path;@Column(columnDefinition = "enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '状态'")public String status;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")public Date ctime;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更改时间'")public Date mtime;@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })@JoinColumn(name = "pid", referencedColumnName = "id")private Category categorys;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "category", fetch = FetchType.EAGER)private Set<Category> category;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public Date getCtime() {return ctime;}public void setCtime(Date ctime) {this.ctime = ctime;}public Date getMtime() {return mtime;}public void setMtime(Date mtime) {this.mtime = mtime;}public Category getCategorys() {return categorys;}public void setCategorys(Category categorys) {this.categorys = categorys;}public Set<Category> getCategory() {return category;}public void setCategory(Set<Category> category) {this.category = category;}@Overridepublic String toString() {return "Category [id=" + id + ", name=" + name + ", description=" + description + ", path=" + path + ", status="+ status + ", ctime=" + ctime + ", mtime=" + mtime + ", categorys=" + categorys + ", category="+ category + "]";}}

期望结果

CREATE TABLE `category` (`id` int(11) NOT NULL AUTO_INCREMENT,`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '????',`description` varchar(255) DEFAULT NULL,`mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '????',`name` varchar(255) DEFAULT NULL,`path` varchar(255) DEFAULT NULL,`status` enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '??',`pid` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `name` (`name`),KEY `path` (`path`),KEY `FKeiel7nqjxu4kmefso9tm9qcsu` (`pid`),CONSTRAINT `FKeiel7nqjxu4kmefso9tm9qcsu` FOREIGN KEY (`pid`) REFERENCES `category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8      

12.3. 复合索引

创建由多个字段组成的复合索引,如: "member_id", "articleId"

package cn.netkiller.api.model;import java.io.Serializable;
import java.util.Date;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;import com.fasterxml.jackson.annotation.JsonFormat;@Entity
@Table(name = "comment", uniqueConstraints = { @UniqueConstraint(columnNames = { "member_id", "articleId" }) })
public class Comment implements Serializable {/*** */private static final long serialVersionUID = -1484408775034277681L;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)private int id;@ManyToOne(cascade = { CascadeType.ALL })@JoinColumn(name = "member_id")private Member member;private int articleId;private String message;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@Temporal(TemporalType.TIMESTAMP)@Column(updatable = false)@org.hibernate.annotations.CreationTimestampprotected Date createDate;public int getId() {return id;}public void setId(int id) {this.id = id;}public Member getMember() {return member;}public void setMember(Member member) {this.member = member;}public int getArticleId() {return articleId;}public void setArticleId(int articleId) {this.articleId = articleId;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Date getCreateDate() {return createDate;}public void setCreateDate(Date createDate) {this.createDate = createDate;}
}

期望结果

CREATE TABLE `comment` (`id` int(11) NOT NULL AUTO_INCREMENT,`article_id` int(11) NOT NULL,`create_date` datetime DEFAULT NULL,`message` varchar(255) DEFAULT NULL,`member_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `UK5qxfiu92nwlvgli7bl3evl11m` (`member_id`,`article_id`),CONSTRAINT `FKmrrrpi513ssu63i2783jyiv9m` FOREIGN KEY (`member_id`) REFERENCES `member` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

12.4. 一对多实例

如下图,我们将实现 categroy 和 article 的一对多关系

       +-----------+| category  ||-----------|+-->|id         | <---+|   |title      |     ||   |description|    1:n|   |status     |     ||   |parent_id  | o---+|   +-----------+|1:n||   +-----------------+            +-----------------+|   | article         |            | feedback        ||   |-----------------|            |-----------------||   |id               |<--1:n--+   |id               ||   |title            |        |   |title            ||   |content          |        |   |content          ||   |datetime         |        |   |datetime         ||   |status           |        |   |status           |+--o|category_id      |        +--o|article_id       |+--o|member_id        |        +-->|member_id        ||   +-----------------+        |   +-----------------+|   | 2007,2008,2009  |        |   | 2007,2008,2009  ||   +-----------------+        |   +-----------------+|                              |1:n  +----------+     +---1:n---+|   | member   |     ||   |----------|     |+-->|id        | <---+|user      ||passwd    ||nickname  ||status    |+----------+

首先定义分类实体类

package common.domain;import java.util.Date;
import java.util.Set;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;import org.springframework.format.annotation.DateTimeFormat;import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;@Entity
@Table(indexes = { @Index(name = "name", columnList = "name DESC"), @Index(name = "path", columnList = "path") })
public class Category {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)public int id;public String name;public String description;public String path;@Column(columnDefinition = "enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '状态'")public String status;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")public Date ctime;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更改时间'")public Date mtime;@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })@JoinColumn(name = "pid", referencedColumnName = "id")private Category categorys;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "category", fetch = FetchType.EAGER)private Set<Category> category;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "category", orphanRemoval = true)private Set<Article> article;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public Date getCtime() {return ctime;}public void setCtime(Date ctime) {this.ctime = ctime;}public Date getMtime() {return mtime;}public void setMtime(Date mtime) {this.mtime = mtime;}public Category getCategorys() {return categorys;}public void setCategorys(Category categorys) {this.categorys = categorys;}public Set<Category> getCategory() {return category;}public void setCategory(Set<Category> category) {this.category = category;}@Overridepublic String toString() {return "Category [id=" + id + ", name=" + name + ", description=" + description + ", path=" + path + ", status="+ status + ", ctime=" + ctime + ", mtime=" + mtime + ", categorys=" + categorys + ", category="+ category + "]";}}

定义文章实体类

package common.domain;import java.io.Serializable;
import java.util.Date;
import java.util.Set;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;import org.springframework.format.annotation.DateTimeFormat;import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;@Entity
@Table(name = "article")
public class Article implements Serializable {private static final long serialVersionUID = 7603772682950271321L;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)public int id;public String title;@Column(name = "short")public String shortTitle;public String description;public String author;public int star;public String tag;public boolean share;public boolean status;public String content;@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })@JoinColumn(name = "category_id", referencedColumnName = "id")private Category category;@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })@JoinColumn(name = "site_id", referencedColumnName = "id")private Site site;@ManyToOne(cascade = { CascadeType.ALL })@JoinColumn(name = "member_id", referencedColumnName = "id")private Member member;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")public Date ctime;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更改时间'")public Date mtime;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "article", fetch = FetchType.EAGER)private Set<Comment> comment;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "article", fetch = FetchType.EAGER)private Set<Favorites> favorites;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, mappedBy = "article", orphanRemoval = true)private Set<Statistics> statistics;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public Date getCtime() {return ctime;}public void setCtime(Date ctime) {this.ctime = ctime;}public String getShortTitle() {return shortTitle;}public void setShortTitle(String shortTitle) {this.shortTitle = shortTitle;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public int getStar() {return star;}public void setStar(int star) {this.star = star;}public String getTag() {return tag;}public void setTag(String tag) {this.tag = tag;}public boolean isShare() {return share;}public void setShare(boolean share) {this.share = share;}public boolean isStatus() {return status;}public void setStatus(boolean status) {this.status = status;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Category getCategory() {return category;}public void setCategory(Category category) {this.category = category;}public Member getMember() {return member;}public void setMember(Member member) {this.member = member;}public Set<Comment> getComment() {return comment;}public void setComment(Set<Comment> comment) {this.comment = comment;}public Set<Favorites> getFavorites() {return favorites;}public void setFavorites(Set<Favorites> favorites) {this.favorites = favorites;}public Set<Statistics> getStatistics() {return statistics;}public void setStatistics(Set<Statistics> statistics) {this.statistics = statistics;}public Site getSite() {return site;}public void setSite(Site site) {this.site = site;}public Date getMtime() {return mtime;}public void setMtime(Date mtime) {this.mtime = mtime;}@Overridepublic String toString() {return "Article [id=" + id + ", title=" + title + ", shortTitle=" + shortTitle + ", description=" + description+ ", author=" + author + ", star=" + star + ", tag=" + tag + ", share=" + share + ", status=" + status+ ", content=" + content + ", category=" + category + ", site=" + site + ", member=" + member+ ", ctime=" + ctime + ", mtime=" + mtime + ", comment=" + comment + ", favorites=" + favorites+ ", statistics=" + statistics + "]";}}

希望结果

CREATE TABLE `category` (`id` int(11) NOT NULL AUTO_INCREMENT,`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '????',`description` varchar(255) DEFAULT NULL,`mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '????',`name` varchar(255) DEFAULT NULL,`path` varchar(255) DEFAULT NULL,`status` enum('Enabled','Disabled') DEFAULT 'Enabled' COMMENT '??',`pid` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `name` (`name`),KEY `path` (`path`),KEY `FKeiel7nqjxu4kmefso9tm9qcsu` (`pid`),CONSTRAINT `FKeiel7nqjxu4kmefso9tm9qcsu` FOREIGN KEY (`pid`) REFERENCES `category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `article` (`id` int(11) NOT NULL AUTO_INCREMENT,`author` varchar(255) DEFAULT NULL,`content` varchar(255) DEFAULT NULL,`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '????',`description` varchar(255) DEFAULT NULL,`mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '????',`share` bit(1) NOT NULL,`short` varchar(255) DEFAULT NULL,`star` int(11) NOT NULL,`status` bit(1) NOT NULL,`tag` varchar(255) DEFAULT NULL,`title` varchar(255) DEFAULT NULL,`category_id` int(11) DEFAULT NULL,`member_id` int(11) DEFAULT NULL,`site_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `FKy5kkohbk00g0w88fi05k2hcw` (`category_id`),KEY `FK6l9vkfd5ixw8o8kph5rj1k7gu` (`member_id`),KEY `FKrxbc33rok9m4n6pnbbwb3piwf` (`site_id`),CONSTRAINT `FK6l9vkfd5ixw8o8kph5rj1k7gu` FOREIGN KEY (`member_id`) REFERENCES `member` (`id`),CONSTRAINT `FKrxbc33rok9m4n6pnbbwb3piwf` FOREIGN KEY (`site_id`) REFERENCES `site` (`id`),CONSTRAINT `FKy5kkohbk00g0w88fi05k2hcw` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

现在我们已经将 categroy 与 article 两张表一对多关系建立起来。

12.5. ManyToMany 多对多

用户与角色就是一个多对多的关系,多对多是需要中间表做关联的。所以需要一个 user_has_role 表。

          +----------+          +---------------+            +--------+| users    |          | user_has_role |            | role   |+----------+          +---------------+            +--------+| id       | <------o | user_id       |      /---> | id     || name     |          | role_id       | o---+      | name   || password |          |               |            |        |+----------+          +---------------+            +--------+

创建 User 表

package cn.netkiller.api.domain.test;import java.io.Serializable;
import java.util.Set;import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.JoinColumn;@Entity
@Table(name = "users")
public class Users implements Serializable {/*** */private static final long serialVersionUID = -2480194112597046349L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private int id;private String name;private String password;@ManyToMany(fetch = FetchType.EAGER)@JoinTable(name = "user_has_role", joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") })private Set<Roles> roles;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Set<Roles> getRoles() {return roles;}public void setRoles(Set<Roles> roles) {this.roles = roles;}@Overridepublic String toString() {return "Users [id=" + id + ", name=" + name + ", password=" + password + ", roles=" + roles + "]";}}

创建 Role 表

package cn.netkiller.api.domain.test;import java.io.Serializable;
import java.util.Set;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;@Entity
@Table(name = "roles")
public class Roles implements Serializable {private static final long serialVersionUID = 6737037465677800326L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private int id;private String name;@ManyToMany(mappedBy = "roles")private Set<Users> users;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Set<Users> getUsers() {return users;}public void setUsers(Set<Users> users) {this.users = users;}@Overridepublic String toString() {return "Roles [id=" + id + ", name=" + name + ", users=" + users + "]";}}

最终产生数据库表如下

CREATE TABLE `users` (`id` INT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(255) NULL DEFAULT NULL,`password` VARCHAR(255) NULL DEFAULT NULL,PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB; CREATE TABLE `roles` (`id` INT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(255) NULL DEFAULT NULL,PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;CREATE TABLE `user_has_role` (`user_id` INT(11) NOT NULL,`role_id` INT(11) NOT NULL,PRIMARY KEY (`user_id`, `role_id`),INDEX `FKsvvq61v3koh04fycopbjx72hj` (`role_id`),CONSTRAINT `FK2dl1ftxlkldulcp934i3125qo` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),CONSTRAINT `FKsvvq61v3koh04fycopbjx72hj` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

12.6. 外键级联删除

orphanRemoval = true 可以实现数据级联删除

package cn.netkiller.api.domain;import java.io.Serializable;
import java.util.Set;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Table;import com.fasterxml.jackson.annotation.JsonIgnore;@Entity
@Table(name = "member")
public class Member implements Serializable {/*** */private static final long serialVersionUID = 1L;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)private int id;private String name;private String sex;private int age;private String wechat;@Column(unique = true)private String mobile;private String picture;private String ipAddress;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "member")private Set<Comment> comment;@JsonIgnore@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "member")private Set<StatisticsHistory> statisticsHistory;public Member() {}public Member(int id) {this.id = id;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getWechat() {return wechat;}public void setWechat(String wechat) {this.wechat = wechat;}public String getMobile() {return mobile;}public void setMobile(String mobile) {this.mobile = mobile;}public String getPicture() {return picture;}public void setPicture(String picture) {this.picture = picture;}public String getIpAddress() {return ipAddress;}public void setIpAddress(String ipAddress) {this.ipAddress = ipAddress;}@Overridepublic String toString() {return "Member [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", wechat=" + wechat + ", mobile=" + mobile + ", picture=" + picture + ", ipAddress=" + ipAddress + "]";}}

相关文章:

  1. 找到并留住最佳员工
  2. 让程序猿和攻城狮更敬业
  3. 攻城狮的自我营销
  4. 人力资源管理·培训与开发

Spring Data 数据库建模最佳实践相关推荐

  1. TigerGraph | 图数据库建模最佳实践

    (一)引言 我们知道,家庭物品收纳整理有个重要的原则,就是分类.人们不会把所有的衣物乱堆在一起,而会按衣服的用途.颜色等等进行分类,这样衣物才会井然有序,容易取用. 类似的,现代企业的数据容量爆发式增 ...

  2. 需求分析与建模最佳实践_社交媒体和主题建模:如何在实践中分析帖子

    需求分析与建模最佳实践 主题建模的实际使用 (Practical use of topic modeling) There is a substantial amount of data genera ...

  3. Mysql8官方分布式数据库MGR最佳实践

    MGR简介 MGR是mysql Group Replication简称,中文名称是Mysql组复制,MGR是MySQL官方于2016年12月推出的一个全新的高可用与高扩展的解决方案,提供了高可用.高扩 ...

  4. Spring Boot 3.x 最佳实践

    Spring Boot 3.x 最佳实践 Spring Boot不需要任何特定的代码布局.然而有一些最佳实践可以提供帮助. 代码结构 使用默认的包 当一个类没有包含包声明时,它会默认在"de ...

  5. java jpa性能_[Java Performance] 数据库性能最佳实践 - JPA和读写优化

    数据库性能最佳实践 当应用须要连接数据库时.那么应用的性能就可能收到数据库性能的影响. 比方当数据库的I/O能力存在限制,或者因缺失了索引而导致运行的SQL语句须要对整张表进行遍历.对于这些问题.只相 ...

  6. 20个数据库设计最佳实践

    2019独角兽企业重金招聘Python工程师标准>>> 数据库设计是整个程序的重点之一,为了支持相关程序运行,最佳的数据库设计往往不可能一蹴而就,只能反复探寻并逐步求精,这是一个复杂 ...

  7. 一键导入excel到数据库的最佳实践

    一键导入excel到数据库的最佳实践 摘要:通常情况下,我们需要将excel数据导入到数据库(比如mysql,sql server,oracle)进行数据分析处理.通常我们会用像navicate这样的 ...

  8. 21. Spring Boot 2.x最佳实践之 HTTPS 集成

    Spring Boot 2.x最佳实践之 HTTPS 集成 1. HTTPS 科普篇 1.1 什么是HTTP? 1.2 什么是HTTPS? 1.3 HTTP 和HTTPS 的区别 1.4 HTTPS ...

  9. 调优 DB2 UDB v8.1 及其数据库的最佳实践-IBM developerWorks

    http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0404mcarthur/#sqltuning 级别: 初级 ...

  10. Spring Boot 几条最佳实践!

    点击上方"朱小厮的博客",选择"设为星标" 回复"1024"获取独家整理的学习资料 Spring Boot是最流行的用于开发微服务的Java ...

最新文章

  1. linux文件系统中文件基本权限,Linux文件权限基本属性图文详解
  2. 如何快速判断某 URL 是否在 20 亿的网址 URL 集合中?
  3. STL中empty()函数的误用
  4. 国外值得关注的网站系列之二-社交化推荐网站GetGlue
  5. linux 系统频率,Linux下CPU的手动频率设定
  6. BaseAnimation
  7. Unity HoloLens 开发初识
  8. 分享2022上半年我读过的7本书
  9. 【考研攻略】北京交通大学网络空间安全专业2018-2022年考研数据分析
  10. 双路cpu比单路强多少_关于CPU单核单路/双路和双核双路的区别
  11. 如何利用Python爬虫爬取小说网站并保存到txt文件
  12. 硕士学位论文写作分享
  13. android开发学习:打电话和发短信
  14. 微信公众号 多台服务器,在多台 Linux 服务器上搭建 Pulsar 集群
  15. 手机版python3h如何自制游戏_教你如何用 Python 写一个小游戏
  16. 赠书活动 | 万亿级市场:大家都能看到的机会谈不上机会
  17. Java程序启动QQ(九)
  18. 【教程】Western 操作步骤
  19. 人在旅途——》张家界5天出行准备清单
  20. Golang 小技巧

热门文章

  1. [前端案例]百行代码实现炫酷时钟
  2. Qt和PyQt中的组合键输入捕获处理
  3. 来自资深会员管理人的深度思考
  4. SpringSecurity 密码加密
  5. (C#)Winform修改DateTimePicker控件的背景色Winform中日期控件DateTimePicker默认是不能修改背景色和边框色的
  6. word中突然有一行文字间距特别大的解决办法
  7. 最简单的51单片机软件定时器的编写
  8. MFC 0xC000041D 回调期间遇到未处理的异常
  9. 应用nslookup命令查看A记录、MX记录、CNAME记录和NS记录
  10. 表示整数x的绝对值大于5时值为真的c语言表达式是——.,1表示'整数x的绝对值大于5'时值为'真'的C语言表达式是_____...