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}级联策略。
版权声明:本文为博主原创文章,未经博主允许不得转载。