工作中用hibernate框架,但是呢一直只是粗略知道简单的CRUD,由于不太深入了解hibernate,工作上遇到很多莫名其妙的问题,所以买了一本孙卫琴的书补一下,写下一点琐碎的笔记做为记录,以免以后忘记了,温故而知新嘛。
问题一:
描述:Customer与Order一对多,建立双向关联,只记录关键,其他属性略去不写:
Customer配置文件中:
<set name="orders" inverse="true" cascade="delete" fetch="join"> <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
Order配置文件中:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" not-null="true" lazy="false" />
1.如果Customer配置文件中去掉inverse="true"或设置inverse="false",以下代码报org.hibernate.TransientObjectException:
Session session = sessionFactory.openSession(); session.beginTransaction(); Customer cus = new Customer("cus1"); Order order = new Order(); order.setOrderNumber("orderNumber1"); //order.setCustomer(cus);//不设置关联 Order order2 = new Order(); order2.setOrderNumber("orderNumber2"); //order2.setCustomer(cus);//不设置关联 cus.getOrders().add(order); cus.getOrders().add(order2); session.save(cus); session.getTransaction().commit(); session.close();
这是因为hibernate执行完insert以后还会执行更新,所以报上面的错:
Hibernate: insert into CUSTOMERS (NAME, ID) values (?, ?)
Hibernate: update ORDERS set CUSTOMER_ID=? where ID=?
2.如果设置inverse="true"则不会报错,这是因为session.save(cus);的时候将控制权反转给了对方即Order,不会产生update语句,这样的结果 是只产生一条Hibernate: insert into CUSTOMERS (NAME, ID) values (?, ?)。
3.如果修改Custom配置文件:
<set name="orders" inverse="false" cascade="all" fetch="join"> <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
再次执行上面的代码,结果将是期望得到的,即产生三条insert插入三条记录,然后再产生两条update更新两个order对象的外键custom_id,总共产生五条sql。
综上其实我们可以总结出来:
如果One这方在set标签中设置inverse=false,custom这方关联多少条order,就会产生多少条update去更新order的外键,即update Order set custom_id = ? where id = ?;它的作用只是产生update语句,一般为了不产生这些多余的sql,一般都设置 为true
问题二:关于inverse与cascade:
custom:
<set name="orders" inverse="true" cascade="all" fetch="join"> <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
order配置文件和上面相同
执行如下代码:
Session session = sessionFactory.openSession(); Customer cus = (Customer) session.load(Customer.class, 1L); session.beginTransaction(); session.delete(cus); session.getTransaction().commit(); session.close();
上面的代码表达的意思无非就是级联删除,假设数据库有一条ID为1的Customer,两条关联的order,想实现将Customer和其两条关联的order都删除,上面的代码获得如期的效果:
Hibernate: delete from ORDERS where ID=?
Hibernate: delete from ORDERS where ID=?
Hibernate: delete from CUSTOMERS where ID=?
但是如果将上述inverse="true"去掉:产生sql如下:
Hibernate: update ORDERS set CUSTOMER_ID=null where CUSTOMER_ID=?
Hibernate: delete from ORDERS where ID=?
Hibernate: delete from ORDERS where ID=?
Hibernate: delete from CUSTOMERS where ID=?
它会先去维护两者的关系产生update语句,这时如果你的数据库中外键字段CUSTOMER_ID如果不允许为NULL,则会直接在update时直接产生一个异常,下面SQL不再执行。
由上可以得出:如果cascade与inverse都存在,先inverse再cascade,即先维护关联关系再删除。
上面的结论有个例外,那就是:
<set name="orders" inverse="true" cascade="delete-orphan" fetch="join"> <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
如果inverse="true" cascade="delete-orphan",执行下面代码:
Session session = sessionFactory.openSession(); Customer cus = (Customer) session.load(Customer.class, 1L); session.beginTransaction(); Iterator<Order> it = cus.getOrders().iterator(); it.next(); it.remove(); session.getTransaction().commit(); session.close();
虽然inverse=true,不维护关联关系了,但还是会产生delete语句:
Hibernate: delete from ORDERS where ID=?
如果inverse="true" cascade="all",则上面代码不会删除order记录,不会删除记录。