组合关系java
在开发时,有一种状况,有一张数据表的数据来自于多个对象。好比,一个computer(电脑)表,其中有电脑的基本信息、CPU信息、显卡信息、主板信息和内存信息等等,对应的实体对象则是电脑对象、CPU对象、显卡对象和内存对象。这种状况下可使用组合关系映射。sql
以电脑与CPU为例,Computer类中包含了一个Cpu类,在*.hbm.xml文件中,使用component来进行组合关系映射。数据库
看一下这两个实体类的代码和映射文件:session
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方法:app
@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表的外键。spa
在*.hbm中,一的一端使用set标签设置多的一端的集合,在set中加入key与one-to-many。.net
多的一端使用<one-to-may>属性配置一对多。hibernate
看一下实体类代码和配置文件:code
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,具体以下:
使用方式与一对多多对一相同。