Hibernate inverse, cascade, update, insert, fetch用法

1. inverse="true"

 

例如 one-to-many 关系User (1) - Order(*)

 

Java代码:

 

 

public class User {

 

    private Set<Order> orders = new HashSet<Order>();

 

    // ...

}

 

public class Order {

 

    private User user;

    // ..

}


对应的 mapping files:

Order.hbm.xml

<hibernate-mapping package="org.pprun.hjpetstore.domain">
  <class name="Order" table="Orders">
    ...

    <!-- owner side of this bidirectional one-to-many association (user to order)-->
    <many-to-one column="userId" foreign-key="fk_order_userId" name="user" not-null="true" />

  </class>
</hibernate-mapping>


User.hbm.xm

<hibernate-mapping package="org.pprun.hjpetstore.domain">
  <class name="User" table="User">
   ...

    <set cascade="all" inverse="true" name="orders">
      <key column="userId" not-null="true"/>
      <one-to-many class="Order"/>
    </set>
  </class>
</hibernate-mapping>


好了,以上都是作为开发者必须了解的内容,也是日常工作之一。


inverse 的由来

在 Java 的世界里,我们将利用如下代码建立起 object instance 关系:

user.getOrders().add(newOrder);
order.setUser(user);

这个建立关系的代码一般会反映在 domain 对象中的一个方法中,这个方法有一个恐怖的名字 scaffolding method:

    /** scaffolding code for collection field */
    public final void addOrder(Order order) {
        if (order == null) {
            throw new IllegalArgumentException("Can't add a null Order.");
        }

        order.setUser(this);
        this.getOrders().add(order);
    }

那么,如果在 User.hbm.xm 对于关系的映射没有指定  inverse="true" 会发生什么?

Hibernate 将发出两条不同的 SQL 做同一件事情,建立起关系,但反映在数据库中(见ERD),只是对同一个字段 orders.userId 这个外键进行操作。
通过指定 inverse="true", 我们告诉 hibernate 哪端是 owner 端,并将维护实体关系。而另一端是 inverse 端,不需要维护关系。

规则1,双向many-to-one 关系,one 端永远是 inverse 端,而many 端永远是 owner 端。

这下明白了 inverse 的含义了,它是相对于实体关系的 owner 来讲的。

Owner 怎么来的?

在这个例子中,User 是 one, Order 是 many, 因 Order 是 owner, 所以在 Orders 表中包含外键关系 userId. 这是 Owner 的来历-- 谁拥有外键字段,谁就是 Owner.

规则2, 双向 one-to-one 关系,决定取决于 ERD,而非 JAVA, 同样是哪个实体拥有外键,哪边就是 Owner, 相反,另一端(other side)就是inverse.

规则3,双向 many-to-many,因为实现这一关系的有效办法是利用独立关联表(join table), 所以我们不得不任意指定任一端为 owner(另外一端就是 inverse),但外键关系(复合外键)实际上是在独立的关联表中。


inverse 该放在哪边?

规则1,双向many-to-one 关系,one 端永远是 inverse 端,而many 端永远是 owner 端。

有这个规则,在映射关系时,one-to-many 是指定 one 端,即 inverse 端,因些应该指定 inverse 属性。
    <set cascade="all" inverse="true" name="orders">
      <key column="userId" not-null="true"/>
      <one-to-many class="Order"/>
    </set>

因此,many-to-one 是 owner 端,不应该指定 inverse 属性:
    <!-- owner side of this bidirectional one-to-many association (user to order)-->
    <many-to-one column="userId" foreign-key="fk_order_userId" name="user" not-null="true" />


因此完整的映射如下(fixed 错误的注释,请好好理解一下注释)
<hibernate-mapping package="org.pprun.hjpetstore.domain">
  <class name="Order" table="Orders">
    ...

     <!-- owner side of this bidirectional one-to-many association (user to order)-->
    <many-to-one column="userId" foreign-key="fk_order_userId" name="user" not-null="true" />

  </class>
</hibernate-mapping>

User.hbm.xm
<hibernate-mapping package="org.pprun.hjpetstore.domain">
  <class name="User" table="User">
   ...
    <!--
inverse="true" means Order is the owner side (User is the mirror side - inverse),
Order will take charge of relationship 'userId', which is the foreign-key in Order.
If only call user.getOrders().add(newOrder), no changes are made persistent.
But only call order.setUser(user) will make the relationship set - means the foreign-key will be persisted.
-->
    <set cascade="all" inverse="true" name="orders">
      <key column="userId" not-null="true"/>
      <one-to-many class="Order"/>
    </set>
  </class>
</hibernate-mapping>

2. Inverse和Cascade的比较

Inverse:负责控制关系,默认为false,也就是关系的两端都能控制,但这样会造成一些问题,更新的时候会因为两端都控制关系,于是重复更新。一般来说有一端要设为true。
Cascade:负责控制关联对象的级联操作,包括更新、删除等,也就是说对一个对象进行更新、删除时,其它对象也受影响,比如我删除一个对象,那么跟它是多对一关系的对象也全部被删除。
举例说明区别:删除“一”那一端一个对象O的时候,如果“多”的那一端的Inverse设为true,则把“多”的那一端所有与O相关联的对象外键清空;如果“多”的那一端的Cascade设为Delete,则把“多”的那一端所有与O相关联的对象全部删除。


3. 各个配置属性含义

< many-to-one name ="parent" class ="com.client.model.ClientChildType" update ="false" insert ="false" cascade ="none" > < column name ="parent_id" /> </ many-to-one >

update:进行update操作时是否包含此字段  
insert:进行insert操作时是否包含此字段  

name:映射类属性的名字  
column:关联的字段  
class:关联类的名字  
cascade:设置操作中的级联策略 可选值为 all所有操作情况均进行级联、none所有操作情况均不进行级联、save-update执行save和update操作时级联、delete执行删除操作时级联  
fetch:设置抓取数据的策略 默认值为select序列选择抓取 可选值为join外连接抓取  

property-ref:指定关联类的一个属性,这个属性将会和本类的外键相对应(当外键参照一键时需要指定改属性)

fetch属性

hibernate抓取策略(单端代理的批量抓取fetch=select(默认)/join)

测试用例:

Student student = (Student)session.get(Student.class, 1);
    System.out.println(student.getName());
    System.out.println(student.getClasses().getName());

1)保持默认,同fetch="select",如:
<many-to-one name="classes" column="classesid" fetch="select"/>

fetch="select",另外发送一条select语句抓取当前对象关联实体或集合

执行结果:2条语句

Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_, student0_.class_id as class3_1_0_ from student_join student0_ where student0_.id=?
学生1
Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from classes_join classes0_ where classes0_.id=?
高一(1)班

======================================

2)设置fetch="join",如:
<many-to-one name="classes" column="classesid" fetch="join"/>

fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合

此时lazy会失效

执行结果:一条join语句

Hibernate: select student0_.id as id1_1_, student0_.name as name1_1_, student0_.class_id as class3_1_1_, classes1_.id as id0_0_, classes1_.name as name0_0_ from student_join student0_ left outer join classes_join classes1_ on student0_.class_id=classes1_.id where student0_.id=?
学生1
高一(1)班

 

你可能感兴趣的:(Hibernate)