1.1 Hibernate关联关系的映射 :

1.1.1 实体之间的关系:

实体之间有三种关系:

* 一对多:

* 一个用户,生成多个订单,每一个订单只能属于一个用户.

* 建表原则:

* 在多的一方创建一个字段,作为外键,指向一的一方的主键.

* 多对多:

* 一个学生可以选择多门课程,一个课程可以被多个学生选择.

* 建表原则:

* 创建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.

* 一对一:(特殊.最少.)

* 一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用.(否则将两个表建到一个

表.)

* 建表原则:

* 唯一外键:

* 一对一的双方,假设一方是多的关系.需要在多的一方创建一个字段,作为外键.指向一

的一方的主键.但是在外键添加一个unique.

* 主键对应:

* 一对一的双方,通过主键进行关联.


1.1.2 Hibernate中一对多的配置 :(*****)

数据库准备:

create table customer2(

cid INT primary KEY auto_increment,

cname varchar(30)

)



insert into customer2 values(1,'hyf');

insert into customer2 values(2,'fy');

insert into customer2 values(3,'haha');



create table orders(

oid INT PRIMARY KEY auto_increment,

cid INT,

addr VARCHAR(30),

foreign key(cid) REFERENCES customer2(cid)

)



insert into orders values(null,1,'佛山');

insert into orders values(null,1,'广州');

insert into orders values(null,1,'四川');

insert into orders values(null,2,'佛山');

insert into orders values(null,2,'广州');

insert into orders values(null,2,'四川');

insert into orders values(null,3,'佛山');

insert into orders values(null,3,'广州');

insert into orders values(null,3,'四川');

第一步:

* 创建两个实体:

* 客户实体:

public class Customer {

private Integer cid;

private String cname;

// 一个客户有多个订单 .

private Set<Order> orders = new HashSet<Order>() ;

public Integer getCid() {

return cid;

}

public void setCid(Integer cid) {

this.cid = cid;

}

public String getCname() {

return cname;

}

public void setCname(String cname) {

this.cname = cname;

}

public Set<Order> getOrders() {

return orders;

}

public void setOrders(Set<Order> orders) {

this.orders = orders;

}

@Override

public String toString() {

return "Customer [cid=" + cid + ", cname=" + cname + ", orders =" + orders + "]";

}

}


* 订单实体:

public class Order {

private Integer oid;

private String addr;

// 订单属于某一个客户 . 放置一个客户的对象 .

private Customer customer ;

public Integer getOid() {

return oid;

}

public void setOid(Integer oid) {

this.oid = oid;

}

public String getAddr() {

return addr;

}

public void setAddr(String addr) {

this.addr = addr;

}

public Customer getCustomer() {

return customer;

}

public void setCustomer(Customer customer) {

this.customer = customer;

}

@Override

public String toString() {

return "Order [oid=" + oid + ", addr=" + addr + "]";

}

}




第二步:建立映射:

Customer.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo2.Customer" table="customer2">

<!-- 配置唯一标识 -->

<id name="cid" column="cid">

<generator class="native"/>

</id>

<!-- 配置普通属性 -->

<property name="cname" column="cname" length="20"/>

<!-- 建立映射 -->

<!-- 配置一个集合 <set> name Customer 对象中的关联对象的属性名称 . -->

<set name="orders">

<!-- <key>标签中 column : 用来描述一对多多的一方的 外键的名称 . -->

<key column=" cno "></key>

<!-- 配置一个 <one-to-many> 标签中 class 属性 : 订单的类的全路径 -->

<one-to-many class="cn.itcast.hibernate3.demo2.Order"/>

</set>

</class>

</hibernate-mapping>


Order.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo2.Order" table="orders">

<!-- 配置唯一标识   -->

<id name="oid" column="oid">

<generator class="native"/>

</id>

<!-- 配置普通属性 -->

<property name="addr" column="addr" length="50"/>

