今天讲的是其实是一个老生常谈的问题,但这前一直没有完全搞清楚,今天正好项目里遇到相关问题,仔细弄了一会儿,终于算是搞明白了。
不知道大家有没有遇到过这个问题,举例先:
@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);