组合关系
在开发时,有一种情况,有一张数据表的数据来自于多个对象。比如,一个computer(电脑)表,其中有电脑的基本信息、CPU信息、显卡信息、主板信息和内存信息等等,对应的实体对象则是电脑对象、CPU对象、显卡对象和内存对象。这种情况下可以使用组合关系映射。
以电脑与CPU为例,Computer类中包含了一个Cpu类,在*.hbm.xml文件中,使用component来进行组合关系映射。
看一下这两个实体类的代码和映射文件:
package cn.net.bysoft.model;public class Computer {//getter and setter private int id; private String name; private Cpu cpu;}
package cn.net.bysoft.model;public class Cpu { //getter and setter private String name;}
生成的数据表并测试save方法:
@Test public void testComponent() throws IOException { // save一个组合关系对象。 Computer computer = new Computer(); computer.setName("pc"); Cpu cpu = new Cpu(); cpu.setName("Inter"); computer.setCpu(cpu); session.save(computer); }
一对多与多对一
一对多与多对一关联,一般用于一个对象包含另一个对象的集合,被包含的对象中存在包含对象的实体,比如一个customer可以有多比订单(order)。在数据表中,order表有customer表的外键。
在*.hbm中,一的一端使用set标签设置多的一端的集合,在set中加入key与one-to-many。
多的一端使用<one-to-may>属性配置一对多。
看一下实体类代码和配置文件:
package cn.net.bysoft.model1;import java.util.HashSet;import java.util.Set;public class Customer { getter/setter属性 private int Id; private String name; // 一对用户可以有多个订单。 private Setorders = new HashSet ();}
package cn.net.bysoft.model1;public class Order { getter/setter属性 private int id; private String name; // 每个订单都属于一个用户。 private Customer customer;}
新增和删除的时候有一些需要注意的地方,看一段save代码:
@Test public void testOneToManySave() { Customer customer = new Customer(); customer.setName("Kobe"); Order order1 = new Order(); order1.setName("buy book"); Order order2 = new Order(); order2.setName("buy ball"); customer.getOrders().add(order1); customer.getOrders().add(order2); order1.setCustomer(customer); order2.setCustomer(customer); session.save(customer); session.save(order1); session.save(order2); /** * output: Hibernate: * insert into CUSTOMERS (NAME) values (?) * insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?) * insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?) * */ }
先save一的一端,在save多的一端,会打印三条sql语句,是正常的。如果先保存多的一端在保存一的一端,会输出七条sql语句,其中有4条是update语句,因为先保存多的一端,此时并没有一的一端的主键,等到保存好一的一端后,在回头由两端都进行update。
session.save(order1); session.save(order2); session.save(customer); /** * output: * Hibernate: insert into ORDERS (NAME, CUSTOMER_ID) values (?,?) * Hibernate: insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?) * Hibernate: insert into CUSTOMERS (NAME) values (?) * Hibernate: update ORDERS set NAME=?, CUSTOMER_ID=? where ID=? * Hibernate: update ORDERS set NAME=?, CUSTOMER_ID=? where ID=? * Hibernate: update ORDERS set CUSTOMER_ID=? where ID=? * Hibernate: update ORDERS set CUSTOMER_ID=? where ID=? * */
正常来说,不需要两端都进行update维护,要解决该问题需要在一端加入inverse属性,建议由多的一端去控制,所以在Customer.hbm.xml中的set节点中加入:
在进行保存时,update语句减少了,只由多的一端维护关系:
再来说说delete。删除时在不设置级联的情况下,不能删除一的一端,因为这一端的主键被关键关联着:
@Test public void testOneToManyDelete() { Customer customer = (Customer) session.get(Customer.class, 1); session.delete(customer); /** * output: * INFO: HHH000010: On release of batch it still contained JDBC statements * */ }
使用级联关系的属性是cascade,该属性有三个值,分别是:
delete:删除一的一端,会连带着删除多的一端;
delete-orphan:一的一端使用多的一端的集合的clear属性可以删除多的一端;
save-update:值保存一的一端,多的一端会自动保存;
在设置好级联关系后,可直接删除一的一端:
在项目中建议使用手动控制关联关系。
一对一
有两种情况均为一对一关联关系,一个部门有一个部门经理。可以使用某一个字段做外键,也可以使用主键做外键。先来看看主外键情况做一对一。
在Manager对象的配置文件中加入<one-to-one>节点,而Dept对象中加入<many-to-one>节点,对该节点加入unique属性进行唯一约束,下面是实体类与配置文件内容:
package cn.net.bysoft.model1;public class Dept { //getter/setter private int id; private String name; private Manager manager;}
package cn.net.bysoft.model1;public class Manager { //getter/setter private int id; private String name; private Dept dept;}
接下来是主键与主键做一对一关系,实体类无需改变,只需要修改dept的配置文件,将id节点的class修改成foreign模式,将many-to-one修改成one-to-one,添加constrained属性等于true,具体如下:
manager
增删改查方法的调用与一对多多对一一样。
多对多
用数据表描述多对多,需要有三张表,A表,B表,A-R-B表。
用对象描述多对多,A对象中有B对象的集合,B对象中也有A对象的集合。
举个例子,现在有订单类与商品类,一个订单中可以有多个商品,而一个商品也可以属于多个订单。看一下实体类的代码:
package cn.net.bysoft.model1;import java.util.HashSet;import java.util.Set;public class Orders { //getter and setter private int id; private String name; private Setproducts = new HashSet ();}
package cn.net.bysoft.model1;import java.util.HashSet;import java.util.Set;public class Products { //getter and setter private int id; private String name; private Setorders = new HashSet ();}
两个对象的配置文件也很像,每个配置文件中都有set节点,但是在多对多中,必须有一个set的inverse=true,具体如下:
使用方式与一对多多对一相同。