<!-- 配置映射 -->

<!-- <many-to-one>标签name :关联对象的属性的名称 . column :表中的 外键名称 .

class : 关联对象类的全路径 -->

<many-to-one name="customer" column=" cno "

class="cn.itcast.hibernate3.demo2.Customer"/>

</class>

</hibernate-mapping>


第三步:将映射放到核心配置文件中.

 <mapping resource="。。。Customer.hbm.xml"/>

<mapping resource="。。。Order.hbm.xml"/>



发现:不能两个实体类的重写toString()方法中都带有对方

测试:

查询:

Customer customer = (Customer) session.get(Customer.class, 1);

System.out.println(customer);

System.out.println(customer.getOrders());

插入用户及其订单:

Customer customer = new Customer();

customer.setCname("阿黄");

Order order = new Order();

order.setAddr("广州");

customer.getOrders().add(order);

order.setCustomer(customer); --没有级联,还需要订单去关联用户

session.save(customer);--没有级联,用户和订单都要保存

session.save(order);



下面就不测试其他了,因为没级联极其的麻烦,下面将会进行级联后的测试



1.1.3 Hibernate中级联保存的效果 :

级联:操作当前对象的时候,关联的对象如何处理.


cascade=”save-update”

级联方向性:

* 保存客户的时候,选择级联订单.

在Customer.hbm.xml的<set name="orders">标签添加上cascade="save-update"

* 保存订单的时候,选择级联客户.

  在Order.hbm.xml的<many-to-one name="Customer">标签上添加上cascade="save-update"

级联保存测试:

保存用户及其订单

Customer custoemr= new Customer();

custoemr.setCname("何泳锋");

Order order = new Order();

order.setAddr("天下");

custoemr.getOrders().add(order); --订单不用再关联用户

session.save(custoemr); --只需要保存用户即可

级联更新测试:

将oid为1的订单的用户改为cid为8的用户:

Customer customer = (Customer) session.get(Customer.class, 8);

Order order = (Order) session.get(Order.class, 1);

//修改订单所属用户

order.setCustomer(customer); --一级缓存会自动发送SQL更新数据库对应表

//customer.getOrders().add(order); --因为修改后一级缓存的数据和一级缓存快照区的不一致

1.1.4 Hibernate中级联删除的效果 :

cascade=”delete”

删除客户时可以删掉对应的订单(配置和上面的一样,变为cascade="save-update,delete")

级联删除测试:

Customer customer = (Customer) session.get(Customer.class, 3);

session.delete(customer); --一般都是建议先查询再删除,删除用户时,会发送删除对应订单SQL

tx.commit();

session.close();

1.1.5 Hibernate中的级联取值 :

none :不使用级联

save-update :保存或更新的时候级联

delete :删除的时候级联

all :除了孤儿删除以外的所有级联.

delete-orphan :孤儿删除(孤子删除).

* 仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方.

* 当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了.

all-delete-orphan :包含了孤儿删除的所有的级联.



1.1.6 双向维护产生多余的SQL:

例如上面的级联删除,如果你信息看一下控制台的SQL的话,会发现会有两次删除订单的SQL,这就是双向维护产生多余的SQL,其实删除一次就够了。



那么我们应该配置 inverse=”true”:在哪一端配置.那么哪一端 放弃了外键的维护权.

* 一般情况下,一的一方去放弃.

  这里的话就是在Customer的<set>标签中添加inverse="true",因为默认为false。



cascade:操作关联对象.

inverse:控制外键的维护.



1.1.7 Hibernate的多对多的配置 :

数据库准备:

create table student(

sid int primary key auto_increment,

sname varchar(30)

)

create table course(

cid int PRIMARY key auto_increment,

cname VARCHAR(30)

)



create table stu_cou(

sno int,

cno int,

foreign KEY(sno) REFERENCES student(sid),

foreign key(cno) references course(cid)

)



insert into student values(null,'hyf');

