今天讲的是其实是一个老生常谈的问题,但这前一直没有完全搞清楚,今天正好项目里遇到相关问题,仔细弄了一会儿,终于算是搞明白了。
不知道大家有没有遇到过这个问题,举例先:
@Entity
public class Teacher {
private String name;
private Set<Student> students;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "teacher_oid")
public Set<Student> getStudents() {
return students;
}
//其他代码省略.......
}
@Entity
public class Student {
private String name;
}
在以上的例子里, 如果保存teacher时,students里有记录,那么Hibernate会自动保存Teacher和Student的记录,但问题在于,控制台可能会生成以下几句SQL:
insert into teacher(name, id) values ("T1", 1);
insert into student(name, id) values("S1", 1);
update student set teacher_oid = 1 where id = 1;
请留意以上第3句SQL,是不是觉得很多余,为什么第二句SQL里不能带着teacher_oid一起呢?
这里回到主题,今天要讲的就是Hibernate的inverse的事,通过对inverse的配置,就可以去掉第3句SQL,用2句SQL完成操作。
题外话:在Hibernate4里面,我们主要都是在annotation去实现,很少使用hbm.xml配置文件,那么之前很多文档里提到的那个inverse在注解方式该如何使用呢。其实就是使用mappedBy来完成的。
将之前的代码改成:
@Entity
public class Teacher {
private String name;
private Set<Student> students;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "teacher")
//不需要这行了 @JoinColumn(name = "teacher_oid")
public Set<Student> getStudents() {
return students;
}
//其他代码省略.......
}
@Entity
public class Student {
private String name;
//需要定义Teacher的引用
private Teacher teacher;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "teacher_oid")
public Teacher getTeacher() {
}
}
也就是OneToMany端配置mappedBy,然后ManyToOne端必须配置One那端的对象,这么改动之后,优点是Hibernate生成的SQL更简洁了,但缺点也很明显,之前子类不需要关注父类,但现在这样,子类必须拥有父类的引用。
生成的SQL如下:
insert into teacher(name, id) values ("T1", 1);
insert into student(name, id, teacher_oid) values("S1", 1, 1);
至于项目中如何选择,就看各位的想法了。
另外,在OneToMany里有一个orphanRemoval的配置,默认为false,也就是在级联操作的情况,如果子类的记录发生删除操作,Hibernate只会将那些被删除的子类记录的外键ID设为Null,但不会删除记录。
如果设为true,则Hibernate会将那些子记录一并删除。
但在操作上需要注意,要按照以下方式:
Teacher teacher = service.findById(1);
//不要直接调用teacher.setStudnets,而是需要先clear,再addAll
teacher.getStudents().clear();
teacher.getStudents().addAll(newStudentsList);