hibernate自实现的set类如persiterSet,在hibernate有很大的用处:
用例1:
public void test5() {
Session session = HibernateUtil.getInstance().getSession();
Transaction tx = null;
tx = session.beginTransaction();
Customer customer=null;
try {
//延迟加载下的customer是存在session中的
customer = (Customer)session.get(Customer.class, 7);
tx.commit();
}catch(Exception e) {
tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
Session session1 = HibernateUtil.getInstance().getSession();
Transaction tx1 = null;
tx1 = session1.beginTransaction();
try {
session1.saveOrUpdate(customer);
tx1.commit();
}catch(Exception e) {
tx1.rollback();
e.printStackTrace();
}finally {
session1.close();
}
}
当执行saveOrUpdate()的时候不会发出任何语句。
当执行saveOrUpdate的时候只会发出一条更新customer的语句(上一条语句的更正)。
但是下面案例就不一样了:
public void test5() {
Session session = HibernateUtil.getInstance().getSession();
Transaction tx = null;
tx = session.beginTransaction();
Customer customer=null;
try {
//延迟加载下的customer是存在session中的
customer = (Customer)session.get(Customer.class, 11);
Hibernate.initialize(customer.getOrders());
tx.commit();
}catch(Exception e) {
tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
Session session1 = HibernateUtil.getInstance().getSession();
Transaction tx1 = null;
tx1 = session1.beginTransaction();
try {
session1.saveOrUpdate(customer);
tx1.commit();
}catch(Exception e) {
tx1.rollback();
e.printStackTrace();
}finally {
session1.close();
}
}
发出语句:
Hibernate: update t_customer set name=? where customer_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
Hibernate: update t_order set order_num=?, customer_id=? where order_id=?
为什么会出现这样的情况呢?
其实这就是Hibernate自实现set的功能,这里仅仅是set的它的一个作用哦!
persiterSet中有个init即初始化标记,在案例1中由于没有初始化该set,因此在更新游离态的时候,Hibernate发现set中的初始化标记为false,即没初始化,那么此时hiberante会认定该customer中的set集合没有发生改变,更不会发出update order null.
如果显式的使用Hibernate.initialize(customer.getOrders());,那么会更新初始化标记,为true,那么执行saveOrUpdate的时候,就无法判断customer中的set集合中的orders有没有改变,因此会发出好多条Update order 语句。
下面还有一个比较好的例子,不能说是奇葩,只能说hibernate的延迟机制吧
public void test5() {
Session session = HibernateUtil.getInstance().getSession();
Transaction tx = null;
tx = session.beginTransaction();
Customer customer=null;
try {
//延迟加载下的customer是存在session中的
customer = (Customer)session.load(Customer.class, 7);
tx.commit();
}catch(Exception e) {
tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
Session session1 = HibernateUtil.getInstance().getSession();
Transaction tx1 = null;
tx1 = session1.beginTransaction();
try {
session1.saveOrUpdate(customer);
tx1.commit();
}catch(Exception e) {
tx1.rollback();
e.printStackTrace();
}finally {
session1.close();
}
}
如果改为Load(customer),那么这个时候由于返回的是代理对象,那么Hibernate就一定能知道该代理对象是否被改变过,因为hibernate知道该代理对象到底有没有初始化!那么当执行saveOrUpdate()的时候不会发出update order set.......即更新orader的语句。
但是在delete操作的时候就会发现问题:
delete(customer),的时候如果customer这一端set没有设置级联,但是可以控制customer与order的关系:
Customer customer = (Customer)session.get(Customer.class, 13);
session.delete(customer);
这个时候会先发一条update order set cutomer_id=null,然后再发出delete cutomer
说明Hibernate在删除的时候会认为order集合为空存在脏数据(这句话是错误的,不是存在脏数据才update,而是因为customer必须控制关系),而customer控制了关系,因此会update order null,这与上面的案例不太符合。应该是hibernate在delete操作
的时候认为set为空的时候是改变过的吧。
但是如果:
显式的加载orders集合,Hibernate.initialize(orders);,那么这个时候删除的时候就会报错:
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade
其实 我觉得Hibernate是这样做的,hibernate中的perisiterset有个dirty属性,当改变set的时候会改变更新dirty为true,而此时没有改变,因此不会发出update.
在删除一个对象的时候,因为不存在级联删除,因此会报上面的错,因此删除之前必须先清除customer和order的关系才行,即cutomer.getOrders().remove(orders)
当然delete操作的原理与上面saveOrUpdate()机制是不一样的,目前只能这么想了。。