| 知乎專欄 | 多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者” |
目錄
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 cn.netkiller.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
我們希望為 name 和 path 欄位增加普通索引
package cn.netkiller.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
創建由多個欄位組成的復合索引,如: "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;
如下圖,我們將實現 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 cn.netkiller.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 cn.netkiller.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 兩張表一對多關係建立起來。
用戶與角色就是一個多對多的關係,多對多是需要中間表做關聯的。所以需要一個 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;
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 + "]";
}
}