数据库表中存在的关系在hibernate为各实体之间的关系。
实体之间有三种关系:
一对多:一个用户,生成多个订单,每一个订单只能属于一个用户
建表原则:在多的一方创建一个字段,作为外键,指向一的一方的主键.
多对多:一个学生可以选择多门课程,一个课程可以被多个学生选择.
建表原则:创建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.
一对一(特殊.最少):一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用.(否则将两个表建到一个表.)
建表原则:
唯一外键:一对一的双方,假设一方是多的关系.需要在多的一方创建一个字段,作为外键.指向一的一方的主键.但是在外键添加一个unique.
主键对应: 一对一的双方,通过主键进行关联.
Hibernate中一对多的配置:
第一步:创建两个实体:
客户实体:
1 public class Customer { 2 private Integer cid; 3 private String cname; 4 // 一个客户有多个订单. 5 private Set<Order> orders = new HashSet<Order>(); 6 public Integer getCid() { 7 return cid; 8 } 9 public void setCid(Integer cid) { 10 this.cid = cid; 11 } 12 public String getCname() { 13 return cname; 14 } 15 public void setCname(String cname) { 16 this.cname = cname; 17 } 18 public Set<Order> getOrders() { 19 return orders; 20 } 21 public void setOrders(Set<Order> orders) { 22 this.orders = orders; 23 } 24 25 }
订单实体:
1 public class Order { 2 private Integer oid; 3 private String addr; 4 // 订单属于某一个客户.放置一个客户的对象. 5 private Customer customer; 6 public Integer getOid() { 7 return oid; 8 } 9 public void setOid(Integer oid) { 10 this.oid = oid; 11 } 12 public String getAddr() { 13 return addr; 14 } 15 public void setAddr(String addr) { 16 this.addr = addr; 17 } 18 public Customer getCustomer() { 19 return customer; 20 } 21 public void setCustomer(Customer customer) { 22 this.customer = customer; 23 } 24 25 }
第二步:建立映射:
Customer.hbm.xml
1 <hibernate-mapping> 2 <class name="cn.itcast.hibernate3.demo2.Customer" table="customer"> 3 <!-- 配置唯一标识 --> 4 <id name="cid" column="cid"> 5 <generator class="native"/> 6 </id> 7 <!-- 配置普通属性 --> 8 <property name="cname" column="cname" length="20"/> 9 10 <!-- 建立映射 --> 11 <!-- 配置一个集合 <set>的name Customer对象中的关联对象的属性名称. --> 12 <set name="orders"> 13 <!-- <key>标签中column:用来描述一对多多的一方的外键的名称. --> 14 <key column="cno"></key> 15 <!-- 配置一个<one-to-many>标签中class属性:订单的类的全路径 --> 16 <one-to-many class="cn.itcast.hibernate3.demo2.Order"/> 17 </set> 18 </class> 19 </hibernate-mapping>
Order.hbm.xml
1 <hibernate-mapping> 2 <class name="cn.itcast.hibernate3.demo2.Order" table="orders"> 3 <!-- 配置唯一标识 --> 4 <id name="oid" column="oid"> 5 <generator class="native"/> 6 </id> 7 <!-- 配置普通属性 --> 8 <property name="addr" column="addr" length="50"/> 9 <!-- 配置映射 --> 10 <!-- 11 <many-to-one>标签 12 name :关联对象的属性的名称. 13 column :表中的外键名称. 14 class :关联对象类的全路径 15 --> 16 <many-to-one name="customer" column="cno" class="cn.itcast.hibernate3.demo2.Customer"/> 17 </class> 18 </hibernate-mapping>
第三步:将映射放到核心配置文件中,完成。
测试:
1 @Test 2 // 向客户表插入一个客户,在订单表中插入两个订单. 3 public void demo1(){ 4 Session session = HibernateUtils.openSession(); 5 Transaction tx = session.beginTransaction(); 6 7 // 定义一个客户: 8 Customer customer = new Customer(); 9 customer.setCname("老汤姆"); 10 11 // 定义两个订单: 12 Order order1 = new Order(); 13 order1.setAddr("吉尼斯"); 14 15 Order order2 = new Order(); 16 order2.setAddr("西西里"); 17 18 // 建立关系: 19 order1.setCustomer(customer); 20 order2.setCustomer(customer); 21 22 customer.getOrders().add(order1); 23 customer.getOrders().add(order2); 24 25 session.save(customer); 26 session.save(order1); 27 session.save(order2); 28 29 tx.commit(); 30 session.close();
~成功
但是,这样保存数据太麻烦,在hibernate中存在级联保存:操作当前对象的时候,关联的对象也会被保存
只需在Customer.hbm.xml中set中加入<set name="orders" cascade="save-update">
1 @Test 2 // 保存客户级联订单. 3 // <set>集合是客户的关联订单对象的集合.所以在<set>上配置一个属性:cascade="save-update" 4 public void demo3(){ 5 Session session = HibernateUtils.openSession(); 6 Transaction tx = session.beginTransaction(); 7 8 // 定义客户: 9 Customer customer = new Customer(); 10 customer.setCname("老汤姆"); 11 12 // 定义订单: 13 Order order = new Order(); 14 order.setAddr("吉尼斯"); 15 order.setCustomer(customer); 16 17 customer.getOrders().add(order); 18 19 // 保存的时候只保存一方: 20 session.save(customer); 21 22 tx.commit(); 23 session.close(); 24 }
当然,hibernate中不止一种级联
none :不使用级联 dave-update :保存或更新的时候级联 delete :删除的时候级联 all :除了孤儿删除以外的所有级联. delete-orphan :孤儿删除(孤子删除). * 仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方. * 当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了. all-delete-orphan :包含了孤儿删除的所有的级联.
另外,hibernate中多表之间由于外键存在双向维护,导致产生多余的sql语句,同样在Customer.hbm.xml中<set name="orders" cascade="save-update" inverse="true">
当inverse=true时,就是告诉Customer放弃对外键的维护权。
Hibernate中多对多的配置和一对多差不多,只是在各自的实体类中都存在对方的实体类集合,另外xml中如下:
Course.hbm.xml
1 <hibernate-mapping> 2 <class name="cn.itcast.hibernate3.demo3.Course" table="course"> 3 <!-- 配置唯一标识 --> 4 <id name="cid" column="cid"> 5 <generator class="native"/> 6 </id> 7 <!-- 配置普通属性 --> 8 <property name="cname" column="cname" length="20"/> 9 <!-- 配置与学生关联映射 --> 10 <!-- <set>中name:对应当前类中的学生的集合的名称 table:中间表的名称--> 11 <set name="students" table="stu_cour" inverse="true"> 12 <!-- <key>中column:当前类在中间表中外键 --> 13 <key column="cno"></key> 14 <!-- <many-to-many>中class:另一方的类全路径. column:另一方在中间表中外键名称 --> 15 <many-to-many class="cn.itcast.hibernate3.demo3.Student" column="sno"/> 16 </set> 17 </class> 18 </hibernate-mapping>
Student.hbm.xml
1 <hibernate-mapping> 2 <class name="cn.itcast.hibernate3.demo3.Student" table="student"> 3 <!-- 配置唯一标识 --> 4 <id name="sid" column="sid"> 5 <generator class="native"/> 6 </id> 7 <!-- 配置普通属性 --> 8 <property name="sname" column="sname" length="20"/> 9 10 <!-- 配置关联映射 --> 11 <!-- <set>标签 name:对应学生中的课程集合的名称 table:中间表名称. --> 12 <set name="courses" table="stu_cour" cascade="save-update,delete"> 13 <!-- <key>中column写 当前类在中间表的外键.--> 14 <key column="sno"></key> 15 <!-- <many-to-many>中class:另一方类的全路径. column:另一方在中间表中外键名称--> 16 <many-to-many class="cn.itcast.hibernate3.demo3.Course" column="cno"/> 17 </set> 18 </class> 19 </hibernate-mapping>
另外,对应多对多中数据的删除,建议采用remove来删除数据
1 @Test 2 // 多对多的学生退选. 3 public void demo4(){ 4 Session session = HibernateUtils.openSession(); 5 Transaction tx = session.beginTransaction(); 6 7 // 查询一号学生 8 Student student = (Student) session.get(Student.class, 1); 9 Course course = (Course) session.get(Course.class, 2); 10 student.getCourses().remove(course); 11 12 tx.commit(); 13 session.close(); 14 }