Hibernate複雜關係級聯操作

1. 一對一 @OneToOne

@Target({METHOD, FIELD}) 
@Retention(RUNTIME)
public @interface OneToOne {

    /** 
     * (Optional) The entity class that is the target of 
     * the association. 
     *
     * <p> Defaults to the type of the field or property 
     * that stores the association. 
     */
    Class targetEntity() default void.class;

    /**
     * (Optional) The operations that must be cascaded to 
     * the target of the association.
     *
     * <p> By default no operations are cascaded.
     */
    CascadeType[] cascade() default {};

    /** 
     * (Optional) Whether the association should be lazily 
     * loaded or must be eagerly fetched. The EAGER 
     * strategy is a requirement on the persistence provider runtime that 
     * the associated entity must be eagerly fetched. The LAZY 
     * strategy is a hint to the persistence provider runtime.
     */
    FetchType fetch() default EAGER;

    /** 
     * (Optional) Whether the association is optional. If set 
     * to false then a non-null relationship must always exist.
     */
    boolean optional() default true;

    /** (Optional) The field that owns the relationship. This 
      * element is only specified on the inverse (non-owning) 
      * side of the association.
     */
    String mappedBy() default "";


    /**
     * (Optional) Whether to apply the remove operation to entities that have
     * been removed from the relationship and to cascade the remove operation to
     * those entities.
     * @since 2.0
     */
    boolean orphanRemoval() default false;
}

2. 單向一對多(一般不用)

一般不使用單向一對多,會產生額外update語句,如果需要就使用雙向一對多
雙向一對多雙向多對一是一個意思

<hibernate-mapping package="cn.itsource.hibernate.day2.manytoone"> 
	<class name="ProductDir">
		<id name="id"> 
			<generator class="native" />
		</id> 
		<property name="name" /> 
		<!-- 一個產品類型包含多個產品 -->
		<!-- private Set<Product> products = new HashSet<Product>(); -->
		<set name="products">
			<!-- 在一方ProductDir配置外鍵的列名,自動跑到多方Product的表中 -->
			<key column="dir_id"/>
			<one-to-many class="Product"/>
		</set>
	</class>

	<class name="Product">
		<id name="id">
			<generator class="native" />
		</id>
		<property name="name" />
	</class>
</hibernate-mapping>

set說明

name: java屬性
column: 外鍵名稱
class: 多方的類名

2.1. 保存

必定會產生額外update語句,因爲是一方維護外鍵值

// 一方
ProductDir dir = new ProductDir();
dir.setName("類型1");
// 多方
Product product = new Product();
product.setName("產品1");
Product product2 = new Product();
product2.setName("產品2");

// 只能由一方建立到多方的關係(設置外鍵)
dir.getProducts().add(product);
dir.getProducts().add(product2);

Session session = HibernateUtils.getSession();
session.beginTransaction();

session.save(product);
session.save(product2);
session.save(dir);

session.getTransaction().commit();
session.close();

2.2. 查詢

先獲取一方再獲取多方
默認是延遲加載多方

2.3. 坑

dir.getProducts() 不會爲 null

ProductDir dir = (ProductDir) session.get(ProductDir.class, 1L); 
System.out.println(dir); 36. System.out.println(dir.getProducts().getClass()); 
if (dir.getProducts() == null) {
	System.out.println("當前產品類型沒有產品"); 
} else {
	System.out.println("當前產品類型有產品"); 
}

原因:只會發一條sql查詢productDir,不會查詢product表,所以dir.getProducts() 永遠不爲null
正確用法:會發兩條sql

if (dir.getProducts().size() > 0) {

2.4. @OneToMany

3. 單向多對一

<hibernate-mapping package="cn.itsource.hibernate.day2.manytoone"> 
	<class name="ProductDir">
		<id name="id"> 
			<generator class="native" />
		</id> 
		<property name="name" /> 
	</class>

	<class name="Product">
		<id name="id">
			<generator class="native" />
		</id>
		<property name="name" />
		<!-- 多個Product屬於一個ProductDir,外鍵在那個表,這個表就是多 -->
		<!-- private ProductDir dir; -->
		<many-to-one name="dir" class="ProductDir" column="dir_id" lazy="true"/>
	</class>
</hibernate-mapping>

many-to-one說明

name: java的屬性
class: 唯一一個地方可以不配置class,反射獲取
column: 外鍵的列名,可以不配置,默認使用name屬性值
lazy: 延遲加載,爲false時立即加載

3.1. 保存

先保存一方,再保存多方,否則出現多個update語句

// 一方
ProductDir dir = new ProductDir();
dir.setName("類型1");
// 多方
Product product = new Product(); 
product.setName("產品1"); 
Product product2 = new Product();
product2.setName("產品2");
// 設置外鍵,多方到一方的關係
product.setDir(dir);
product2.setDir(dir);

Session session = HibernateUtils.getSession(); session.beginTransaction();
session.save(dir);
session.save(product);
session.save(product2); 
session.getTransaction().commit();
session.close();

先保存多方,再保存一行(錯誤用法),commit提交時發現多方出現髒數據,會發出額外的update(髒數據更新)

session.save(product);
session.save(product2); 
session.save(dir);

3.2. 查詢

先獲取多方再獲取一方
默認是延遲加載一方

3.3. 報錯

org.hibernate.LazyInitializationException
延遲加載異常
原因:提前關閉了session

session.close();
System.out.println(product.getDir());

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.i tsource.hibernate.day2.manytoone.ProductDir
保存了一個臨時(瞬時)對象
原因:在多方實例化了一方,因爲實例化之後一方沒有主鍵,也就是多方沒有外鍵值,保存會出錯

public class Product 
private Long id; 
private String name; 
//錯誤用法,hibernate處理時認爲有id,但是本質沒id
private ProductDir dir = new ProductDir();

//ProductDir dir = new ProductDir()建立了關聯,所以註銷下面2行
//product.setDir(dir);
//product2.setDir(dir);

session.save(product);
session.save(product2);

正確用法

private ProductDir dir;

3.4 @ManyToOne

4. 單向多對多

@ManyToMany

5. 雙向多對多

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章