组合关系
在开发时,有一种情况,有一张数据表的数据来自于多个对象。比如,一个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;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.net.bysoft.model">
<class name="Computer" table="computer">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<!-- 使用组合关系映射,CPU的Name信息存放在Cpu_Name字段中。 -->
<component name="cpu" class="Cpu">
<property name="name" type="string" column="CPU_NAME"></property>
</component>
</class>
</hibernate-mapping>
生成的数据表并测试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 Set<Order> orders = new HashSet<Order>();
}
package cn.net.bysoft.model1;
public class Order {
getter/setter属性
private int id;
private String name;
// 每个订单都属于一个用户。
private Customer customer;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.net.bysoft.model1.Customer" table="CUSTOMERS">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<!-- 一对多 -->
<set name="orders" table="ORDERS">
<key column="CUSTOMER_ID"></key>
<one-to-many class="cn.net.bysoft.model1.Order"/>
</set>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.net.bysoft.model1.Order" table="ORDERS">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<!-- 多对一 -->
<many-to-one name="customer" class="cn.net.bysoft.model1.Customer" column="CUSTOMER_ID">
</many-to-one>
</class>
</hibernate-mapping>
新增和删除的时候有一些需要注意的地方,看一段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节点中加入:
<set name="orders" table="ORDERS" inverse="true">
在进行保存时,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:值保存一的一端,多的一端会自动保存;
在设置好级联关系后,可直接删除一的一端:
<set name="orders" table="ORDERS" inverse="true" cascade="delete">
在项目中建议使用手动控制关联关系。
一对一
有两种情况均为一对一关联关系,一个部门有一个部门经理。可以使用某一个字段做外键,也可以使用主键做外键。先来看看主外键情况做一对一。
在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;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.net.bysoft.model1.Dept" table="DEPT">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<!-- 多对一 -->
<many-to-one name="manager" class="cn.net.bysoft.model1.Manager"
column="MANAGER_ID" unique="true"></many-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.net.bysoft.model1.Manager" table="MANAGER">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<!-- 一对一 -->
<one-to-one name="dept" class="cn.net.bysoft.model1.Dept" property-ref="manager"></one-to-one>
</class>
</hibernate-mapping>
接下来是主键与主键做一对一关系,实体类无需改变,只需要修改dept的配置文件,将id节点的class修改成foreign模式,将many-to-one修改成one-to-one,添加constrained属性等于true,具体如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.net.bysoft.model1.Dept" table="DEPT">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="foreign">
<param name="property">manager</param>
</generator>
</id>
<property name="name" type="string" column="NAME"></property>
<one-to-one name="manager" class="cn.net.bysoft.model1.Manager" constrained="true">
</one-to-one>
</class>
</hibernate-mapping>
增删改查方法的调用与一对多多对一一样。
多对多
用数据表描述多对多,需要有三张表,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 Set<Products> products = new HashSet<Products>();
}
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 Set<Orders> orders = new HashSet<Orders>();
}
两个对象的配置文件也很像,每个配置文件中都有set节点,但是在多对多中,必须有一个set的inverse=true,具体如下:
使用方式与一对多多对一相同。