JPA、Hibernate映射@OneToMany关系
@OneToMany注释分两种映射关联:
-
单向
@OneToMany
关联 -
双向
@OneToMany
关联
双向关联要求子实体使用@ManyToOne
注释。
单向@OneToMany
@Table
@Entity
public class School extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String name;
@OneToMany(cascade = CascadeType.ALL)
private List<Teacher> teacher = new ArrayList<>();
public School() {
super();
}
public School(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Teacher> getTeacher() {
return teacher;
}
public void setTeacher(List<Teacher> teacher) {
this.teacher = teacher;
}
}
@Table
@Entity
public class Teacher extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String name;
public Teacher() {
super();
}
public Teacher(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Teacher teacher1 = new Teacher("teacher1");
Teacher teacher2 = new Teacher("teacher2");
School school = new School("school1");
school.getTeacher().add(teacher1);
school.getTeacher().add(teacher2);
entityManager.persist(school);
entityManager.flush();
Hibernate将执行以下SQL语句:
insert into school (id, name) values (null, ?)
insert into teacher (id, name) values (null, ?)
insert into teacher (id, name) values (null, ?)
insert into school_teacher (school_id, teacher_id) values (?, ?)
insert into school_teacher (school_id, teacher_id) values (?, ?)
为什么执行了这么多查询?默认情况下,这就是单向@OneToMany
关联的工作方式,数据库表关系:
看起来更像是多对多关联并不是一对多关联,而且效率也不高。所以不推荐所用默认的单向@OneToMany
。
使用@JoinColumn的单向@OneToMany
要解决上述问题,我们只需要组合使用@JoinColumn
:
@Table
@Entity
public class Post extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column
private String title;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private List<Comment> comments = new ArrayList<>();
public Post() {
super();
}
public Post(String title) {
super();
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
}
@Table
@Entity
public class Comment extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String review;
public Comment() {
super();
}
public Comment(String review) {
super();
this.review = review;
}
public String getReview() {
return review;
}
public void setReview(String review) {
this.review = review;
}
}
Comment comment1 = new Comment("comment1");
Comment comment2 = new Comment("comment2");
Post post = new Post("post1");
post.getComments().add(comment1);
post.getComments().add(comment2);
entityManager.persist(post);
entityManager.flush();
Hibernate将执行以下SQL语句:
insert into post (id, title) values (null, ?)
insert into comment (id, review) values (null, ?)
insert into comment (id, review) values (null, ?)
update comment set post_id=? where id=?
update comment set post_id=? where id=?
数据库表关系:
双向@OneToMany
映射@OneToMany
关联的最佳方法是子实体配合使用@ManyToOne
:
@Entity
@Table
public class Unit extends AbstractEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Column
private String name;
@CreationTimestamp
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date createDatetime;
@OneToMany(mappedBy = "unit", cascade = CascadeType.ALL, orphanRemoval = true, targetEntity = User.class)
private List<User> users = new ArrayList<>();
//Getters and setters are omitted for brevity
public void addUser(User user) {
users.add(user);
user.setUnit(this);
}
public void removeUser(User user) {
users.remove(user);
user.setUnit(null);
}
}
@Entity
@Table
public class User extends AbstractEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Column(name = "name")
private String name;
@Column(name = "email", nullable = false, length = 250)
private String email;
@NotNull
@Range(max = 150, min = 1)
@Column
private Integer age;
@CreationTimestamp
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date createDatetime;
@ManyToOne(fetch = FetchType.LAZY)
private Unit unit;
//Getters and setters are omitted for brevity
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
User user = (User) o;
return Objects.equals(getId(), user.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
User user1 = new User();
user1.setAge(21);
user1.setCreateDatetime(new Date());
user1.setEmail("test51@163.com");
user1.setName("test51");
User user2 = new User();
user2.setAge(22);
user2.setCreateDatetime(new Date());
user2.setEmail("test52@163.com");
user2.setName("test52");
Unit unit = new Unit("游戏公司", new Date());
unit.addUser(user1);
unit.addUser(user2);
entityManager.persist(unit);
// orphanRemoval = true执行delete,默认:false
unit.removeUser(user1);
entityManager.flush();
Hibernate将执行以下SQL语句:
insert into unit (id, create_datetime, name) values (null, ?, ?)
insert into user (id, age, create_datetime, email, name, unit_id) values (null, ?, ?, ?, ?, ?)
insert into user (id, age, create_datetime, email, name, unit_id) values (null, ?, ?, ?, ?, ?)
delete from user where id=?
数据库表关系:
上述映射,注意几个事项:
-
@ManyToOne
关联使用FetchType.LAZY
,否则影响性能。 Unit
增加两个方法(addUser
和removeUser
),用于同步双向关联的双方关系。- User重写
equals
和hashCode
方法,可以使用唯一标记重写。 @OneToMany
关联使用mappedBy
,类似Hibernate中的inverse 。