数据库建表原则,在多的一方添加一个外键列,引用一的一方的主键,例如客户和订单,在订单表中增加客户编号作为外键
一对多,类对象之间的关系,在多的一方添加一个集合
class A {
B b; // 一个A对应一个B
}
class B {
A[] 、List<A>、Set<A> // 一个B 对应很多A
}
以客户与订单关系为例建立映射关系
建立客户类
public class Customer implements Serializable{
private Integer id;
private String name;
private String city;
pprivate Set<Order> orders = new HashSet<Order>();//一个客户对应多个订单
}
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Customer" table="customer" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <set name="orders"> <!-- customer表在order表中所生成的外键列 --> <key column="customer_id"></key> <one-to-many class="rock.lee.bean.Order" /> </set> <property name="name" column="name" type="java.lang.String"></property> <property name="city" column="city" type="java.lang.String"></property> </class> </hibernate-mapping>
建立订单类
public class Order implements Serializable{
private Integer id;
private String address;
private Double money;
private Customer customer;//一个订单对应一个客户
}
Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Order" table="orders" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="address" column="address" type="java.lang.String"></property> <property name="money" column="money" type="double"></property> <!-- 在orders表中添加customer外键列,column为外键列名 --> <many-to-one name="customer" class="rock.lee.bean.Customer" column="customer_id"></many-to-one> </class> </hibernate-mapping>在hibernate.cfg.xml中配置加载Custoemr和Order映射文件
<mapping resource="rock/lee/bean/Customer.hbm.xml" /> <mapping resource="rock/lee/bean/Order.hbm.xml" />
案例一:保存Customer、Order
@Test
public void testOneToMany01() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer c= new Customer("小明", "BJ");
Order order1 = new Order("二环", 11D);
Order order2 = new Order("三环", 22D);
order1.setCustomer(c);//订单关联客户
order2.setCustomer(c);
c.getOrders().add(order1);//客户关联订单
c.getOrders().add(order2);
session.save(c);
session.save(order1);
session.save(order2);
transaction.commit();
session.close();
}
根据控制台打印的SQL,在insert Customer和Order后会update orders表中的外键列
Hibernate:
update
test.orders
set
customer_id=?
where
id=?
程序分别保存了customer和order,如果只保存customer不保存order会抛出异常
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: rock.lee.bean.Order
在Session关闭后,Hibernate不允许一个peristient状态对象关联一个tranient状态对象
案例二:级联保存
保存customer的同时自动保存order,修改customer类的映射文件增加casecade属性
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Customer" table="customer" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <set name="orders" cascade="save-update"> <!-- customer表order是中所生成的外键列 --> <key column="customer_id"></key> <one-to-many class="rock.lee.bean.Order" /> </set> <property name="name" column="name" type="java.lang.String"></property> <property name="city" column="city" type="java.lang.String"></property> </class> </hibernate-mapping>只保存customer,不保存order
@Test
public void testOneToMany02() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer c= new Customer("小明", "BJ");
Order order1 = new Order("二环", 11D);
Order order2 = new Order("三环", 22D);
order1.setCustomer(c);//订单关联客户
order2.setCustomer(c);
c.getOrders().add(order1);//客户关联订单
c.getOrders().add(order2);
session.save(c);
transaction.commit();
session.close();
}
配置了casecade后当一个persistent对象关联了一个traniesent对象是会自动保存关联的那个transient对象
案例三:对象导航
案例四:级联删除
@Test
public void testOneToMany03() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 1);
session.delete(c);
transaction.commit();
session.close();
}
删除customer时会先把order表中的外键列customer_id设置为null,在删除customer
Hibernate:
update
test.orders
set
customer_id=null
where
customer_id=1
Hibernate:
delete
from
test.customer
where
id=1
删除orders表中的数据时直接删除,因为没有外键依赖关系
修改Customer.hbm.xml配置文件,增加级联删除配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Customer" table="customer" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <set name="orders" cascade="save-update,delete"> <!-- customer表order是中所生成的外键列 --> <key column="customer_id"></key> <one-to-many class="rock.lee.bean.Order" /> </set> <property name="name" column="name" type="java.lang.String"></property> <property name="city" column="city" type="java.lang.String"></property> </class> </hibernate-mapping>此时删除customer对象时,也将删除order对象,级联删除,仍然是先接触外键关系,在删除数据,如果删除一个detached状态的customer对象无法级联删除的
修改Order.hbm.xml文件,将外键设置为非null
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Order" table="orders" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="address" column="address" type="java.lang.String"></property> <property name="money" column="money" type="double"></property> <!-- 在orders表中添加customer外键列,column为外键列名 --> <many-to-one name="customer" class="rock.lee.bean.Customer" column="customer_id" not-null="true"></many-to-one> </class> </hibernate-mapping>此时,当删除Customer对象是无法级联删除Order,因为删除前需要先接触外键,设置orders表中customer_id为null,但由于配置了not-null="true"属性会抛出异常
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'customer_id' cannot be null
这种情况只能先删除orders表中的订单数据,在删除customer表中的数据,业务上不会有此种情况,删个订单还把客户干掉了
案例五:孤儿删除
在一对多关系中,表的建立存在父子表,customer为父表,orders为子表,当一个customer和一个order解除关系后,作为order信息实际上就不完整了,不会有一个订单不关联任何用户的情况
修改Customer.hbm.xml,在casecade属性中增加delete-orphan
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Customer" table="customer" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <set name="orders" cascade="save-update,delete,delete-orphan" > <!-- customer表order是中所生成的外键列 --> <key column="customer_id"></key> <one-to-many class="rock.lee.bean.Order" /> </set> <property name="name" column="name" type="java.lang.String"></property> <property name="city" column="city" type="java.lang.String"></property> </class> </hibernate-mapping>不调用deleet(),只接触cusomter与order之间的关系
@Test
public void testOneToMany04() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Customer c1 = (Customer) session.get(Customer.class, 1);
Order o1 = (Order) session.get(Order.class, 1);
c1.getOrders().remove(o1);//解除客户与订单关系
transaction.commit();
session.close();
}
Hibernate会先接触外键关系,在删除
Hibernate:
update
test.orders
set
customer_id=null
where
customer_id=?
and id=?
Hibernate:
delete
from
test.orders
where
id=?
casecade取值:
casecade取值来源JPA规范,但Hibernate对JPA进行了扩展
有红点的是Hibernate扩展的,其它是JPA
inverse单相关系维护:
数据库数据
mysql> select * from customer;
+----+--------+------+
| id | name | city |
+----+--------+------+
| 1 | 孙艺珍 | BJ |
| 2 | 林允儿 | SH |
+----+--------+------+
2 rows in set (0.00 sec)
mysql> select * from orders;
+----+---------+-------+-------------+
| id | address | money | customer_id |
+----+---------+-------+-------------+
| 1 | 二环 | 11 | 1 |
+----+---------+-------+-------------+
1 row in set (0.00 sec)
将order表中1号订单与林允儿关联
@Test
public void testOneToMany06() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
Order o = (Order) session.get(Order.class, 1);
Customer lyr = (Customer) session.get(Customer.class, 2);
o.setCustomer(lyr);
lyr.getOrders().add(o);
transaction.commit();
session.close();
}
控制台会有两条update语句,更新orders表中的外键列
Hibernate:
update
test.orders
set
address=?,
money=?,
customer_id=?
where
id=?
Hibernate:
update
test.orders
set
customer_id=?
where
id=?
原因是Session中的缓存与快照比对后发现不一样所有cusotmer和order都去更新orders表中的外键列
修改Customer.hbm.xml增加inverse="true"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="rock.lee.bean.Customer" table="customer" catalog="test">
<id name="id" column="id" type="int">
<generator class="native"></generator>
</id>
<set name="orders" cascade="save-update,delete,delete-orphan" inverse="true" >
<!-- customer表order是中所生成的外键列 -->
<key column="customer_id"></key>
<one-to-many class="rock.lee.bean.Order" />
</set>
<property name="name" column="name" type="java.lang.String"></property>
<property name="city" column="city" type="java.lang.String"></property>
</class>
</hibernate-mapping>
同样的操作,只有一条update,有order发出,在Cusomter.hbm.xml配置invers="true" 表示关联关系的控制权被反转了,交由order控制
Customer customer = new Customer();// 定义一个客户
customer.setName("张三");
Order order = new Order();// 定义一个订单
order.setMoney(2000d);
order.setAddr("二环");
// 建立对象之间关系
customer.getOrders().add(order); // 客户对象关联订单
session.save(customer);
custoemr和order都会被保存,但orders表中cusotmer_id列为null,因为维护外键的权利由order控制,但保存了cusomter时级联保存order,却没有维护外键的权利,所以orders表中的外键列为null