hibernate笔记
部门代码 Department.java:
packagecom.cn.domain;
importjava.util.HashSet;
importjava.util.Set;
publicclassDepartment{
privateIntegerid;
privateStringname;
privateSet<Employee>emps=newHashSet<Employee>();
publicSet<Employee>getEmps(){
returnemps;
}
publicvoidsetEmps(Set<Employee>emps){
this.emps=emps;
}
publicIntegergetId(){
returnid;
}
publicvoidsetId(Integerid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
}
员工代码Employee.java:
packagecom.cn.domain;
publicclassEmployee{
privateIntegerid;
privateStringuserName;
privateDepartmentdepart;
publicIntegergetId(){
returnid;
}
publicvoidsetId(Integerid){
this.id=id;
}
publicStringgetUserName(){
returnuserName;
}
publicvoidsetUserName(StringuserName){
this.userName=userName;
}
publicDepartmentgetDepart(){
returndepart;
}
publicvoidsetDepart(Departmentdepart){
this.depart=depart;
}
}
Employee.hbm.xml映射文件:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.cn.domain">
<classname="Employee"table="employee">
<idname="id">
<columnname="id"/>
<generatorclass="native"/>
</id>
<propertyname="userName"/>
<many-to-onename="depart"column="depart_id"/>
</class>
</hibernate-mapping>
Depart.hbm.xml映射文件:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.cn.domain">
<classname="Department"table="department">
<idname="id">
<columnname="id"/>
<generatorclass="native"></generator>
</id>
<propertyname="name"/>
<setname="emps">
<key>
<columnname="depart_id"></column>
</key>
<one-to-manyclass="Employee"/>
</set>
</class>
</hibernate-mapping>
HibernateDepartmentDAO.java文件:
packagecom.cn.dao;
importjava.util.HashSet;
importjava.util.Set;
importorg.hibernate.Session;
importorg.hibernate.Transaction;
importcom.cn.domain.Department;
importcom.cn.domain.Employee;
importcom.cn.session.HibernateSessionUtils;
publicclassHibernateDepartmentDAO{
publicstaticvoidmain(String[]args){
add();
}
publicstaticDepartmentadd(){
Sessionsession=HibernateSessionUtils.getSession();
Transactiontrans=session.beginTransaction();
Set<Employee>emps=newHashSet<Employee>();
Departmentdepart=newDepartment();
depart.setName("财务部");
Employeeemp1=newEmployee();
emp1.setUserName("wgz");
//告知部员工一他属于哪个部门
emp1.setDepart(depart);
Employeeemp2=newEmployee();
emp2.setUserName("cdx");
//告知部员工二他属于哪个部门
emp2.setDepart(depart);
emps.add(emp1);
emps.add(emp2);
//告知部门它有哪些员工
depart.setEmps(emps);
//保存员工一信息
session.save(emp1);
//保存员工二信息
session.save(emp2);
//保存部门信息
session.save(depart);
trans.commit();
HibernateSessionUtils.close();
returnnull;
}
}
注意:本例映射文件没有配级联和inverse
emps.add(emp1);
emps.add(emp2);
session.save(depart);
先保存的是部门员工信息,后保存的是部门信息,下面来看下结果输出地sql语句:
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintodepartment(name)values(?)
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
其中的
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintodepartment(name)values(?)
很好理解,他是有下面语句产生的:
session.save(emp1);
session.save(emp2);
session.save(depart);
但是下面这是条更新语句是怎么回事呢?这两天想了好久还是有点不太明白,最后联系现实生活的例子,勉强给下面的解释,不知得不对,反正我这样理解:
如果我把这两条给注解掉
emp1.setDepart(depart);
emp2.setDepart(depart);
输出的sql语句是:
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintodepartment(name)values(?)
Hibernate:updateemployeesetdepart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
可以发现
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
这两条更新语句不见了,由此可以推段导致上面两句update语句不见是emp1.setDepart(depart);
emp2.setDepart(depart);
这两句被注解掉的原因,下面来分析一下什么原因:
当执行到这句话emp2.setDepart(depart);已经建立了部门与员工表之间的关联。但执行session.save(emp1);这就话时员工以还不知道他是那个部门,同样执行session.save(emp1);时员工与不知他在哪个部门,所以在持久化时depart_id还是空的。打个比方:当一个公司招聘了两个员工,但是没给他分配他隶属在哪个部门(假设这个部门公司还没有打算建立该部门,先招员工),当员工招进公司后,他当然应该有一个自己员工编号的属性(id),名字的属性(userName),理所当然也应该有个隶属那个部门的部门编号(depart_id),但是员工不知道自己在那个部门,当然部门也没通知员工在那个部门。这是保存在数据库中的的employee表中的depat_id应该是null的了。当执行session.save(depart);语句时,这时部门已经建好了,他通知那些员工隶属这个部门,同时通知员工更新员工更新自己档案袋里的depart_id的信息。所以会产生
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
这两个更新语句
这是我把
emp1.setDepart(depart);
emp2.setDepart(depart);
这两句注解给去掉,输出地sql语句是:
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintoemployee(userName,depart_id)values(?,?)
Hibernate:insertintodepartment(name)values(?)
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
下面来分析一下原因:
这里还是假设:公司部门在招进员工时正在组建部门。
当员工招进公司后,他当然应该有一个自己员工编号的属性(id),名字的属性(userName),理所当然也应该有个隶属那个部门的部门编号(depart_id),由于不知道自己部门的编号,所以档案中的depart_id就写成null了,这时员工进公司也被告知他在哪个部门部门也知道它有哪些员工,但是部门经理说了由于我们部门刚建立公司还没给我们部门分配部门编号,你们档案的depart_暂时先更新为null吧,(于是
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
Hibernate:updateemployeesetuserName=?,depart_id=?whereid=?
这两条更新语句便产生了)
等以后公司给我们部门分配部门编号后,我通知你们部门编号是什么,那时你们再把你们的depart_id信息给更新,当执行session.save(depart);这个语句是部门已经建立了,公司也给分配了部门的编号,部门经理这时候通知该部门所有员工:“公司现在由部门编号了,你们可以更新你们档案里的depart_id信息了”,于是便有了
Hibernate:updateemployeesetdepart_id=?whereid=?
Hibernate:updateemployeesetdepart_id=?whereid=?
这两条update语句
写完后,感觉自己理解的好牵强,希望哪位大侠看到后给指证下inverse常用于一对多双向关联关系中。
以Student(学生)和Class(班级)为例,它们之间的关系为一对多的关系,即一个学生只能属于一个班级,一个班级可以包含多个学
生。
学生类定义代码:
ClassStudent{
privateintid;
privateStringname;
privateClassclass;
//省略getter()和setter()方法
}
班级类定义代码:
ClassClass{
privateintid;
privateStringname;
privateSetstudents=newHashSet();
//省略getter()和setter()方法
}
Student类的映射文件:
<classname="Student"table="STUDENT">
<idname="id"type="int"column="ID">
<generatorclass="native"/>
</id>
<propertyname="name"type="string"column="NAME"/>
<many-to-onename="class"column="CLASS_ID"class="Class"cascade="save-update"/>
</class>
Class类的映射文件:
<classname="Class"table="CLASS">
<idname="id"type="int"column="ID">
<generatorclass="native"/>
</id>
<propertyname="name"type="string"column="NAME"/>
<setname="students"table="STUDENT"cascade="save-update"inverse="false">
<keycolumn="CLASS_ID"/>
<one-to-manyclass="Student"/>
</set>
</class>
希望你能对这两个映射文件所表达的数据库模式有正确的认识。即STUDENT表中存在一个名为CLASS_ID的字段,它和CLASS表中的ID字段是主外键关系。那个inverse属性就是用来规定是由谁(Student或Class)来维护这个主外键关系的。
inverse的默认值为false。
在处理逻辑代码中,如下:
Classc1=newClass();
c1.setName("一班");
Students1=newStudent();
Students2=newStudent();
s1.setName("Jason");
s2.setName("Tom");
c1.getStudents().add(s1);
c2.getStudents().add(s2);
s1.setClass(c1);
s2.setClass(c1);//注释1
session.save(c1);
上面的代码会使Hibernate执行五条SQL语句,其中前三条是insert插入语句,后两条是update更新语句。插入就不用说了,那么为什么还要有更新语句呢?这是因为Class类映射文件的<set>元素中指定了inverse="false",这就告之Hibernate:STUDENT表与CLASS表的主外键关系是由Class类来维护的。当执行save后,执行了三条insert语句,这三条语句中的后两条是插入到STUDENT表的,它们的CLASS_ID字段是通过s1.getClass().getID()取出的,假如我将上面“注释1”处修改为s2.setClass(c2);(c2是另一个Class对象,可能是持久化对象),这样,主外键关系不就乱了吗。为了保证主外键关系,Hibernate在这种情况下会再执行两条update语句来更改STUDENT表中两个新插入记录的CLASS_ID字段,当然,这时CLASS_ID字段的取值是从c1对象中直接取得,而不再是s1.getClass().getID()方式了。
如果我们将Class类映射文件的<set>元素中的inverse属性修改为true,这就是告诉Hibernate:Class类不维护主外键关系了,这个任务就交给了Student类。于是,我们再执行上面的代码,Hibernate就会只执行三条insert语句,而不会执行任何update语句。因为Hibernate会通过Student类的s1.getClass().getID()和s2.getClass().getID()来确定CLASS_ID字段的值。
故,为了节省数据库资源,省却不必要的update语句,我们一般建议在一对多双向关联关系中,将一方的inverse属性设置为true,即将主外键的关系交由多方来维护。
打个比方:在一个公司中,是老板认识所有的员工容易,还是所有员工认识老板容易?