场景:customer和order 一对多双向关联
public class Order {
private Long orderId;
private String orderDetail;
private Customer customer;
public Order(){
}
public Order(String orderDetail){
this.orderDetail=orderDetail;
}
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public String getOrderDetail() {
return orderDetail;
}
public void setOrderDetail(String orderDetail) {
this.orderDetail = orderDetail;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
public class Customer {
private Long customerId;
private String customerName;
private Set<Order> orders=new HashSet<Order>();
public Customer(){}
public Customer(String customerName){
this.customerName=customerName;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
Customer.hbm.xml
<hibernate-mapping>
<class name="entity.Customer" table="customers">
<id name="customerId" type="long" column="customer_id">
<generator class="identity"></generator>
</id>
<property name="customerName" type="string" column="customer_name"
not-null="true" length="20" />
<set name="orders" cascade="save-update">
<key column="customer_id"></key>
<one-to-many class="entity.Order"/>
</set>
</class>
</hibernate-mapping>
Order.hbm.xml
<hibernate-mapping>
<class name="entity.Order" table="orders">
<id name="orderId" type="long" column="order_id">
<generator class="identity"></generator>
</id>
<property name="orderDetail" type="string" column="order_detail"
not-null="true" length="20" />
<many-to-one name="customer" column="customer_id"
class="entity.Customer" lazy="false"/>
</class>
</hibernate-mapping>
实验:
(1).saveCustomerAndOrderSeparely()方法,先创建一个Customer和Order对象,不建立他们的关联,最后分别持久化这两个对象
(注意:此时 order.hbm.xml中的many-to-one没有not-null属性,即其属性默认为false,这样可以保证不违反主键非空约束)
public void saveCustomerAndOrderSeparely(){
Session session=null;
Transaction tx=null;
try{
session=HibernateSessionFactory.getSession();
Customer customer=new Customer("tom");
Order order1=new Order("order1");
tx=session.beginTransaction();
session.save(customer);
session.save(order1);
tx.commit();
}catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
session.close();
}
}
运行该方法将执行一下两条insert 语句:
insert into customer(customer_id,customer_name) values(1,"tom");
insert into orders(order_id,order_name) values(1,"order1",null);
(2)先加载持久化对象Customer和Order对象,然后建立两者的一对多双向关联关系:
public void associateCustomerAndOrder(){
Session session=null;
Transaction tx=null;
try{
session=HibernateSessionFactory.getSession();
tx=session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, (long)1);
Order order=(Order)session.load(Order.class,(long)1);
//建立customer和order关联
order.setCustomer(customer);
customer.getOrders().add(order);
tx.commit();
}catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
session.close();
}
}
Hibernate会自动清理缓存中的所有持久化对象, 按照持久化对象的属性变化来同步更新数据库。如果把Customer.hbm.xml中的<set>元素的inverse属性设置为false,那么hibernate在清理以上Customer对象和Order对象是执行一下两条sql语句:
update orders set order_detail='tom',customer_id=1 where order_id=1;
update orders set customer_id=1 where order_id=2;
尽管实际上只是操作了orders表的一条记录,但是以上sql语句表明hibernate执行了两次update操作。这是因为hibernate根据内存中持久化对象的属性变化来决定需要执行那些sql语句。当建立order对象和customer对象的双向关联关系时,需要程序中分别修改这两个对象的属性:
(1)修改Order对象,建立order对象到customer对象的多对一关联关系:
order.setCustomer(customer);
Hibernate探测到持久化对象Order的属性的上述变化后,执行相应的sql语句为:
update orders set order_detail='tom',customer_id=1 where order_id=1;
(2)修改Customer对象,建立Customer对象到Order对象的一对多关联关系:
customer.getOrders().add(order);
Hibernate探测到持久化对象Customer的属性的上述变化,执行相应的sql语句:
update orders set customer_id=1 where order_id=2;
重复的执行多余的sql语句会影响java应用的性能,解决这一问题的办法是把<set>元素的inverse属性设置为true,该属性默认为false;
<set name="orders" cascade="save-update" inverse="true">
<key column="customer_id"></key>
<one-to-many class="entity.Order"/>
</set>
以上代码表示在Customer和Order的双向关联中,customer端的关联只是order端关联的镜像。当Hibernate探测到持久化对象Customer和Order的属性均发生变化时,仅按照Order对象的属性的变化同步更新数据库。
按照上述方法修改Customer.hbm.xml文件,在运行associateCustomerAndOrder()方法,Hibernate只执行一条sql语句:
session=HibernateSessionFactory.getSession();
tx=session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, (long)1);
Order order=(Order)session.load(Order.class,(long)1);
order.setCustomer(customer);
//customer.getOrders().add(order);
tx.commit();
如果对associateCustomerAndOrder方法做如下修改
session=HibernateSessionFactory.getSession();
tx=session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, (long)1);
Order order=(Order)session.load(Order.class,(long)1);
order.setCustomer(customer);
//customer.getOrders().add(order);
tx.commit();
以上代码仅设置了Order对象的customer属性,hibernate仍然会按照Order对象的属性变化来同步更新数据库,执行一下sql:
update orders set order_detail='tom',customer_id=1 where order_id=1;
如果对associateCustomerAndOrder方法做如下修改
session=HibernateSessionFactory.getSession();
tx=session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, (long)1);
Order order=(Order)session.load(Order.class,(long)1);
//order.setCustomer(customer);
customer.getOrders().add(order);
tx.commit();
以上代码仅设置了customer对象的orders属性,由于<set>元素的inverse属性为true,因此Hibernate不会按照Customer对象的属性变化来同步更新数据库。
根据上述实验,可以得出这样的结论:
〈1〉在映射一对多双向关联关系时,应该将"one"方把<set>元素的inverse属性设置为true,可以提高应用性能。
〈2〉在建立两个对象的双向关联时,应该同时修改关联对象两端对象的相应属性。
order.setCustomer(customer);
customer.getOrders().add(order);
这样才会使程序更加健壮,提高业务逻辑曾的独立性,使业务逻辑层的代码不受hibernate实现的影响。同理,当解除双向关联的关系时,也应该修改关联两段的对象的相应属性:
customer.getOrders().remove(order);
order.setCustomer(null);