数据库建表原则,在多的一方添加一个外键列,引用一的一方的主键,例如客户和订单,在订单表中增加客户编号作为外键
一对多,类对象之间的关系,在多的一方添加一个集合
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