本文节选自电子书《Netkiller Architect 手札》
出处:http://www.netkiller.cn
作者:netkiller , QQ:13721218, 订阅号:netkiller-ebook
第 12 章 Spring Data 数据库建模最佳实践
ORM的出现解决了程序猿学习数据库学历成本,也加快了开发的速度。程序猿无需再学习数据库定义语言DDL以及数据库客户端,也无需关注建表这些繁琐的工作,同时也降低了数据库结构变更管理中与DBA频繁沟通的成本。
在过去的两年中我们采用 Spring Data JPA 定义数据库,访问数据库,积累了很多经验,最终我们发现使用 Spring Data 实体定义完全可以代替 DBA 的建模工作。
下面我们采用案例,一个一个讲解,各种数据库实体关系的定义。相关数据库建模知识请先阅读 《Netkiller Architect 手札》 以及 《Netkiller Spring 手札》
这是一个通用分类表,常见的父子关系加上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; } @Override public 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; } @Override public 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.CreationTimestamp protected 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; } @Override public 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; } @Override public 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; } @Override public 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; } @Override public 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; } @Override public String toString() { return "Member [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", wechat=" + wechat + ", mobile=" + mobile + ", picture=" + picture + ", ipAddress=" + ipAddress + "]"; } }
相关文章: