精通Hibernate——建立双向一对多关联

来源:互联网 发布:iphone视频截图软件 编辑:程序博客网 时间:2024/06/06 17:33

当类与类之间建立了关联,就可以方便的从一个对象导航到另一个对象或者一组与他关联的对象。
对象位于内存中,在内存中从一个对象导航到另一个对象显然比到数据库中查询数据速度快多了。类与类之间到底建立双向还是单向都是由业务决定。以Customer和Order为例,如果软件应用有大量这样的需求:
根据客户可以查询该客户所有的订单
根据给定的订单可以查询发出订单的客户
以上需求就需要我们为Customer和Order类创建双向关联,代码如下:

public class Customer{    private Set orders = new HashSet();    public Set getOrders(){        return orders;    }    public void setOrders(Set orders){        this.orders = orders;    }}

有了以上属性,当Customer查询订单时候只需要调用customer.getOrders()方法即可。Hibernate要求在持久化类中定义集合属性,必须把属性定义为接口类型如:java.util.Map,java.util.ArrayList,java.util.Set等,这样可以提供Hibernate持久化类的透明性。
在定义orders集合属性时,通常把它初始化为集合实现类的一个实例,例如:
private Set orders = new HashSet();
这样可以提供程序的健壮性,避免orders中没有任何东西时也不会抛出NullPointException,例如:

Set orders = new HashSet();Iterator it = orders.iterator();while(it.hasNext()){    // do something}

下面我们说一下如何在配置文件中映射集合类型的orders属性,配置代码如下:

<set name="orders" cascade="save-update">    <key column="CUSTOMER_ID" />    <one-to-many class="com.fyw.Orders" /></set>

下面分别介绍下一些保存操作:
(1)saveCustomerAndOrdersWithCaseCade():该方法用于当set的casecade属性配置为save-update时Hibernate的运行时行为:

tx = session.beginTransaction();Customer customer = new Customer("tom",new HashSet());Order order = new Order();order.setOrderNumber("tom_order001");// 建立Customer与Orders对象的双向关联order.setCustomer(customer);customer.getOrders().add(order);session.save(customer);tx.commit();

(2)saveCustomerAndOrderWithInverse():该方法会依次调用下面两个方法
saveCustomerAndOrderSeparately()
先创建一个Customer对象和一个Order对象,不建立它们的关联关系,最后分别持久化这两个对象:

tx = session.beginTransaction();Customer customer = new Customer();customer.setName("Jack");Orders order = new Orders();order.setOrderNumber("Jack_order001");session.save(customer);session.save(order);tx.commit();

为了使以上代码运行正常需要将Order.hbm.xml文件中one-to-many元素的not-null属性设置为false,否则会因为违反参照完整性约束而产生异常
associateCustomerAndOrder()
该方法加载被saveCustomerAndOrderSeparately()方法持久化的Customer和Order对象,然后建立两者两者一对多的双向关联关系:

tx = session.beginTransaction();// 加载持久化对象Customer customer = (Customer)session.load(Customer.class,new Long(2));Order order = (Order)session.load(Order.class,new Long(2));// 建立Customer和Orders的双向关联order.setCustomer(customer);customer.getOrders().add(order);    tx.commit();

Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象状态的改变来同步更新数据库,此时Hibernate在清理以上Customer对象和Order对象时执行了以下两条sql语句:

update orders set order_numbers = 'Jack_order001',customer_id=2 where id = 2;update orders set customer_id=2 where id = 2;

尽管只是修改了Orders表的一条记录,但是执行两次SQL,这是因为Hibernate根据内存中持久化对象的状态变化类决定需要执行哪些sql语句
order.setCustomer(customer);会执行

update orders set order_numbers = 'Jack_order001',customer_id=2 where id = 2;customer.getOrders().addOrder(order);会执行:update orders set customer_id = 2 where id = 2;

重复执行多余的sql会影响Hibernate的性能,解决这一问题的办法是将inverse属性配置为true,默认为false.

<set name="orders" cascade="save-update" inverse="true">    <key column="CUSTOMER_ID" />    <one-to-many class="com.fyw.Orders" /></set>

以上代码表明在Customer和Order双向关联关系中,Customer的关联只是Order端关联的镜像,当Hibernate探测到持久化对象Customer和Order状态均发生变化时,仅按照Order对象状态变化来同步更新数据库,因此我们可以将associateCustomerAndOrder()改为:

tx = session.beginTransaction();// 加载持久化对象Customer customer = (Customer)session.load(Customer.class,new Long(2));Order order = (Order)session.load(Order.class,new Long(2));// order.setCustomer(customer);customer.getOrders().add(order);    tx.commit();

以上代码仅设置了Customer对象的Order属性,由于set的inverse属性为true,因此Hibernate不会按照Customer对象的状态变化来同步更新数据库。
因此:
1.在映射一对多的双向关联关系时,应该在many方把inverse属性设为true,这样可以提高性能
2.在建立两个对象的双向关联时,应该同时修改关联两端的对象的相应属性
级联删除
在deleteCustomer()方法中,先加载一个Customer对象,然后删除这个对象

tx = session.beginTransaction();Customer customer = (Customer)session.load(Customer.class,new Long(1));session.delete(customer);tx.commit();

当cascase属性取默认值none时,不会执行级联删除,除非配置为cascade属性设置delete

<set name="orders" cascade="delete" inverse="true">    <key column="CUSTOMER_ID" />    <one-to-many class="com.fyw.Orders" /></set>

casecade还有一个all-delete-orphan属性,含义为:
当保存或者更新Customer对象时,相当于cascade=’save-udpate’,当删除Customer对象时相当于cascade=”delete”
删除不再和Customer对象关联的所有的Order对象

0 0