3 OneToMany ManyToMany MappedBy Cascade

1 双向1-N关联

对于1-N关联,Hibernate推荐使用双向关联,而且不要让1的一方控制关联关系,而使用多的一方控制关联关系。

a. 一的一方  表示班级

@Entity
@Table(name="team_1")
public class Team
{
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="team_id")
	private int id;
	private String name;
	@OneToMany(mappedBy="team",targetEntity=Student.class,cascade=CascadeType.ALL)
	
	
	private Set<Student>students=new HashSet<Student>();
//省略set  get方法
}
b.多的一方  表示学生

@Entity
@Table(name="student_1")
public class Student
{
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="student_id")
	private int id;
	private String name;
	@ManyToOne(targetEntity=Team.class,fetch=FetchType.LAZY)
	@JoinColumn(name="team_id",nullable=false,referencedColumnName="team_id")
	private Team team;
//省略set  get方法
}
c.持久化代码

                        Team team=new Team();
			team.setName("20150225");
			session.persist(team);
			Student student=new Student();
			student.setName("zhangsan");
			student.setTeam(team);
			
			
			
			Student student2=new Student();
			student.setName("lisi");
			student2.setTeam(team);
			
			session.save(student);
			session.save(student2);
			
			tx.commit();
在持久化代码中,首先创建了一的一方 team对象,并对他进行了保存然后创建了多的一方,并进行了保存。这是符合客观事实的,即首先要有班级,然后才能有学生从属于某个班级。如果不这样做,那么将会抛出异常。对于这样的程序,应注意:

  • 最好先持久化Team对象。因为程序希望持久化student对象时,Hibernate可为Student的外键属性分配值,也就是说,象student表插入记录时,该记录的外键已制定了值,这表明他参照的主表记录已经存在。
  • 先设置student与team的关联关系(student2.setTeam(team);),然后再保存student。如果顺序反过来 ,将会增加update语句。
  • 不要通过team对象来设置关联关系,因为在team中已经指定了@MappedBy属性,该属性表明Team对象不能控制关联关系。所以,1.要将team的数据,赋给student,即用student的setTeam()方法去捆定team数据;2.在进行数据插入/更新session.save()/session.update()时,最后操作的是student.
  • 在team一方设置了cascade属性为ALL,而在@manyToOne一方不要设置级联属性

2 N-N双向关联

双向N-N关系需要两端都使用Set集合属性,两端都增加对集合属性的访问。双向N-N关系只能采用连接表的方式。

双向N-N关联需要在两端分别使用@ManyToMany修饰Set集合属性,并在两端都是用@JoinTable显示映射连接表。如果程序希望一端放弃控制关联关系,则可在这一端的@ManyToMany注解中指定mappedby属性,这一端就无须、也不能使用@JoinTable映射连接表了。

下面示例表示课程与学生的关系。一个课程可由多个学生选择,一个学生可以选择多门课程

@Entity
@Table(name="course_2")
public class Course
{
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="course_id")
	private int id;
	private String name;
	@ManyToMany(targetEntity=Student.class)
	@JoinTable
	(name="student_course",
	joinColumns=@JoinColumn(name="courseId",referencedColumnName="course_id"),
	inverseJoinColumns=@JoinColumn(name="studentId",referencedColumnName="student_id")
	)
	
	private Set<Student> courses=new HashSet<Student>();

}


@Entity
@Table(name="student_2")
public class Student
{ 
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="student_id")
	private int id;
	private String name;
	@ManyToMany(targetEntity=Course.class,cascade=CascadeType.ALL)
	@JoinTable
	(name="student_course",
	joinColumns=@JoinColumn(name="studentId",referencedColumnName="student_id"),
	inverseJoinColumns=@JoinColumn(name="courseId",referencedColumnName="course_id")
	)
	
	private Set<Course> courses=new HashSet<Course>();

}




3 Cascade

(1)对于关联实体而言,Hibernate默认不会启用级联操作,当父对象被保存时,他关联的子实体不会被保存。为了启用不同持久化操作的级联行为,Hibernate定了如下级联风格

  • CascadeType.ALL:指定Hibernate将所有的持久化操作都级联到关联实体。
  • CascadeType.MERGE:指定Hibernate将级联更新操作都级联到关联实体。
  • CascadeType.PERSIST:指定Hibernate将级联保存操作都级联到关联实体。
  • CascadeType.REFRESH:指定Hibernate将级联同步操作都级联到关联实体。
  • CascadeType.REMOVE:指定Hibernate将级联移除持久化操作都级联到关联实体。

(2)Hibernate还支持一个特殊的级联策略:删除孤儿对象(可通过@OneToMany,@OneToOne的orphanRemoval属性来启动级联策略)

<span style="font-size:18px;">@OneToMany(mappedBy="team",targetEntity=Student.class,cascade=CascadeType.ALL,orphanRemoval=true)</span>

该策略只能对应当前实体1的一端,且底层数据表为主表时有效。对于启用了roaphanRemoval策略的级联操作而言,当程序通过主表实体切断与从表实体的关联关系时-虽然此时主表实体对应的记录并没有删除,但由于从表实体失去了对主表实体的引用,因此这些从表实体成为了“孤儿”,hibernate会自动删除这些记录。

(3)对于级联的设定,Hibernate有如下建议

  • 在@ManyToOne中指定级联没有什么意义。级联通常在@OneToOne和@OneToMany关系中,因为级联操作应该是由主表记录传播到从表记录,通常从表记录不应该传播到主表记录。
  • 如果从表记录被完全限制在主表记录之内,则可以指定cascade=Cascade.ALL,再配合orphanRemoval=true级联策略,将从表实体的生命周期完全交给主表实体管理。
  • 如果经常在某个事务中同时使用主表实体和从表实体,则可以考虑cascade={CascadeType.PERSIST,CascadeType.MERGE}级联策略。














你可能感兴趣的:(3 OneToMany ManyToMany MappedBy Cascade)