Java实战之02Hibernate-04多表映射
Posted 铭昊Coder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实战之02Hibernate-04多表映射相关的知识,希望对你有一定的参考价值。
十、多表映射
0、内容补充:数据完整性
作用:防止用户的误操作。
实体完整性:主键。用于确定表中唯一的一条记录。
域完整性:表中的字段。
数据类型约束:
非空约束:
唯一约束:
参照完整性:
多表设计:表之间的关系
一对多(用的最多的)
多对多(比较重要)
一对一(实际开发中,根本不用)
1、一对多关系映射(非常重要)
1.1、单向多对一映射
1 /** 2 * 客户的数据模型 3 * @author zhy 4 * 5 * 一个客户可以有多个订单 6 * 多个订单属于一个客户。 7 * 8 * 客户和订单之间的关系是一对多 9 */ 10 public class Customer implements Serializable { 11 12 private Integer id; 13 private String name; 14 private Integer age; 15 16 //一对多关系映射:一个客户可以有多个订单 17 private Set<Order> orders = new HashSet<Order>(0); 18 19 20 public Integer getId() { 21 return id; 22 } 23 public void setId(Integer id) { 24 this.id = id; 25 } 26 public String getName() { 27 return name; 28 } 29 public void setName(String name) { 30 this.name = name; 31 } 32 public Integer getAge() { 33 return age; 34 } 35 public void setAge(Integer age) { 36 this.age = age; 37 } 38 public Set<Order> getOrders() { 39 return orders; 40 } 41 public void setOrders(Set<Order> orders) { 42 this.orders = orders; 43 } 44 @Override 45 public String toString() { 46 return "Customer [id=" + id + ", name=" + name + ", age=" + age + "]"; 47 } 48 }
1 /** 2 * 订单的数据模型 3 * @author zhy 4 * 5 * 一个客户可以有多个订单 6 * 多个订单属于一个客户。 7 * 8 * 客户和订单之间的关系是一对多 9 */ 10 public class Order implements Serializable { 11 12 private Integer id; 13 private String ordernum; 14 private Float money; 15 16 //多对一关系映射:多个订单属于一个客户。 17 private Customer customer; 18 19 20 public Integer getId() { 21 return id; 22 } 23 public void setId(Integer id) { 24 this.id = id; 25 } 26 public String getOrdernum() { 27 return ordernum; 28 } 29 public void setOrdernum(String ordernum) { 30 this.ordernum = ordernum; 31 } 32 public Float getMoney() { 33 return money; 34 } 35 public void setMoney(Float money) { 36 this.money = money; 37 } 38 public Customer getCustomer() { 39 return customer; 40 } 41 public void setCustomer(Customer customer) { 42 this.customer = customer; 43 } 44 @Override 45 public String toString() { 46 return "Order [id=" + id + ", ordernum=" + ordernum + ", money=" + money + "]"; 47 } 48 }
1 <hibernate-mapping package="cn.itcast.domain"> 2 <class name="Customer" table="T_CUSTOMERS"> 3 <id name="id" column="id"> 4 <generator class="native"></generator> 5 </id> 6 <property name="name" column="NAME"></property> 7 <property name="age" column="AGE"></property> 8 <!-- 一对多关系映射: 9 set元素: 10 作用:映射集合元素 11 属性: 12 name:映射实体类中的集合属性 13 table:指定对应的表 14 key元素:它是set的子元素 15 作用:就是用于指定外键的 16 属性: 17 column:指定外键字段的名称 18 one-to-many元素:它是set的子元素 19 作用:描述当前实体映射文件和set中指定属性之间的关系。 20 属性: 21 class:指定是从表的实体类名称 22 --> 23 <set name="orders" table="T_ORDERS" cascade="save-update,delete" inverse="true"> 24 <key column="CUSTOMER_ID"></key> 25 <one-to-many class="Order"/> 26 </set> 27 </class> 28 </hibernate-mapping>
1 <hibernate-mapping package="cn.itcast.domain"> 2 <class name="Order" table="T_ORDERS"> 3 <id name="id" column="id"> 4 <generator class="native"></generator> 5 </id> 6 <property name="ordernum" column="ORDERNUM"></property> 7 <property name="money" column="MONEY"></property> 8 9 <!-- 多对一关系映射 10 使用的元素:many-to-one 11 属性: 12 name:指定的是在实体类中要映射的属性 13 class:指定该属性所对应的类 14 column:指定外键字段。 15 --> 16 <many-to-one name="customer" class="Customer" column="CUSTOMER_ID" cascade="save-update"></many-to-one> 17 </class> 18 </hibernate-mapping>
a、保存操作
1 /* 2 * 保存操作 3 * 需求: 4 * 保存两个订单,同时保存一个客户 5 * 一定是先保存订单,再保存客户 6 * 问题: 7 * 当我们先保存订单,再保存客户时,会执行5条SQL语句 8 * Hibernate: insert into T_ORDERS (ORDERNUM, MONEY, CUSTOMER_ID) values (?, ?, ?) 9 Hibernate: insert into T_ORDERS (ORDERNUM, MONEY, CUSTOMER_ID) values (?, ?, ?) 10 Hibernate: insert into T_CUSTOMERS (NAME, AGE) values (?, ?) 11 Hibernate: update T_ORDERS set ORDERNUM=?, MONEY=?, CUSTOMER_ID=? where id=? 12 Hibernate: update T_ORDERS set ORDERNUM=?, MONEY=?, CUSTOMER_ID=? where id=? 13 解决办法: 14 实际上我们只需要三条insert语句就够了 15 在保存时,先保存主表数据,再保存从表数据 16 */ 17 @Test 18 public void test1(){ 19 //数据准备 20 Customer c1 = new Customer(); 21 c1.setName("test"); 22 c1.setAge(18); 23 24 Order o1 = new Order(); 25 o1.setOrdernum("A001"); 26 o1.setMoney(100f); 27 28 Order o2 = new Order(); 29 o2.setOrdernum("A002"); 30 o2.setMoney(200f); 31 //建立单向多对一关联关系 32 o1.setCustomer(c1); 33 o2.setCustomer(c1); 34 35 Session s = HibernateUtil.getSession(); 36 Transaction tx = s.beginTransaction(); 37 //保存操作 38 s.save(c1); 39 s.save(o1); 40 s.save(o2); 41 42 tx.commit(); 43 s.close(); 44 }
b、查询操作
1 @Test 2 public void test2(){ 3 //数据准备 4 Customer c1 = new Customer();//临时态 5 c1.setName("test2"); 6 c1.setAge(28); 7 8 Session s = HibernateUtil.getSession(); 9 Transaction tx = s.beginTransaction(); 10 //查询id为1的订单 11 Order o1 = s.get(Order.class, 1);//持久态 12 tx.commit(); 13 s.close(); 14 }
c、持久态引用临时态报错
1 /* 2 * 更新操作 3 * 需求: 4 * 先创建一个订单,然后查询出来一个客户。 5 * 建立客户和新订单的关联关系。 6 * 更新客户 7 * 问题: 8 * 一个持久态对象,关联了一个临时态的对象。 9 * 解决办法: 10 * 配置级联保存更新 11 * <set name="orders" table="T_ORDERS" cascade="save-update"> 12 */ 13 @Test 14 public void test2(){ 15 16 //创建一个新的订单 17 Order o1 = new Order();//临时态 18 o1.setOrdernum("A003"); 19 o1.setMoney(100f); 20 21 Session s = HibernateUtil.getSession(); 22 Transaction tx = s.beginTransaction(); 23 //查询一个客户 24 Customer c1 = s.get(Customer.class, 1);//持久态 25 //建立双向关联关系 26 c1.getOrders().add(o1); 27 o1.setCustomer(c1); 28 //更新操作 29 s.update(c1); 30 31 tx.commit(); 32 s.close(); 33 }
d、级联保存和更新
1 /* 2 * 3 * 更新操作 4 * 需求: 5 * 创建一个新的客户,查询出来一个订单。把新客户和查询的订单建立关联关系。 6 * 然后更新订单 7 * 问题: 8 * 一个持久态对象,关联了一个临时态对象。会报错。 9 * 解决办法: 10 * 思路:在更新之前,先把临时态对象,转成持久态。(先执行保存,再执行更新) 11 * 执行级联保存更新。 12 * 在配置文件中配置:要想级联谁,就在对应的映射属性上配置 13 * cascade属性:就是用于配置级联操作的 14 * <many-to-one name="customer" class="Customer" column="CUSTOMER_ID" cascade="save-update"> 15 */ 16 @Test 17 public void test2(){ 18 //数据准备 19 Customer c1 = new Customer();//临时态 20 c1.setName("test2"); 21 c1.setAge(28); 22 23 Session s = HibernateUtil.getSession(); 24 Transaction tx = s.beginTransaction(); 25 //查询id为1的订单 26 Order o1 = s.get(Order.class, 1);//持久态 27 //建立订单和客户的单向多对一关联关系 28 o1.setCustomer(c1); 29 //更新订单 30 s.update(o1); 31 tx.commit(); 32 s.close(); 33 }
1.2、双向关联映射
注意事项:
Hibernate要求在持久化类中定义集合属性时,必须把属性声明为接口类型,如Set、Map、List.声明接口类型可提高持久化类的透明性。(与延迟加载有关)
通常在定义集合属性时,直接初始化为一个实现类的实例。可避免空指针异常。
a、双向关联关系保存操作
1 /* 2 * 保存操作 3 * 需求: 4 * 先保存客户,再保存订单 5 问题: 6 当我们建立了双向关联关系之后,就算是先保存主表,再保存从表,也是会产生5条SQL语句 7 Hibernate: insert into T_CUSTOMERS (NAME, AGE) values (?, ?) 8 Hibernate: insert into T_ORDERS (ORDERNUM, MONEY, CUSTOMER_ID) values (?, ?, ?) 9 Hibernate: insert into T_ORDERS (ORDERNUM, MONEY, CUSTOMER_ID) values (?, ?, ?) 10 Hibernate: update T_ORDERS set CUSTOMER_ID=? where id=? 11 Hibernate: update T_ORDERS set CUSTOMER_ID=? where id=? 12 解决办法: 13 思路:让主表的集合放弃维护关联关系的权利。 14 操作方式:注释上 15 c1.getOrders().add(o1); 16 c1.getOrders().add(o2); 17 */ 18 @Test 19 public void test1(){ 20 //数据准备 21 Customer c1 = new Customer(); 22 c1.setName("testC"); 23 c1.setAge(18); 24 25 Order o1 = new Order(); 26 o1.setOrdernum("C001"); 27 o1.setMoney(100f); 28 29 Order o2 = new Order(); 30 o2.setOrdernum("C002"); 31 o2.setMoney(200f); 32 33 //建立双向一对多关联关系 34 o1.setCustomer(c1); 35 o2.setCustomer(c1); 36 37 //c1.getOrders().add(o1); 38 //c1.getOrders().add(o2); 39 40 41 42 43 Session s = HibernateUtil.getSession(); 44 Transaction tx = s.beginTransaction(); 45 //保存操作 46 s.save(c1); 47 s.save(o1); 48 s.save(o2); 49 50 tx.commit(); 51 s.close(); 52 }
b、双向关联关系,持久态关联临时态的问题及解决
1 /* 2 * 更新操作 3 * 需求: 4 * 先创建一个订单,然后查询出来一个客户。 5 * 建立客户和新订单的关联关系。 6 * 更新客户 7 * 问题: 8 * 一个持久态对象,关联了一个临时态的对象。 9 * 解决办法: 10 * 配置级联保存更新 11 * <set name="orders" table="T_ORDERS" cascade="save-update"> 12 */ 13 @Test 14 public void test2(){ 15 16 //创建一个新的订单 17 Order o1 = new Order();//临时态 18 o1.setOrdernum("A003"); 19 o1.setMoney(100f); 20 21 Session s = HibernateUtil.getSession(); 22 Transaction tx = s.beginTransaction(); 23 //查询一个客户 24 Customer c1 = s.get(Customer.class, 1);//持久态 25 //建立双向关联关系 26 c1.getOrders().add(o1); 27 o1.setCustomer(c1); 28 //更新操作 29 s.update(c1); 30 31 tx.commit(); 32 s.close(); 33 }
c、变更关系(关于双向关联的处理办法)
1 /* 2 * 需求:变更关系 3 * 把id为1的订单,从属于2号客户,改为属于1号客户 4 * 解决办法: 5 * 使用配置的方式,来实现让有集合的一方,放弃维护的权利 6 * inverse:是否放弃维护的权利 7 * 取值:true放弃 和 false 不放弃(默认值)。 8 * inverse用于应该只出现在set元素上 9 * <set name="orders" table="T_ORDERS" cascade="save-update" inverse="true" > 10 */ 11 @Test 12 public void test3(){ 13 Session s = HibernateUtil.getSession(); 14 Transaction tx = s.beginTransaction(); 15 Customer c1 = s.get(Customer.class, 1);//查询出来1号客户 16 Order o1 = s.get(Order.class, 1);//查询出啦1号订单 17 //建立双向关联关系 18 c1.getOrders().add(o1); 19 o1.setCustomer(c1); 20 //更新操作 21 s.update(c1); 22 23 tx.commit(); 24 s.close(); 25 26 //System.out.println(c1.getOrders()); 27 }
为了保持程序的健壮性,建议是双向关联,就建立双向关联的关系。
弊端:当我们使用了双向关联时,会有冗余的SQL语句执行,造成程序的效率下降。
解决办法:不要双向维护关联关系,让少的一方放弃维护权利。(但不要在代码中修改,而是写在配置文件中)
d、解除关系
1 /* 2 * 解除关系 3 * 需求: 4 * 把1订单和1号客户之间的关系解除 5 */ 6 @Test 7 public void test6(){ 8 Session s = HibernateUtil.getSession(); 9 Transaction tx = s.beginTransaction(); 10 Customer c1 = s.get(Customer.class,1); 11 Order o1 = s.get(Order.class, 1); 12 13 //解除1号订单和1号客户之间的关系 14 c1.getOrders().remove(o1); 15 o1.setCustomer(null); 16 17 18 tx.commit(); 19 s.close(); 20 }
e、删除操作
/* * 需求: * 删除一个客户 * * 在直接删除客户的时候,如果客户的集合属性(set元素)上没有配置inverse=true, * 会直接把客户删除掉,同时把订单中关联改该客户的id置为null * * 在直接删除客户的时候,如果客户的集合属性(set元素)上配置了inverse=true, * 如果有订单以上是关于Java实战之02Hibernate-04多表映射的主要内容,如果未能解决你的问题,请参考以下文章
Java实战之02Hibernate-01简介常用接口CRUD操作
高效 告别996,开启java高效编程之门 3-7实战:常用中间操作演示之:过滤/映射/扁平化 filter/map/flagMap