insert into student values(null,'fy');



insert into course VALUES(null,'语文');

insert into course VALUES(null,'数学');

insert into course VALUES(null,'英语');



insert into stu_cou values(1,1);

insert into stu_cou values(1,2);

insert into stu_cou values(1,3);

insert into stu_cou values(2,1);

insert into stu_cou values(2,3);



第一步:创建实体类:

学生的实体:

public class Student {

private Integer sid;

private String sname;

// 一个学生选择多门课程 :

private Set<Course> courses = new HashSet<Course>() ;

public Integer getSid() {

return sid;

}

public void setSid(Integer sid) {

this.sid = sid;

}

public String getSname() {

return sname;

}

public void setSname(String sname) {

this.sname = sname;

}

public Set<Course> getCourses() {

return courses;

}

public void setCourses(Set<Course> courses) {

this.courses = courses;

}

}

课程的实体:

public class Course {

private Integer cid;

private String cname;

// 一个课程被多个学生选择 :

private Set<Student> students = new HashSet<Student>() ;

public Integer getCid() {

return cid;

}

public void setCid(Integer cid) {

this.cid = cid;

}

public String getCname() {

return cname;

}

public void setCname(String cname) {

this.cname = cname;

}

public Set<Student> getStudents() {

return students;

}

public void setStudents(Set<Student> students) {

this.students = students;

}

}


第二步建立映射:

Student.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo3.Student" table="student">

<!-- 配置唯一标识 -->

<id name="sid" column="sid">

<generator class="native"/>

</id>

<!-- 配置普通属性 -->

<property name="sname" column="sname" length="20"/>

<!-- 配置关联映射 -->

<!-- <set>标签 name: 对应学生中的课程集合的名称    table: 中间表名称 . -->

<set name="courses" table="stu_cour">

<!-- <key>中 column 当前类在中间表的外键 .-->

<key column="sno"></key>

<!-- <many-to-many>中 class: 另一方类的全路径 . column: 另一方在中间表中外键名称 -->

<many-to-many class="cn.itcast.hibernate3.demo3.Course" column="cno"/>

</set>

</class>

</hibernate-mapping>


Course.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo3.Course" table="course">

<!-- 配置唯一标识 -->

<id name="cid" column="cid">

<generator class="native"/>

</id>

<!-- 配置普通属性 -->

<property name="cname" column="cname" length="20"/>

<!-- 配置与学生关联映射 -->

<!-- <set>中 name: 对应当前类中的学生的集合的名称   table: 中间表的名称 -->

<set name="students" table="stu_cour">

<!-- <key>中 column: 当前类在中间表中外键 -->

<key column="cno"></key>

<!-- <many-to-many>中 class: 另一方的类全路径 . column: 另一方在中间表中外键名称 -->

<many-to-many class="cn.itcast.hibernate3.demo3.Student" column="sno"/>

</set>

</class>

</hibernate-mapping>


第三步:将映射文件加入到核心配置文件中 :



查询测试:

List<Student> list= session.createQuery("from Student").list();

for(Student student : list) {

System.out.println(student);

}

1.1.8 Hibernate的多对多的保存 :

两方都会改中间表的记录, 多对多保存一定要有一方放弃外键的维护权

默认会出现错误,应该在 主动方设置放弃外键的维护能力

*在Student.hbm.xml的set标签加上 inverse="true"。


级联保存测试:

新建student和course:

Student student = new Student();

student.setSname("哈哈");

Course course = new Course();

course.setCname("政治");

student.getCourses().add(course);

session.save(student);

级联修改测试:

Student student = (Student) session.get(Student.class, 3);

Course course = (Course) session.get(Course.class, 4);

student.getCourses().remove(course);

1.1.9 Hibernate的多对多的级联操作 :

配置cascade

级联删除在多对多中是很少使用的

*例如删除学生的时候如果级联删除课程这是不太合理的做法