Java
Java Web框架

Hibernate(5)

简介:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

1. 多表设置——一对多关系

下面使用Customer和Order来演示一对多关系,一个Customer可以对应多个Order。首先创建两个对象的实现类:

注:在一对多的情况下,往往在“一”的一方用Set转载所有“多”的对象,在“多”的一方引用“一”的一方的对象。

Customer类:

  • package com.coderap.domain;
  • import java.util.HashSet;
  • import java.util.Set;
  • public class Customer {
  • private Integer id;
  • private String name;
  • // 在1 的一方,表达持有多的一方的引用=>使用集合
  • private Set<Order> orders = new HashSet<Order>();
  • public Integer getId() {
  • return id;
  • }
  • public void setId(Integer id) {
  • this.id = id;
  • }
  • public String getName() {
  • return name;
  • }
  • public void setName(String name) {
  • this.name = name;
  • }
  • public Set<Order> getOrders() {
  • return orders;
  • }
  • public void setOrders(Set<Order> orders) {
  • this.orders = orders;
  • }
  • }

Order类:

  • package com.coderap.domain;
  • public class Order {
  • private Integer id;
  • private String name;
  • private Customer customer;
  • public Integer getId() {
  • return id;
  • }
  • public void setId(Integer id) {
  • this.id = id;
  • }
  • public String getName() {
  • return name;
  • }
  • public void setName(String name) {
  • this.name = name;
  • }
  • public Customer getCustomer() {
  • return customer;
  • }
  • public void setCustomer(Customer customer) {
  • this.customer = customer;
  • }
  • }

对于上面的两个对象有配置文件:

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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • name:集合的属性名称
  • -->
  • <set name="orders">
  • <!--
  • key 用来描述外键
  • column : 外键的值
  • -->
  • <key column="cid" ></key>
  • <!--
  • one-to-many:表示Customer与orders的关系是一对多
  • class:表达关联的另一方的完整类名
  • -->
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>

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 package="com.coderap.domain" >
  • <class name="Order" table="t_order" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达多对一关系
  • name:引用的属性名称
  • column:外键的列名
  • class:我引用的Order的完整类名
  • -->
  • <many-to-one name="customer" column="cid" class="Customer" ></many-to-one>
  • </class>
  • </hibernate-mapping>

1.1. 一对多的操作

  1. 保存操作
  • @Test
  • // 1
  • // 解决=>
  • // 注意=> 外键维护的放弃,只能由非外键所在对象来放弃.
  • // Customer inverse属性: true
  • // 只打印3条语句=> 外键由order自己来维护
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = new Customer();
  • c.setName("tom");
  • Order o1 = new Order();
  • o1.setName("商品1");
  • Order o2 = new Order();
  • o2.setName("商品2");
  • c.getOrders().add(o1);//维护关系
  • c.getOrders().add(o2); //维护关系
  • o1.setCustomer(c);// 维护关系
  • o2.setCustomer(c);// 维护关系
  • session.save(c);// 保存对象
  • session.save(o1);// 保存对象
  • session.save(o2);// 保存对象
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

在上面面的测试一对多关系中,保存操作共打印5条语句, 前3条打印insert用于保存对象,维护外键;后两条打印update,维护外键,这表明在insertupdate操作中重复维护了外键,为了解决这个问题,可以单纯指定一对多关系由其中一方来维护,另一方不维护关系,如将Customer配置inversetrue,将不维护外键,将外键交由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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • name:集合的属性名称
  • inverse:是否将关系的维护反转给对方,默认值为false;设置为true即在Customer中放弃维护外键关系
  • -->
  • <set name="orders" inverse="false">
  • <!--
  • key 用来描述外键
  • column : 外键的值
  • -->
  • <key column="cid" ></key>
  • <!--
  • one-to-many:表示Customer与orders的关系是一对多
  • class:表达关联的另一方的完整类名
  • -->
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>

注:外键维护的放弃,只能由非外键所在对象来放弃。

此时将测试代码中Customer用于维护的代码注释之后再执行,将只打印3条语句,此时外键由order自己来维护:

  • @Test
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = new Customer();
  • c.setName("tom");
  • Order o1 = new Order();
  • o1.setName("商品1");
  • Order o2 = new Order();
  • o2.setName("商品2");
  • // c.getOrders().add(o1);//维护关系
  • // c.getOrders().add(o2); //维护关系
  • o1.setCustomer(c);// 维护关系
  • o2.setCustomer(c);// 维护关系
  • session.save(c);// 保存对象
  • session.save(o1);// 保存对象
  • session.save(o2);// 保存对象
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }
  1. 删除操作

当在Customer并没有设置inverse属性为true的情况下,以下代码删除Customer时会自动先移除 Customer中引用的外键,然后再删除Customer:

  • @Test
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 3);
  • session.delete(c);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

但当在Customer设置了inverse属性为true的情况下,上述代码会报错,因为Customer不负责维护外键,直接删除Customer会导致Order引用了无效的id,违反了外键约束;此时需要手动将Order中的Customer移出以清空外键:

  • @Test
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 3);
  • // Customer的inverse属性为true时,需要手动清空Order的Customer属性
  • Set<Order> set = c.getOrders();
  • for (Order o : set) {
  • o.setCustomer(null);// 设置订单不属于任何Customer
  • // 此处可以不调用update,因为Order发生了变化会自动update同步
  • }
  • session.delete(c);
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

