今天在javaeye上看到了这个文章,感觉超好.做下来看看....
前不久在搭建系统框架的时候遇到one-many与many-one的关系中一个比较麻烦的问题,就是inverse的设置,看到在这里也有许多人提出这个问题,也有许多解决方法,自己总结总结以
后作为资料库吧。
1.现在假设有两个类Customer与Order,一个Customer可以有多个Order
2. 如果在Customer.hbm.xml中设置inverse=投入额,那么代表customer与order的关系由order来维护,就是说如果采用 Customer c=customerDao.findById("1"),c.getOrders.add(new Order()),那么在数据库层面表现为,首先假设在order表格中customer_id可以为空,那么上面的代码会在order表中重保存一条 order记录,但是这条纪录中的customer_id=null,这正体现出inverse的设置作用,customer的保存不会维护 ustomer与order之间的关系。
?
3.如果在Customer.hbm.xml中设置iverser=false,那么代表customer与order的关系由customer来维护,
还是采用2种说明的代码,那么在数据库中表现为会增加一条order记录,并且这条记录的customer_d不为空.
?
具体代码说明:
1.模型类
- public?class?Customer?{??
- ??
- ????private?String?id;??
- ??
- ????private?String?name;??
- ??
- ????private?Set?orders?=?new?HashSet();??
- ??
- ????public?String?getId()?{??
- ????????return?id;??
- ????}??
- ??
- ????public?void?setId(String?id)?{??
- ????????this.id?=?id;??
- ????}??
- ??
- ????public?String?getName()?{??
- ????????return?name;??
- ????}??
- ??
- ????public?void?setName(String?name)?{??
- ????????this.name?=?name;??
- ????}??
- ??
- ????public?Set?getOrders()?{??
- ????????return?orders;??
- ????}??
- ??
- ????public?void?setOrders(Set?orders)?{??
- ????????this.orders?=?orders;??
- ????}??
- }??
public class Customer { private String id; private String name; private Set orders = new HashSet(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getOrders() { return orders; } public void setOrders(Set orders) { this.orders = orders; } }
?
?
- public?class?Order?{??
- ??
- ????private?String?id;??
- ??
- ????private?Customer?customer;??
- ??
- ????private?String?address;??
- ??
- ????public?String?getAddress()?{??
- ????????return?address;??
- ????}??
- ??
- ????public?void?setAddress(String?address)?{??
- ????????this.address?=?address;??
- ????}??
- ??
- ????public?Customer?getCustomer()?{??
- ????????return?customer;??
- ????}??
- ??
- ????public?void?setCustomer(Customer?customer)?{??
- ????????this.customer?=?customer;??
- ????}??
- ??
- ????public?String?getId()?{??
- ????????return?id;??
- ????}??
- ??
- ????public?void?setId(String?id)?{??
- ????????this.id?=?id;??
- ????}??
- }??
public class Order { private String id; private Customer customer; private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public String getId() { return id; } public void setId(String id) { this.id = id; } }?
?
1.配置文件
- "1.0"?encoding="utf-8"?>??
- "-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??
- ??
- <hibernate-mapping>??
- ????<class?name="domain.Customer"?table="customers"?catalog="blogday">??
- ????????<id?name=<span>"id">??
- ????????????<column?name=<span>"id"?/>??
- ????????????<generator?<span>class="native"></generator>??
- ????????</id>??
- ????????<property?name=<span>"name">??
- ????????????<column?name=<span>"name"?length="20"?/>??
- ????????</property>??
- ????????<set?name=<span>"orders"?lazy="true"?inverse="true"?cascade="all">??
- ????????????<key?column=<span>"customer_id"?/>??
- ????????????<one-to-many?<span>class="domain.Order"?/>??
- ????????</set>??
- ????class>??
- </hibernate-mapping>??
- ???
<hibernate-mapping> <class name="domain.Customer" table="customers" catalog="blogday"> <id name="id"> <column name="id"> <generator class="native"></generator> </id> <property name="name"> <column name="name" length="20"> </property> <set name="orders" lazy="true" inverse="true" cascade="all"> <key column="customer_id"> <one-to-many class="domain.Order"> </set> </class> </hibernate-mapping> ??
- "1.0"?encoding="utf-8"?>??
- "-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??
- ??
- <hibernate-mapping>??
- ????<class?name="domain.Order"?table="orders"?catalog="blogday">??
- ????????<id?name=<span>"id">??
- ????????????<column?name=<span>"id"?/>??
- ????????????<generator?<span>class="native"></generator>??
- ????????</id>??
- ????????<property?name=<span>"address">??
- ????????????<column?name=<span>"address"?length="20"?/>??
- ????????</property>??
- ????????<many-to-one?name=<span>"customer"?column="customer_id"??
- ????????????class="domain.Customer"?cascade="none"?/>??
- ????class>??
- </hibernate-mapping>??
<hibernate-mapping> <class name="domain.Order" table="orders" catalog="blogday"> <id name="id"> <column name="id"> <generator class="native"></generator> </id> <property name="address"> <column name="address" length="20"> </property> <many-to-one name="customer" column="customer_id" class="domain.Customer" cascade="none"> </class> </hibernate-mapping>?
3.测试代码
- public?void?testSaveOrdersByInverse_Error_Method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order2);??
- ????????c.getOrders().add(order);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNotNull(e);??
- ????}??
- ???????
public void testSaveOrdersByInverse_Error_Method() { Exception e = null; Customer c = customerDao.findById("1"); Order order = new Order(); Order order2 = new Order(); c.getOrders().add(order2); c.getOrders().add(order); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNotNull(e); } ?
4. 结果说明
?
4.1? 如果设置invserse=true,那么方法testSaveOrdersByInverse_Error_Method()在测试的时候
customerDao.getHibernateTemplate().flush()进行flush的时候会发生错误,错误原因是在order表格中
不 允许customer_id=null,而在此时方法testSaveOrdersByInverse_Error_Method会级联保存order记 录,但是保存的order记录中hibernate却设置customer_id=null,这也说明了inverse=true说明customer不 负责与order之间的引用关系,只是简单的保存记录,并没有建立起二者之间的关系.
?
4.2如果设置 inverse=false,说明customer与order之间的关系由customer来维护,那么上面的测试方法 testSaveOrdersByInverse_Error_Method()不但会保存相应的order记录,并且order记录中的 customer_id也不为空.
?
5.性能考虑
?
5.1 如果设置inverse=true,用如下代码测试
- public?void?testSaveOrdersByInverse_Right_method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order1?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order1);??
- ????????c.getOrders().add(order2);??
- ????????order1.setCustomer(c);??
- ????????order2.setCustomer(c);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNull(e);??
- ????}??
public void testSaveOrdersByInverse_Right_method() { Exception e = null; Customer c = customerDao.findById("1"); Order order1 = new Order(); Order order2 = new Order(); c.getOrders().add(order1); c.getOrders().add(order2); order1.setCustomer(c); order2.setCustomer(c); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNull(e); }
?? 测试结果为:
?
- Hibernate:?select?orders0_.customer_id?as?customer3_1_,?orders0_.id?as?id1_,?orders0_.id?as?id1_0_,?orders0_.address?as?address1_0_,?orders0_.customer_id?as?customer3_1_0_?from?blogday.orders?orders0_?where?orders0_.customer_id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
Hibernate: select orders0_.customer_id as customer3_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.address as address1_0_, orders0_.customer_id as customer3_1_0_ from blogday.orders orders0_ where orders0_.customer_id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: insert into blogday.orders (address, customer_id) values (?, ?)??? 说明没插入一条order只需要执行一次插入操作,没有额外的代码执行.
?
5.2 如果设置inverse=false,也顺便设置数据库中order表格中customer_id可以为空,这样下面的代码才不会 抛出错误
- public?void?testSaveOrdersByInverse_Error_Method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order1?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order1);??
- ????????c.getOrders().add(order2);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNull(e);??
- ????}??
public void testSaveOrdersByInverse_Error_Method() { Exception e = null; Customer c = customerDao.findById("1"); Order order1 = new Order(); Order order2 = new Order(); c.getOrders().add(order1); c.getOrders().add(order2); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNull(e); }
?测试结果:
- Hibernate:?select?customer0_.id?as?id0_0_,?customer0_.name?as?name0_0_?from?blogday.customers?customer0_?where?customer0_.id=???
- Hibernate:?select?orders0_.customer_id?as?customer3_1_,?orders0_.id?as?id1_,?orders0_.id?as?id1_0_,?orders0_.address?as?address1_0_,?orders0_.customer_id?as?customer3_1_0_?from?blogday.orders?orders0_?where?orders0_.customer_id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?update?blogday.orders?set?customer_id=??where?id=???
- Hibernate:?update?blogday.orders?set?customer_id=??where?id=???
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_ from blogday.customers customer0_ where customer0_.id=? Hibernate: select orders0_.customer_id as customer3_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.address as address1_0_, orders0_.customer_id as customer3_1_0_ from blogday.orders orders0_ where orders0_.customer_id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: update blogday.orders set customer_id=? where id=? Hibernate: update blogday.orders set customer_id=? where id=?
?
?
插入customer也会相应的插入order记录,这个在上面已经说明.但是看这里多出了两条update语句,针对每一个孩子都去更新父亲的id明显速度很慢,因为父亲有个孩子的集合,他无法知道哪个孩子的父亲id已经指向自己了,所以对于每一个孩子,都要更新父 亲使他只想自己,而这个关系由孩子维护就好多了,每个孩子只有一个父亲,只有设置过的才需要更新,所以显然,这个父子关系由孩子来维护比较省力.减轻了数 据库的负担.
?
?
正对以上情况在设置one-many或者many-one或者many-many的时候需要谨慎用inverse,而且从性能上来说一般还是有字节点来维护与父节点的关系比较好如下代码就可以简单而且高效来建立二者之间的关系.
?
- public?void?testSave()?{??
- ????Customer?customer?=?customerDao.findById("1");??
- ????Order?order?=?new?Order();??
- ????order.setCustomer(customer);??
- ????orderDao.save(order);??
- ????assertNotNull(order.getId());??
- }??
public void testSave() { Customer customer = customerDao.findById("1"); Order order = new Order(); order.setCustomer(customer); orderDao.save(order); assertNotNull(order.getId()); }?
这里的操作很简单而且数据库操作也仅有一条查询语句和一条插入语句.
?
- Hibernate:?select?customer0_.id?as?id0_0_,?customer0_.name?as?name0_0_?from?blogday.customers?customer0_?where?customer0_.id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_ from blogday.customers customer0_ where customer0_.id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?)?
?
?
?
?