@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关联的工作方式,数据库表关系:

image-20200412163737604

看起来更像是多对多关联并不是一对多关联,而且效率也不高。所以不推荐所用默认的单向@OneToMany

GitHub

使用@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=?

数据库表关系:

image-20200412165301271

GitHub

双向@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=?

数据库表关系:

image-20200412170139549

上述映射,注意几个事项:

  • @ManyToOne关联使用FetchType.LAZY,否则影响性能。

  • Unit增加两个方法(addUserremoveUser),用于同步双向关联的双方关系。
  • User重写equalshashCode方法,可以使用唯一标记重写。
  • @OneToMany关联使用mappedBy,类似Hibernate中的inverse

GitHub