注:如果“一”的一方经常需要维护外键,那么在“一”的一方不要配置inverse属性为true。同时,在“多”的一方是不允许配置inverse属性的。

2. Cascade级联操作

2.1. save-update级联保存或更新

在保存数据时,如果我们希望在保存Customer时,自动将未保存的Orders当中的Order保存,就可以将Customer配置文件中<set>标签用于维护一对多外键关系的cascade属性置为save-update

  • <?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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • cascade:级联操作
  • save-update:级联保存,级联修改,保存A时,同时保存B
  • -->
  • <set name="orders" inverse="false" cascade="save-update" >
  • <key column="cid" ></key>
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>
  • @Test
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = new Customer();
  • c.setName("tom");
  • Order o1 = new Order();
  • o1.setName("商品1");
  • Order o2 = new Order();
  • o2.setName("商品2");
  • c.getOrders().add(o1);// 维护关系
  • c.getOrders().add(o2); // 维护关系
  • /*
  • * o1.setCustomer(c);// 维护关系
  • * o2.setCustomer(c);// 维护关系
  • */
  • session.save(c);// 保存对象
  • // session.save(o1);// 保存对象
  • // session.save(o2);// 保存对象
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

如果我们设置了cascadesave-update,在通过查询Customer数据并修改其订单的某些属性的时候,会自动将订单新的数据更新到数据库

  • @Test
  • public void fun2() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 8);// 1条 select
  • for (Order o : c.getOrders()) { // 1条 select
  • o.setName("哇哈哈"); // 修改订单
  • }
  • // ------------------------------------------------
  • // 因为设置级联修改,将执行update语句自动将订单的修改保存到数据库
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

2.2. delete级联删除

同样的,将Customer配置文件中<set>标签用于维护一对多外键关系的cascade属性置为delete时,删除Customer时,会将Customer下的Order一并删除:

  • <?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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • cascade:级联操作
  • delete:删除A,同时删除B,AB都不存在
  • -->
  • <set name="orders" inverse="false" cascade="delete" >
  • <key column="cid" ></key>
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>
  • @Test
  • // inverse : false 6条sql语句
  • // inverse : true 5条sql语句 比上面少一条维护外键
  • public void fun3() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 7);// 1条 select
  • session.delete(c);// 删除Customer,并删除两个Order
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

需要注意的是,千万不要在两方都配置级联删除,删除任何一方,会导致整个关系链对象全部删除:

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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • cascade:级联操作
  • delete:删除A,同时删除B,AB都不存在
  • -->
  • <set name="orders" inverse="false" cascade="delete" >
  • <key column="cid" ></key>
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>

Order.gbm.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 package="com.coderap.domain">
  • <class name="Order" table="t_order">
  • <id name="id" column="id">
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string"></property>
  • <many-to-one name="customer" column="cid" class="Customer" cascade="delete"></many-to-one>
  • </class>
  • </hibernate-mapping>
  • @Test
  • // cascade: delete
  • // 操作的两方cascade值都为delete
  • // 需要注意: 千万不要在两方都配置 级联删除. 删除任何一方,会导致整个关系链对象全部删除.
  • public void fun4() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Order o = (Order) session.get(Order.class, 9);// select
  • session.delete(o);// delete删除当前order
  • // select所有关联的Customer删除
  • // delete删除Customer
  • // Customer配置了级联删除,select找下面的order
  • // 删除所有Order
  • // 删除Customer
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

2.3. delete-orphan孤儿删除

配置了delete-orphan的对象,当没有任何外键引用该对象时,该对象会被删除:

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 package="com.coderap.domain" >
  • <class name="Customer" table="t_customer" >
  • <id name="id" column="id" >
  • <generator class="native"></generator>
  • </id>
  • <property name="name" column="name" type="string" ></property>
  • <!--
  • 表达一对多关系中的集合
  • cascade:级联操作
  • delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的
  • -->
  • <set name="orders" inverse="false" cascade="delete-orphan" >
  • <key column="cid" ></key>
  • <one-to-many class="Order" />
  • </set>
  • </class>
  • </hibernate-mapping>
  • @Test
  • public void fun1() {
  • Session session = HibernateUtils.openSession();
  • session.beginTransaction();
  • // ------------------------------------------------
  • Customer c = (Customer) session.get(Customer.class, 9);
  • Iterator<Order> it = c.getOrders().iterator();
  • // 注意: 删除Customer下的订单时,不能使用c.setOrders(null);及c.setOrders<Order>(new HashSet());
  • // 遍历Customer下的订单,并将订单删除,维护关系
  • while (it.hasNext()) {
  • it.next();
  • it.remove();
  • }
  • // ------------------------------------------------
  • session.getTransaction().commit();
  • session.close(); // 游离状态
  • }

注:此时要将Customer的inverse置为false,否则不维护外键,该属性将不会生效。

cascade还有两个属性值,分别解释如下:

  • allsave-updatedelete整合
  • all-delete-orphan:三个整合