Hibernate关联映射分类
- 单向关系:只需单向访问关联端。例如:只能通过老师访问学生或只能通过学生访问老师。
- 双向关系:关联的两端可以访问。如老师和学生可以互相访问。
单向关联分为:
- 单向N-1
- 单向1-N
- 单向1-1
- 单向N-N
双向关联分为:
- 双向1-1
- 双向1-N
- 双向N-N
1 单向多对一:N-1
案例:一个客户有多个订单,从订单一方访问客户信息
1.1 Order对象和映射文件
//对象文件 public class Order { private int id; private String orderNumber; private Customer customer;
//以下所有的对象都省略了get/set方法 }
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="orderNumber" /> <!-- name属性名;column外键列名;class该属性关联的全类名--> <many-to-one name="customer" column="cust_id" class="com.silvan.pojo.Customer" not-null="true"/> </class> </hibernate-mapping>
1.2 Customer对象和映射文件
public class Customer { private int id; private String custName; }
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> </class> </hibernate-mapping>
1.3 hibernate.cfg.xml加上映射文件:
<mapping resource="com/silvan/pojo/Order.hbm.xml" />
<mapping resource="com/silvan/pojo/Customer.hbm.xml" />
1.4 测试方法
//保存订单和客户信息,先保存客户信息 public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order = new Order(); order.setOrderNumber("1"); order.setCustomer(customer); session.save(customer); session.save(order); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } //打印的保存过程: //Hibernate: select hibernate_sequence.nextval from dual //Hibernate: select hibernate_sequence.nextval from dual //Hibernate: insert into t_customer (custName, id) values (?, ?) //Hibernate: insert into t_order (orderNumber, cust_id, id) values (?, ?, ?) //查询订单信息,通过订单信息查询客户信息,这时候会发出一条查询客户信息的SQL语句 public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Order order = (Order) session.get(Order.class, 4); System.out.println(order.getCustomer().getCustName()); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
1.5 级联(cascade)
当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,而是会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。 级联指的是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作。
所以如果上面案例中设置成
<many-to-one name="customer" cascade="save-update" column="cust_id" class="com.silvan.pojo.Customer" not-null="true"/>则保存的时候不需要session.save(customer);也可以保存客户信息;而如果没有设置cascade,不保存客户信息的情况下去保存订单信息的话会抛出异常。
2 单向一对多:1 - N
案例:一个客户有多个订单,从客户一方访问订单信息
2.1 Order对象和映射文件
//对象文件 public class Order { private int id; private String orderNumber; }
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="orderNumber" /> </class> </hibernate-mapping>
2.2 Customer对象和映射文件
public class Customer { private int id; private String custName; private Set orders=new HashSet(); }
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> <!-- name属性名;column外键列名;class该属性关联的全类名; cascade 级联方式 --> <set name="orders" cascade="save-update"> <key column="cust_id"></key> <one-to-many class="com.silvan.pojo.Order" /> </set> </class> </hibernate-mapping>
2.3 测试方法
//保存订单和客户信息, //先保存客户和订单的基本信息,然后更新订单中的外键客户编号信息 public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); Order order2 = new Order(); order2.setOrderNumber("2order"); Set<Order> orders = new HashSet(); orders.add(order1); orders.add(order2); customer.setOrders(orders); session.save(customer); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } //打印的保存过程: //Hibernate: select hibernate_sequence.nextval from dual //Hibernate: select hibernate_sequence.nextval from dual //Hibernate: select hibernate_sequence.nextval from dual //Hibernate: insert into t_customer (custName, id) values (?, ?) //Hibernate: insert into t_order (orderNumber, id) values (?, ?) //Hibernate: insert into t_order (orderNumber, id) values (?, ?) //Hibernate: update t_order set cust_id=? where id=? //Hibernate: update t_order set cust_id=? where id=? public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = (Customer) session.get(Customer.class,1); Set orders = customer.getOrders(); for(Object obj:orders){ Order order = (Order) obj; System.out.println(order.getOrderNumber()); } }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
3 双向N-1
双向 1-N 与 双向 N-1 是完全相同的两种情形
双向 1-N 需要在 1 的一端可以访问 N 的一端, 反之依然
案例:一个客户有多个订单,从客户一方访问订单信息,从订单一方访问客户信息
3.1 Order
注意:测试中保存先保存哪一方,cascade就应该设置在哪一方的映射文件中
//对象文件 public class Order { private int id; private String orderNumber; private Customer customer;}
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="orderNumber" /> <many-to-one name="customer" column="cust_id" class="com.silvan.pojo.Customer" not-null="true"/> </class> </hibernate-mapping>
3.2Customer
public class Customer { private int id; private String custName; private Set orders=new HashSet(); }
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> <!-- name属性名;column外键列名;class该属性关联的全类名 --> <set name="orders" cascade="save-update" > <key column="cust_id"></key> <one-to-many class="com.silvan.pojo.Order" /> </set> </class> </hibernate-mapping>
3.3 测试方法
//保存订单和客户信息, public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); order1.setCustomer(customer); Order order2 = new Order(); order2.setOrderNumber("2order"); order2.setCustomer(customer); Set<Order> orders = new HashSet(); orders.add(order1); orders.add(order2); customer.setOrders(orders); session.save(customer); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); //从1的一端获取多的一端信息 Customer customer = (Customer) session.get(Customer.class,1); Set orders = customer.getOrders(); for(Object obj:orders){ Order order = (Order) obj; System.out.println(order.getOrderNumber()); } //从多的一端获取1的一端信息 Order order = (Order) session.get(Order.class, 2); System.out.println(order.getCustomer().getCustName()); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
3.4 inverse属性(inverse:相反的,逆向的)
原理:
l 在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
l 在没有设置 inverse=true 的情况下,父子两边都维护父子关系
l 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
l 在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句。
案例结果:1-N双向映射情况下插入数据语句
//用户映射文件中设置inverse="false":
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into t_customer (custName, id) values (?, ?)
Hibernate: insert into t_order (orderNumber, cust_id, id) values (?, ?, ?)
Hibernate: insert into t_order (orderNumber, cust_id, id) values (?, ?, ?)
Hibernate: update t_order set cust_id=? where id=?
Hibernate: update t_order set cust_id=? where id=?
//用户映射文件中设置inverse="true":
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into t_customer (custName, id) values (?, ?)
Hibernate: insert into t_order (orderNumber, cust_id, id) values (?, ?, ?)
Hibernate: insert into t_order (orderNumber, cust_id, id) values (?, ?, ?)
4 单向1-1关联
4.1 外键关联:
l 单向 1-1,POJO 与 N-1 没有丝毫区别。
l 基于外键的单向 1-1 映射文件:只需要在原有的 many-to-one 元素添加 unique=“true”,用以表示 N 的一端必须唯一即可,N的一端增加了唯一约束, 即成为单向 1-1.
案例:假设一个订单只有一个客户,一个客户只有一个订单。
4.1.1 Order
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="orderNumber" /> <many-to-one name="customer" unique="true" cascade="save-update" column="cust_id" class="com.silvan.pojo.Customer" not-null="true"/> </class> </hibernate-mapping>
4.1.2 Customer
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> </class> </hibernate-mapping>
4.1.3 测试方法
//保存订单和客户信息, public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); order1.setCustomer(customer); session.save(order1); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
4.2 主键关联:
一对一的另一种解决方式就是主键关联,在这种关联关系中,要求两个对象的主键必须保持一致,通过两个表的主键建立关联关系须外键参与。
4.2.1 order
//对象文件 public class Order { private int id; private String orderNumber; private Customer customer;}
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="foreign"> <param name="property">customer</param> </generator> </id> <property name="orderNumber" /> <one-to-one name="customer" cascade="save-update" class="com.silvan.pojo.Customer" constrained="true"/> </class> </hibernate-mapping>
4.2.2Customer
public class Customer { private int id; private String custName; }
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> </class> </hibernate-mapping>
4.2.3 测试方法
//保存订单和客户信息, public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); order1.setCustomer(customer); session.save(order1); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } //根据订单信息查询出客户名称 public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Order order = (Order) session.get(Order.class,1); System.out.println(order.getCustomer().getCustName()); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
5 双向1-1关联
5.1 外键关联
5.1.1 Order
//对象文件 public class Order { private int id; private String orderNumber; private Customer customer;}
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="native"/> </id> <property name="orderNumber" /> <many-to-one name="customer" unique="true" cascade="save-update" column="cust_id" class="com.silvan.pojo.Customer" not-null="true"/> </class> </hibernate-mapping>
5.1.2 Customer
public class Customer { private int id; private String custName; private Order order; }
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> <!-- 外键方式:一对一关联。该元素使用 property-ref属性指定使用被关联实体主键以外的字段作为关联字段--> <one-to-one name="order" class="com.silvan.pojo.Order" property-ref="customer"></one-to-one> </class> </hibernate-mapping>
5.1.3 测试方法
//保存订单和客户信息, public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); order1.setCustomer(customer); session.save(order1); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } @Test public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Order order = (Order) session.get(Order.class,1); System.out.println(order.getCustomer().getCustName()); Customer customer = (Customer) session.get(Customer.class,2); System.out.println(customer.getOrder().getOrderNumber()); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }
5.2 主键关联
5.2.1 order
//对象文件 public class Order { private int id; private String orderNumber; private Customer customer;}
<hibernate-mapping> <class name="com.silvan.pojo.Order" table="t_order"> <id name="id" type="java.lang.Integer"> <generator class="foreign"> <param name="property">customer</param> </generator> </id> <property name="orderNumber" /> <one-to-one name="customer" cascade="save-update" class="com.silvan.pojo.Customer" constrained="true"/> </class> </hibernate-mapping>
5.2.2 Customer
public class Customer { private int id; private String custName; private Order order;}
<hibernate-mapping> <class name="com.silvan.pojo.Customer" table="t_customer"> <id name="id"> <generator class="native"></generator> </id> <property name="custName" /> <one-to-one name="order" class="com.silvan.pojo.Order" ></one-to-one> </class> </hibernate-mapping>
5.2.3 测试方法
//保存订单和客户信息, public void save(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCustName("qinqin"); Order order1 = new Order(); order1.setOrderNumber("1order"); order1.setCustomer(customer); session.save(order1); tx.commit(); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } } @Test public void query(){ Session session = HibernateUtil.getSession(); Transaction tx = null; try{ tx = session.beginTransaction(); Order order = (Order) session.get(Order.class,1); System.out.println(order.getCustomer().getCustName()); Customer customer = (Customer) session.get(Customer.class,1); System.out.println(customer.getOrder().getOrderNumber()); }catch(Exception e){ if(tx!=null){ tx.rollback(); } e.printStackTrace(); }finally{ HibernateUtil.closeSession(session); } }