单向关联
仅仅建立从Order到Customer的多对一关联,即仅仅在Order类中定义customer属性。或者仅仅建立从Customer到Order的一对多关联,即仅仅Customer类中定义orders集合。
单向 n-1 关联只需从 n 的一端可以访问 1 的一端
域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性
关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
Hibernate 使用 <many-to-one>
元素来映射多对一关联关系
创建持久化类
package cn.itcast.hibernate0909.onetomany.test;
public class Customer {
private Integer cid;
private String name;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package cn.itcast.hibernate0909.onetomany.test;
public class Order {
private Integer oid;
private String orderNumber;
private Double price;
private Customer customer;
public Integer getOid() {
return oid;
}
public void setOid(Integer oid) {
this.oid = oid;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
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://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="cn.itcast.hibernate0909.onetomany.test.Customer" table="customers">
<id name="cid" type="integer">
<column name="cid"></column>
<generator class="increment"></generator>
</id>
<property name="name" type="string" >
</property>
</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://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="cn.itcast.hibernate0909.onetomany.test.Order" table="orders">
<id name="oid" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="orderNumber" type="string" > </property>
<property name="price" type="double"> </property>
<many-to-one name="customer" class="cn.itcast.hibernate0909.onetomany.test.Customer" >
<column name="customer_id" ></column>
</many-to-one>
</class>
</hibernate-mapping>
配置hibernate.cfg.xml文件
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- 是用来描述数据库的连接 -->
<session-factory>
<!--
驱动
-->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!--
url
-->
<property name="connection.url">
jdbc:mysql://localhost:3306/hibernate0909
</property>
<!--
username
-->
<property name="connection.username">root</property>
<!--
password
-->
<property name="connection.password">123</property>
<!--
hibernate针对建表的操作
update 如果有表,检查表的结构,如果没有则创建
create-drop 启动hibernate创建表,结束hibernate删除表
create 每次启动都重新创建表
validate 每次启动都检查表的结构
-->
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<mapping
resource="cn/itcast/hibernate0909/onetomany/test/Order.hbm.xml" />
<mapping
resource="cn/itcast/hibernate0909/onetomany/test/Customer.hbm.xml" />
</session-factory>
</hibernate-configuration>
保存操作
1.先保存订单,再保存客户
junit测试结果:
从这里可以看出执行了两条insert语句,一条update语句
2.先保存客户,再保存订单
junit测试结果:
可以看出这种情况程序是执行了两条insert语句,而没有执行update语句。
保存操作的总结
保存订单时会发出两条insert语句。
而保存客户时会发出两条insert语句和一条update语句
因为订单是多的一方,而客户是一的一方,所以在多对一的关系中,应该使用一的一方进行操作,这样效率更高。
先保存客户,再保存订单,在下面的代码中注释掉session.save(c),会有什么后果
junit测试结果:
查询订单
junit测试结果:
解释说明:
当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。
修改映射文件,重新测试:
单向关联全部源码
package cn.itcast.hibernate0909.onetomany.test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
public class OneToManyTest {
private static SessionFactory sessionFactory;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/hibernate0909/onetomany/test/hibernate.cfg.xml");
sessionFactory = configuration.buildSessionFactory();
}
/**
* 实验1:先保存订单,再保存客户
*/
@Test
public void insert(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setName("菜肉");
Order orders = new Order();
orders.setOrderNumber("11a");
//将订单和客户关联起来
orders.setCustomer(c);
//保存订单
session.save(orders);
//保存客户
session.save(c);
tx.commit();
session.close();
}
/**
* 实验2:先保存客户,再保存订单
*/
@Test
public void insert2(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setName("菜肉2");
Order orders = new Order();
orders.setOrderNumber("22a");
//将订单和客户关联起来
orders.setCustomer(c);
//保存客户
session.save(c);
//保存订单
session.save(orders);
tx.commit();
session.close();
}
//查询订单
@Test
public void loadOrder(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//通过id查询订单
Order orders = (Order) session.load(Order.class, 1);
System.out.println(orders.getOid() + " " +orders.getOrderNumber());
//打印订单对应的客户
System.out.println(orders.getCustomer().getCid() + " " + orders.getCustomer().getName());
tx.commit();
session.close();
}
/**
* 实验3:先保存客户,再保存订单,注释掉session.save(c)
*/
@Test
public void insert3(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setName("菜肉2");
Order orders = new Order();
orders.setOrderNumber("22a");
//将订单和客户关联起来
orders.setCustomer(c);
//保存客户
// session.save(c);
//保存订单
session.save(orders);
tx.commit();
session.close();
}
}
双向关联
双向 1-n 与 双向 n-1 是完全相同的两种情形
双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然.
域模型
从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性
关系数据模型
ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
建立一对多的双向关联关系
配置新的映射文件和hibernate.cfg.xml文件
保存客户和订单(客户和订单建立双向关联)
保存客户和不保存订单
在下面的代码中注释掉session.save(order1),会有什么后果
junit测试后发现报错:
级联保存和更新
当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定set元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。
修改映射文件,重新测试:
同理,注释掉session.save(c),同样会报TransientObjectException错误.需要对Order.hbm.xml文件中添加cascade属性为save-update
查询客户和订单
查询客户和订单(图)
订单变更客户
上述例子产生了两条update语句:
原因为:
Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的改变来同步更新数据库,因此执行了上述的两条更新语句所以会产生两条update语句
c.getOrders().add(o);执行了一条update语句
o.setCustomer(c);执行了一条update语句
级联删除
inverse属性
- Inverse来源
在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
- Inverse设值
在没有设置 inverse=true 的情况下,父子两边都维护父子关系
- Inverse设值原则
在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句
在一的一方设值inverse为TRUE表明一的一方不维护其关系,这样就会发出一条update语句,这样效率也就提高了。
- Inverse结论
1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,
这可以提高性能。
2.在建立两个对象的关联时,应该同时修改关联两端的相应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码
不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改
关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);
解除关联关系
解除某个订单与某个客户的关系
junit测试结果:
级联删除
junit测试结果:
错误的根本原因:就是主外键的约束
外键(jdbc)—>inverse(hibernate):inverse的属性值为true,没有关联约束,不维护关系
修改映射文件
测试结果:
测试结果通过
在数据库中对集合进行排序
<set>
元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序
双向关联全部源码
package cn.itcast.hibernate0909.onetomany.test2;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
public class OneToManyTest {
private static SessionFactory sessionFactory;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/hibernate0909/onetomany/test2/hibernate.cfg.xml");
sessionFactory = configuration.buildSessionFactory();
}
/**
* 实验1:保存客户和订单(客户和订单建立双向关联)
*/
@Test
public void insert(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Order orders = new Order();
orders.setOrderNumber("11a");
Customer c = new Customer();
c.setName("菜肉");
//客户和订单关联的双向关联
c.getOrders().add(orders);
orders.setCustomer(c);
//保存客户
session.save(c);
//保存订单
session.save(orders);
tx.commit();
session.close();
}
/**
* 实验2:保存客户和不保存订单
* 在下面的代码中注释掉session.save(order1),会有什么后果
*/
@Test
public void insert2(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Order orders = new Order();
orders.setOrderNumber("22a");
Customer c = new Customer();
c.setName("菜肉2");
//客户和订单关联的双向关联
c.getOrders().add(orders);
orders.setCustomer(c);
//保存客户
session.save(c);
//保存订单
//session.save(orders);
tx.commit();
session.close();
}
//查询客户和订单
@Test
public void loadOrder(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//通过客户的id查询订单
Customer c = (Customer) session.load(Customer.class, 1);
System.out.println(c.getName());
//关联到该客户对应的订单
Set<Order> set = c.getOrders();
for (Order order : set) {
System.out.println(order.getOid() +" "+order.getOrderNumber());
}
tx.commit();
session.close();
}
/**
* 实验3:解除某个订单与某个客户的关系
*/
@Test
public void deleteRelastion(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//查询客户
Customer c = (Customer) session.load(Customer.class, 1);
//查询订单
Order o = (Order) session.load(Order.class, 3);
//设置订单关联的客户为null
o.setCustomer(null);
//从客户集合删除订单
c.getOrders().remove(o);
tx.commit();
session.close();
}
/**
* 实验3:解除某个订单与某个客户的关系
*/
@Test
public void deleteCascade(){
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//查询客户
Customer c = (Customer) session.load(Customer.class, 1);
session.delete(c);
tx.commit();
session.close();
}
}