知乎专栏 |
@JoinColumn与@Column注释类似,它的定义如下代码所示。
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface JoinColumn { String name() default ""; String referencedColumnName() default ""; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; }
定义外键名称 @ForeignKey(name = "picture_id")
@Id @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "id", foreignKey = @ForeignKey(name = "picture_id")) private Picture picture;
CREATE TABLE `picture_psychoanalysis` ( `analysis` text COMMENT '心里分析', `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `emotion` varchar(255) DEFAULT NULL COMMENT '感谢|愉快|抱怨|愤怒|喜爱|厌恶|恐惧|悲伤', `mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `replies` text COMMENT '建议回复话术', `sentiment` varchar(255) DEFAULT NULL COMMENT '负向情绪|中性情绪|正向情绪', `id` bigint unsigned NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `picture_id` FOREIGN KEY (`id`) REFERENCES `picture` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='文生图心里分析'
一对一表结构,如下面ER图所示,users表是用户表里面有登陆信息,profile 保存的时死人信息,这样的目的是我们尽量减少users表的字段,在频繁操作该表的时候性能比较好,另外一个目的是为了横向水平扩展。
+----------+ +------------+ | users | | profile | +----------+ +------------+ | id | <---1:1---o | id | | name | | sex | | password | | email | +----------+ +------------+
package cn.netkiller.api.domain.test; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "users") public class Users implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; private String password; 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; } @Override public String toString() { return "Users [id=" + id + ", name=" + name + ", password=" + password + "]"; } }
package cn.netkiller.api.domain.test; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; @Entity @Table(name = "profile") public class Profile implements Serializable { /** * */ private static final long serialVersionUID = -2500499458196257167L; @Id @OneToOne @JoinColumn(name = "id") private Users users; private int age; private String sex; private String email; public Users getUsers() { return users; } public void setUsers(Users users) { this.users = users; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "Profile [users=" + users + ", age=" + age + ", sex=" + sex + ", email=" + email + "]"; } }
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 `profile` ( `age` INT(11) NOT NULL, `email` VARCHAR(255) NULL DEFAULT NULL, `sex` VARCHAR(255) NULL DEFAULT NULL, `id` INT(11) NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK6x079ilawxjrfsljwyyi5ujjq` FOREIGN KEY (`id`) REFERENCES `users` (`id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB;
如果第二张表关联的并非主表的PK(主键)需要使用 referencedColumnName 指定。
@JoinColumn(name = "member_id",referencedColumnName="member_id")
package cn.netkiller.domain.demo; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; }
package cn.netkiller.domain.demo; import jakarta.persistence.*; @Entity public class BookDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private long numberOfPages; @OneToOne private Book book; }
CREATE TABLE `book` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci CREATE TABLE `book_detail` ( `id` bigint NOT NULL AUTO_INCREMENT, `number_of_pages` bigint NOT NULL, `book_id` bigint DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_29qtqq9pgixv8kqlt0woj1hyp` (`book_id`), CONSTRAINT `FKl1hmgccsvfwcxhem3qw6l7gpm` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
package cn.netkiller.domain.demo; import jakarta.persistence.*; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "book_detail") private BookDetail bookDetail; }
package cn.netkiller.domain.demo; import jakarta.persistence.*; @Entity public class BookDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private long numberOfPages; @OneToOne(cascade = CascadeType.ALL, mappedBy = "bookDetail") private Book book; }
CREATE TABLE `book` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `book_detail` bigint DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_d8im4vqhlm2eo0mj9lwjvib94` (`book_detail`), CONSTRAINT `FKagqqxsh6783b9dd9197ow49a5` FOREIGN KEY (`book_detail`) REFERENCES `book_detail` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci CREATE TABLE `book_detail` ( `id` bigint NOT NULL AUTO_INCREMENT, `number_of_pages` bigint NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
package cn.netkiller.domain.demo; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Users { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; }
package cn.netkiller.domain.demo; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.OneToOne; @Entity public class Profile { @Id @OneToOne private Users users; private int age; }
CREATE TABLE `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci CREATE TABLE `profile` ( `age` int NOT NULL, `users_id` bigint NOT NULL, PRIMARY KEY (`users_id`), CONSTRAINT `FKi6d1noonlpe6oyk6pnwc1q49e` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
指定一对一字段名,默认是表名+PK,例如上面的例子 users_id,如果我们希望自定义字段名,可以使用 @JoinColumn(name = "id")
package cn.netkiller.domain.demo; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import lombok.Data; @Entity @Data public class Profile { @Id @OneToOne @JoinColumn(name = "id") private Users users; private int age; private boolean sex; }
效果展示
CREATE TABLE `profile` ( `age` int NOT NULL, `sex` bit(1) NOT NULL, `id` bigint NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK6x079ilawxjrfsljwyyi5ujjq` FOREIGN KEY (`id`) REFERENCES `users` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
如果一对一的关系中,我们希望两端都是用 id 字段,而@OneToOne 一端是对象,必须定义一个 @Id,这时加入 @MapsId 可以解决 does not define an IdClass 错误,这时一段表中没有 @Id
package cn.netkiller.domain.demo; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @Entity @Table @Data @DynamicInsert @DynamicUpdate @Comment("用户表") public class Users { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String username; private String password; private boolean status; }
package cn.netkiller.domain.demo; import jakarta.persistence.Table; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.*; @Entity @Table @Data @DynamicInsert @DynamicUpdate @Comment("用户信息表") public class Profile { @Id @Column(name = "id") private Long id; @MapsId @OneToOne @JoinColumn(name = "id") @OnDelete(action = OnDeleteAction.CASCADE) private Users users; private int age; private boolean sex; }
package cn.netkiller.repository.demo; import cn.netkiller.domain.demo.Profile; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface TestRepository extends JpaRepository<Profile, Long> { }
CREATE TABLE `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `status` bit(1) NOT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表' CREATE TABLE `profile` ( `id` bigint NOT NULL, `age` int NOT NULL, `sex` bit(1) NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK6x079ilawxjrfsljwyyi5ujjq` FOREIGN KEY (`id`) REFERENCES `users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户信息表'
package cn.netkiller.domain.demo; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @Entity @Table @Data @DynamicInsert @DynamicUpdate @Comment("用户表") public class Users { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String username; private String password; private boolean status; @OneToOne(mappedBy = "users", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Profile profile; }
package cn.netkiller.domain.demo; import jakarta.persistence.Table; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.*; @Entity @Table @Data @DynamicInsert @DynamicUpdate @Comment("用户信息表") public class Profile { @Id @Column(name = "id") private Long id; @MapsId @OneToOne @JoinColumn(name = "id") @OnDelete(action = OnDeleteAction.CASCADE) private Users users; private int age; private boolean sex; }
CREATE TABLE `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `status` bit(1) NOT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表' CREATE TABLE `profile` ( `id` bigint NOT NULL, `age` int NOT NULL, `sex` bit(1) NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `FK6x079ilawxjrfsljwyyi5ujjq` FOREIGN KEY (`id`) REFERENCES `users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户信息表'
@OneToOne 保存提示 null identifier,经过排查需要配置
package cn.netkiller.domain; import jakarta.persistence.CascadeType; import jakarta.persistence.ForeignKey; import jakarta.persistence.Table; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.*; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @DynamicUpdate @DynamicInsert @Data @Comment("文生图心里分析") public class PicturePsychoanalysis implements Serializable { @Serial public static final long serialVersionUID = 1L; @Comment("Robert Plutchik 情感轮盘") @Column(columnDefinition = "json") public String plutchik; @Id @Column(name = "id") @Comment("Picture Id 一对一关系") private Long id; @MapsId @OneToOne(cascade = CascadeType.MERGE) @JoinColumn(name = "id", insertable = true, updatable = false, columnDefinition = "bigint unsigned", foreignKey = @ForeignKey(name = "picture_id")) @OnDelete(action = OnDeleteAction.CASCADE) private Picture picture; @Comment("负向情绪|中性情绪|正向情绪") private String sentiment; @Comment("感谢|愉快|抱怨|愤怒|喜爱|厌恶|恐惧|悲伤") private String emotion; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("建议回复话术") private String replies; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("心里分析") private String analysis; // public JSONObject plutchik; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @Comment("修改时间") private Date mtime; }
讲 @OneToOne 增加 @OneToOne(cascade = CascadeType.MERGE) 参数,如果 CascadeType.ALL 需要改为 CascadeType.MERGE
@Id @Column(name = "id") @Comment("Picture Id 一对一关系") private Long id; @MapsId @OneToOne(cascade = CascadeType.MERGE) @JoinColumn(name = "id", insertable = true, updatable = false, columnDefinition = "bigint unsigned", foreignKey = @ForeignKey(name = "picture_id")) @OnDelete(action = OnDeleteAction.CASCADE) private Picture picture;
我们要实现一个一对多实体关系,ER 图如下
+----------+ +------------+ | Classes | | Student | +----------+ +------------+ | id | <---+ | id | | name | | | name | +----------+ +--o | classes_id | +------------+
classes 表需要 OneToMany 注解,Student 表需要 ManyToOne 注解,这样就建立起了表与表之间的关系
package cn.netkiller.api.domain.test; import java.io.Serializable; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name="classes") public class Classes implements Serializable{ /** * */ private static final long serialVersionUID = -5422905745519948312L; @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; private String name; @OneToMany(cascade=CascadeType.ALL,mappedBy="classes") private Set<Student> students; 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<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } @Override public String toString() { return "classes [id=" + id + ", name=" + name + ", students=" + students + "]"; } }
package cn.netkiller.api.domain.test; import java.io.Serializable; import javax.persistence.CascadeType; 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; @Entity @Table(name = "student") public class Student implements Serializable{ /** * */ private static final long serialVersionUID = 6737037465677800326L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; // 若有多个cascade,可以是:{CascadeType.PERSIST,CascadeType.MERGE} @ManyToOne(cascade = { CascadeType.ALL }) @JoinColumn(name = "classes_id") private Classes classes; 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 Classes getClasses() { return classes; } public void setClasses(Classes classes) { this.classes = classes; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", classes=" + classes + "]"; } }
最终 SQL 表如下
CREATE TABLE `classes` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NULL DEFAULT NULL, PRIMARY KEY (`id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB; CREATE TABLE `student` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NULL DEFAULT NULL, `class_id` INT(11) NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `FKnsl7w2nw6o6eq53hqlxfcijpm` (`class_id`), CONSTRAINT `FKnsl7w2nw6o6eq53hqlxfcijpm` FOREIGN KEY (`class_id`) REFERENCES `classes` (`id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB;
Classes classes=new Classes(); classes.setName("One"); Student st1=new Student(); st1.setSname("jason"); st1.setClasses(classes); studentRepostitory.save(st1); Student st2=new Student(); st2.setSname("neo"); st2.setClasses(classes); studentRepostitory.save(st2);
用户与角色就是一个多对多的关系,多对多是需要中间表做关联的。所以我方需要一个 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 是 JPA 定义,并不是数据库原生
@Id @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "id", foreignKey = @ForeignKey(name = "picture_id")) private Picture picture;
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 + "]"; } }
cascade 属性: 指定级联操作的行为(可多选)
CascadeType.PERSIST:级联新增(又称级联保存):对A对象保存时也会对B对象进行保存。并且,只有A类新增时,会级联B对象新增。若B对象在数据库存在则抛异常。对应EntityManager的presist方法。 CascadeType.MERGE:级联合并(级联更新):指A类新增或者变化,会级联B对象(新增或者变化)。对应EntityManager的merge方法。 CascadeType.REMOVE:级联删除:只有A类删除时,会级联删除B类,即在设置的那一端进行删除时,另一端才会级联删除。对应EntityManager的remove方法。 CascadeType.REFRESH:级联刷新:获取A对象时也重新获取最新的B对象。对EntityManager的refresh(object)方法。即会重新查询数据库里的最新数据(用的比较少) CascadeType.DETACH:级联分离。 CascadeType.ALL:级联所有操作。
@OneToMany(mappedBy = "boss", cascade = CascadeType.PERSIST) private List<Staff> staffList;
@OneToMany(mappedBy = "boss", cascade = CascadeType.REMOVE) private List<Staff> staffList;
package cn.netkiller.domain; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @Data @DynamicUpdate @DynamicInsert @Comment("客户信息表") public class Consumer implements Serializable { @Serial public static final long serialVersionUID = -1L; @Id // @GeneratedValue(strategy = GenerationType.IDENTITY) // @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned") @Comment("唯一ID") // @Column(name = "id", nullable = false, columnDefinition = "int unsigned") private Integer id; // CascadeType.MERGE 集联更新,会更新外键表数据,覆盖 @OneToOne(cascade = CascadeType.MERGE, optional = false) @MapsId @JoinColumn(name = "id", nullable = false, insertable = true, updatable = false, columnDefinition = "int unsigned", foreignKey = @ForeignKey(name = "device_id")) @JsonIgnore private Device device; @Comment("姓名") @Column(length = 8) private String name; @Comment("昵称") @Column(length = 16) private String nickname; @Comment("头像") private String avatar; @Comment("性别") private Boolean gender; @Comment("年龄") private Integer age; @Comment("生日") @JsonFormat(pattern = "yyyy-MM-dd") @Temporal(TemporalType.DATE) @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday; @Comment("地址") private String address; @Comment("电话") @Column(length = 15) private String mobile; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @Comment("创建时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @Comment("修改时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date mtime; public Consumer() { } }
package cn.netkiller.domain; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import org.springframework.data.annotation.CreatedDate; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "business_id"})} // indexes = { // @Index(name = "key_device_unique", columnList = "name"), // @Index(name = "key_device_unique", columnList = "business_id") //} ) @DynamicUpdate @DynamicInsert @Data //@Builder @Comment("设备表") public class Device implements Serializable { @Serial public static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned") @Comment("主键") private Integer id; @Column(nullable = false, length = 32) @Comment("设备名称") private String name; @Column(unique = true, nullable = false, insertable = true, updatable = false, length = 32) @Comment("序列号") private String sn; @Comment("型号") @Column(length = 16) private String model; @Comment("固件版本") @Column(length = 16) private String fireware; @Comment("软件版本") @Column(length = 16) private String version; @Column(columnDefinition = "int unsigned") @Comment("存储容量") private Integer capacity = null; @Column(columnDefinition = "int unsigned") @Comment("可用容量") private Integer available; @Column(columnDefinition = "int unsigned") @Comment("内存容量") private Integer memory; @Comment("移动网络提供商") @Column(columnDefinition = "enum('中国移动','中国电信','中国联通','其他') DEFAULT NULL") private String provider; @Comment("IMEI") @Column(length = 15) private String imei; @Comment("MEID") @Column(length = 15) private String meid; @Comment("ICCID") @Column(length = 20) private String iccid; @Comment("wlan 无线 mac 地址") @Column(length = 17) private String wlan; @Column(length = 17) @Comment("蓝牙 mac 地址") private String bluetooth; @Comment("品牌") private String brand; @Comment("CPU") private String cpu; @Comment("设备驱动") private String device; @Comment("显示屏") private String display; @Comment("设备标识") private String fingerprint; @Comment("硬件") private String hardware; @Comment("制造商") private String manufacturer; @Comment("产品") private String product; @Column(columnDefinition = "enum('Y','N') DEFAULT 'N'") @Comment("设备状态,正常=Y,其他=N") private String status; @Comment("设备状态原因") @Column(columnDefinition = "enum('未激活','激活','充值','正常','禁用','冻结') DEFAULT '正常'") private String reason; @Comment("设备状态描述") private String description; @CreatedDate @Column(nullable = true, insertable = true, updatable = true) @Temporal(TemporalType.TIMESTAMP) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("访问时间") private Date atime; @ManyToOne @JoinColumn(name = "business_id") private Business business; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") // @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") // @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("修改时间") private Date mtime; public Device() { } }
@Transactional public Optional<Consumer> update(String deviceName, Consumer consumer) { Device device = new Device(); device.setId(consumer.getId()); device.setName(deviceName); consumer.setDevice(device); log.debug(consumer.toString()); consumer = consumerRepository.save(consumer); return Optional.ofNullable(consumer); }
向 Consumer 表中增加数据,Device 表受到影响,会修改里面的数据。这就是 CascadeType.MERGE 作用,如果不想影响 Device 数据,改为 CascadeType.PERSIST
@OneToOne(cascade = CascadeType.MERGE, optional = false)
@OnDelete(action = OnDeleteAction.CASCADE)
package cn.netkiller.domain; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @DynamicUpdate @Data @Comment("文生图心里分析") public class PicturePsychoanalysis implements Serializable { @Serial public static final long serialVersionUID = 1L; @Id @OneToOne() @JoinColumn(name = "id", foreignKey = @ForeignKey(name = "picture_id")) @OnDelete(action = OnDeleteAction.CASCADE) private Picture picture; // @Column(unique = true, nullable = false, insertable = true, updatable = false) // @Comment("会话") // private String session; @Comment("负向情绪|中性情绪|正向情绪") private String sentiment; @Comment("感谢|愉快|抱怨|愤怒|喜爱|厌恶|恐惧|悲伤") private String emotion; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("建议回复话术") private String replies; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("心里分析") private String analysis; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @Comment("修改时间") private Date mtime; }
CREATE TABLE `picture_psychoanalysis` ( `analysis` text COMMENT '心里分析', `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `emotion` varchar(255) DEFAULT NULL COMMENT '感谢|愉快|抱怨|愤怒|喜爱|厌恶|恐惧|悲伤', `mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `replies` text COMMENT '建议回复话术', `sentiment` varchar(255) DEFAULT NULL COMMENT '负向情绪|中性情绪|正向情绪', `id` bigint unsigned NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `picture_id` FOREIGN KEY (`id`) REFERENCES `picture` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='文生图心里分析'
@JoinTable(name = "table")
@OneToMany(cascade = CascadeType.ALL) @JoinTable private List<UserProfile> userProfile;
@JoinTable(name = "cust_user", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id") )
@JoinTable(name = "cust_user", inverseJoinColumns = @JoinColumn(name = "user_ext_id", referencedColumnName = "id") )
@JoinTable(name = "cust_user", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "user_ext_id", referencedColumnName = "id"), uniqueConstraints = { @UniqueConstraint(name = "unique_user_id", columnNames = {"user_id"}), @UniqueConstraint(name = "unique_user_ext_id", columnNames = {"user_ext_id"}) } )
package cn.netkiller.domain; import jakarta.persistence.*; import lombok.Data; import java.io.Serializable; @Entity @Table @Data public class Consumer implements Serializable { public static final long serialVersionUID = 7998903421265538801L; public String firstName; public String lastName; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned") public Integer id; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinTable(name = "consumer_has_device", joinColumns = {@JoinColumn(name = "consumer_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "device_id", referencedColumnName = "id")}) private Device device; public Consumer() { } }
package cn.netkiller.domain; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicUpdate; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @DynamicUpdate @Data @Comment("设备表") public class Device implements Serializable { @Serial public static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned") @Comment("主键") private Integer id; @Comment("设备名称") private String name; @Comment("型号") private String model; @Comment("版本") private String fireware; @Comment("版本") private String version; @Column(unique = true, nullable = false, insertable = true, updatable = false) @Comment("序列号") private String sn; @Comment("ip") private String ip; @Comment("mac") private String mac; @Temporal(TemporalType.TIMESTAMP) @Comment("最后一次登陆时间") private Date lastTime; @Column(columnDefinition = "enum('Y','N') DEFAULT 'N'") @Comment("设备状态") private boolean status; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @Comment("修改时间") private Date mtime; }
CREATE TABLE `consumer` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(255) DEFAULT NULL, `last_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci CREATE TABLE `device` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `fireware` varchar(255) DEFAULT NULL COMMENT '版本', `ip` varchar(255) DEFAULT NULL COMMENT 'ip', `last_time` datetime(6) DEFAULT NULL COMMENT '最后一次登陆时间', `mac` varchar(255) DEFAULT NULL COMMENT 'mac', `model` varchar(255) DEFAULT NULL COMMENT '型号', `mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `name` varchar(255) DEFAULT NULL COMMENT '设备名称', `sn` varchar(255) NOT NULL COMMENT '序列号', `status` enum('Y','N') DEFAULT 'N' COMMENT '设备状态', `version` varchar(255) DEFAULT NULL COMMENT '版本', PRIMARY KEY (`id`), UNIQUE KEY `UK_bg7pgyvfwv0q65tmquumxff3d` (`sn`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备表' CREATE TABLE `consumer_has_device` ( `device_id` int unsigned DEFAULT NULL, `consumer_id` int unsigned NOT NULL, PRIMARY KEY (`consumer_id`), UNIQUE KEY `UK_ibck20j1s6ch97lncg99uvpgh` (`device_id`), CONSTRAINT `FKcottusf6sx3bnouahp29vjdwk` FOREIGN KEY (`device_id`) REFERENCES `device` (`id`), CONSTRAINT `FKq7u4eyw8pmfkwg4yymjljx8ra` FOREIGN KEY (`consumer_id`) REFERENCES `consumer` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
// JPA 默认根据 Student 的 ID 主键对 studentList 集合数据进行递增排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy private List<Student> studentList; // 手动指定 id 字段的排序方式,ASC 递增排序,DESC 递减排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy("id desc") private List<Student> studentList; // 手动指定按照 salary 属性进行递减排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy("salary desc") private List<Student> studentList; // 手动指定按照多个属性进行排序 // 下面将根据 sex 和 salay 进行递增排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy("sex,salary") private List<Student> studentList; // 下面将根据 sex 递增排序,salary 递增排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy("sex asc,salary asc") private List<Student> studentList; // 下面将根据 sex 递增排序,salary 递减排序 @OneToMany(cascade = CascadeType.ALL) @OrderBy("sex asc,salary desc") private List<Student> studentList;
当使用 @OneToMany 与 @ManyToOne 相互引用时,你会发现产生了循环调用,此时可以使用 @JsonIgnoreProperties 避免出现循环,此时加入 @Transient 或 @JsonIgnore 注解可以解决,但要该字段就没有外键数据了。
@Entity public class Subarea { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @OneToMany @JsonIgnoreProperties(value = {"subarea"}) @JoinColumn(name = "subarea_id") private List<SubareaSet> subareaSetList; } @Entity public class SubareaSet { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @ManyToOne @JsonIgnoreProperties(value = {"subareaSetList"}) private Subarea subarea; }
optional = true 允许外键为空
@Comment("合作商") @ManyToOne(fetch = FetchType.LAZY, optional = true) @JoinColumn(name = "business_id", updatable = false) @JsonIgnore private Business business;
@Comment("合作商") @ManyToOne(fetch = FetchType.LAZY, optional = true) @JoinColumn(name = "business_id", updatable = false) @JsonIgnore private Business business;
package cn.netkiller.domain.story; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @DynamicUpdate @DynamicInsert @Data @Comment("图片表") //@SecondaryTable(name = "picture_psychoanalysis", // pkJoinColumns = @PrimaryKeyJoinColumn(name = "id") //) public class Picture implements Serializable { @Serial public static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "bigint unsigned") @Comment("主键") private Long id; @Comment("设备") private String device; @Column(unique = true, nullable = false, insertable = true, updatable = false, length = 36) @Comment("会话") private String session; @Comment("提示词") private String prompt; @Comment("英文提示词") @Column(length = 1024) private String translate; @Column(nullable = true) @Comment("缩图") private String thumbnail; @Column(nullable = true) @Comment("原图") private String image; @Column(nullable = true) @Comment("故事音频") private String audio; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("故事") private String story; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("拼音") private String pinyin; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("英文") private String english; @Comment("共享") private boolean share; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("点赞数") private Integer likes; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("收藏数") private Integer favorites; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("转发数") private Integer forward; @Comment("人工审核,NULL 未审核,true 通过,false 未通过") private Boolean audit; @ManyToOne(fetch = FetchType.LAZY, optional = true) @JsonIgnore @JoinColumn(nullable = true) private PictureGroup pictureGroup; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("修改时间") private Date mtime; }
package cn.netkiller.domain.story; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import java.io.Serializable; import java.util.Date; import java.util.List; @Entity @Table @DynamicUpdate @DynamicInsert @Data @Comment("图片组") public class PictureGroup implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "int unsigned") @Comment("主键") private Integer id; @Comment("组名") @Column(unique = true, length = 50) private String name; @OneToMany(mappedBy = "pictureGroup", fetch = FetchType.EAGER) private List<Picture> picture; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("修改时间") private Date mtime; }
CREATE TABLE `picture` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `device` varchar(255) DEFAULT NULL COMMENT '设备', `favorites` int unsigned NOT NULL DEFAULT '0' COMMENT '收藏', `forward` int unsigned NOT NULL DEFAULT '0' COMMENT '转发', `image` varchar(255) DEFAULT NULL COMMENT '原图', `likes` int unsigned NOT NULL DEFAULT '0' COMMENT '点赞', `model` varchar(255) DEFAULT NULL COMMENT '型号', `mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `prompt` varchar(255) DEFAULT NULL COMMENT '提示词', `session` varchar(36) NOT NULL COMMENT '会话', `share` bit(1) NOT NULL COMMENT '共享\r\n0、未共享\r\n1、已共享', `story` text COMMENT '故事', `thumbnail` varchar(255) DEFAULT NULL COMMENT '缩图', `audio` varchar(255) DEFAULT NULL COMMENT '故事音频', `translate` varchar(1024) DEFAULT NULL COMMENT '英文提示词', `pinyin` text COMMENT '拼音', `english` text COMMENT '英文', `audit` bit(1) DEFAULT NULL COMMENT '人工审核\r\n0、审核未通过\r\n1、审核通过\r\n', `picture_group_id` int unsigned DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_6cgmptiqeq2nusk40x5io55oh` (`session`), KEY `index_Is_Charging_Id` (`is_charging`,`device`,`charging_time`) USING BTREE, KEY `FK8qckqvv3cl83c9ant5wx1f6eo` (`picture_group_id`), CONSTRAINT `FK8qckqvv3cl83c9ant5wx1f6eo` FOREIGN KEY (`picture_group_id`) REFERENCES `picture_group` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5846 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='图片表'
@ManyToOne 关系 PictureGroup 加载后会出很多数据,在网上传输比较占用带宽,我们要精简 JSON 数据,实现的方式是讲 pictureGroup 影射给 groupId
package cn.netkiller.domain.story; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.Comment; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import java.io.Serial; import java.io.Serializable; import java.util.Date; @Entity @Table @DynamicUpdate @DynamicInsert @Data @Comment("图片表") public class Picture implements Serializable { @Serial public static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "bigint unsigned") @Comment("主键") private Long id; @Comment("设备") private String device; @Column(unique = true, nullable = false, insertable = true, updatable = false, length = 36) @Comment("会话") private String session; @Comment("提示词") private String prompt; @Comment("英文提示词") @Column(length = 1024) private String translate; @Column(nullable = true) @Comment("缩图") private String thumbnail; @Column(nullable = true) @Comment("原图") private String image; @Column(nullable = true) @Comment("故事音频") private String audio; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("故事") private String story; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("拼音") private String pinyin; @Lob @Basic(fetch = FetchType.LAZY) @Column(nullable = true, columnDefinition = "text") @Comment("英文") private String english; @Comment("共享") private boolean share; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("点赞数") private Integer likes; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("收藏数") private Integer favorites; @Column(columnDefinition = "int unsigned NOT NULL DEFAULT '0'") @Comment("转发数") private Integer forward; @Comment("人工审核,NULL 未审核,true 通过,false 未通过") private Boolean audit; @ManyToOne(fetch = FetchType.EAGER, optional = true) @JsonIgnore @JoinColumn(nullable = true) private PictureGroup pictureGroup; @Column(insertable = false, updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("创建时间") private Date ctime; @Column(nullable = true, insertable = false, updatable = false, columnDefinition = "TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Comment("修改时间") private Date mtime; @JsonProperty private Integer groupId; private void setGroupId(Integer groupId) { this.groupId = groupId; } private Integer getGroupId() { groupId = pictureGroup.getId(); return groupId; } }