Spring Data 数据库建模最佳实践
本文节选自电子书《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 + "]";}}
相关文章:
- 找到并留住最佳员工
- 让程序猿和攻城狮更敬业
- 攻城狮的自我营销
- 人力资源管理·培训与开发
Spring Data 数据库建模最佳实践相关推荐
- TigerGraph | 图数据库建模最佳实践
(一)引言 我们知道,家庭物品收纳整理有个重要的原则,就是分类.人们不会把所有的衣物乱堆在一起,而会按衣服的用途.颜色等等进行分类,这样衣物才会井然有序,容易取用. 类似的,现代企业的数据容量爆发式增 ...
- 需求分析与建模最佳实践_社交媒体和主题建模:如何在实践中分析帖子
需求分析与建模最佳实践 主题建模的实际使用 (Practical use of topic modeling) There is a substantial amount of data genera ...
- Mysql8官方分布式数据库MGR最佳实践
MGR简介 MGR是mysql Group Replication简称,中文名称是Mysql组复制,MGR是MySQL官方于2016年12月推出的一个全新的高可用与高扩展的解决方案,提供了高可用.高扩 ...
- Spring Boot 3.x 最佳实践
Spring Boot 3.x 最佳实践 Spring Boot不需要任何特定的代码布局.然而有一些最佳实践可以提供帮助. 代码结构 使用默认的包 当一个类没有包含包声明时,它会默认在"de ...
- java jpa性能_[Java Performance] 数据库性能最佳实践 - JPA和读写优化
数据库性能最佳实践 当应用须要连接数据库时.那么应用的性能就可能收到数据库性能的影响. 比方当数据库的I/O能力存在限制,或者因缺失了索引而导致运行的SQL语句须要对整张表进行遍历.对于这些问题.只相 ...
- 20个数据库设计最佳实践
2019独角兽企业重金招聘Python工程师标准>>> 数据库设计是整个程序的重点之一,为了支持相关程序运行,最佳的数据库设计往往不可能一蹴而就,只能反复探寻并逐步求精,这是一个复杂 ...
- 一键导入excel到数据库的最佳实践
一键导入excel到数据库的最佳实践 摘要:通常情况下,我们需要将excel数据导入到数据库(比如mysql,sql server,oracle)进行数据分析处理.通常我们会用像navicate这样的 ...
- 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 ...
- 调优 DB2 UDB v8.1 及其数据库的最佳实践-IBM developerWorks
http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0404mcarthur/#sqltuning 级别: 初级 ...
- Spring Boot 几条最佳实践!
点击上方"朱小厮的博客",选择"设为星标" 回复"1024"获取独家整理的学习资料 Spring Boot是最流行的用于开发微服务的Java ...
最新文章
- linux文件系统中文件基本权限,Linux文件权限基本属性图文详解
- 如何快速判断某 URL 是否在 20 亿的网址 URL 集合中?
- STL中empty()函数的误用
- 国外值得关注的网站系列之二-社交化推荐网站GetGlue
- linux 系统频率,Linux下CPU的手动频率设定
- BaseAnimation
- Unity HoloLens 开发初识
- 分享2022上半年我读过的7本书
- 【考研攻略】北京交通大学网络空间安全专业2018-2022年考研数据分析
- 双路cpu比单路强多少_关于CPU单核单路/双路和双核双路的区别
- 如何利用Python爬虫爬取小说网站并保存到txt文件
- 硕士学位论文写作分享
- android开发学习:打电话和发短信
- 微信公众号 多台服务器,在多台 Linux 服务器上搭建 Pulsar 集群
- 手机版python3h如何自制游戏_教你如何用 Python 写一个小游戏
- 赠书活动 | 万亿级市场:大家都能看到的机会谈不上机会
- Java程序启动QQ(九)
- 【教程】Western 操作步骤
- 人在旅途——》张家界5天出行准备清单
- Golang 小技巧
热门文章
- [前端案例]百行代码实现炫酷时钟
- Qt和PyQt中的组合键输入捕获处理
- 来自资深会员管理人的深度思考
- SpringSecurity 密码加密
- (C#)Winform修改DateTimePicker控件的背景色Winform中日期控件DateTimePicker默认是不能修改背景色和边框色的
- word中突然有一行文字间距特别大的解决办法
- 最简单的51单片机软件定时器的编写
- MFC 0xC000041D 回调期间遇到未处理的异常
- 应用nslookup命令查看A记录、MX记录、CNAME记录和NS记录
- 表示整数x的绝对值大于5时值为真的c语言表达式是——.,1表示'整数x的绝对值大于5'时值为'真'的C语言表达式是_____...