工作中用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记录,不会删除记录。