1. 程式人生 > >JPA數據懶加載LAZY和實時加載EAGER(轉)

JPA數據懶加載LAZY和實時加載EAGER(轉)

return ram entity 主從 onetomany sele final sel 自己

原文:https://www.cnblogs.com/MrSi/p/8081811.html

懶加載LAZY和實時加載EAGER的概念,在各種開發語言中都有廣泛應用。其目的是實現關聯數據的選擇性加載,懶加載是在屬性被引用時,才生成查詢語句,抽取相關聯數據。而實時加載則是執行完主查詢後,不管是否被引用,立馬執行後續的關聯數據查詢。社區裏有人認為懶加載這種功能比較雞肋,這種事仁者見仁,智者見智啦,個人覺得依自己業務場景而定。

順帶說一句,使用懶加載來調用關聯數據,必須要保證主查詢session(數據庫連接會話)的生命周期沒有結束,否則,你是無法抽取到數據的。在Spring Data JPA中如何自己控制session的生命周期,這個功能我還沒有研究,作為下一次的研究課題。

在今天解析懶加載與實時加載的實例時,需要借助實體關系映射功能,配置實體間的一對多(OneToMany)和多對一(ManyToOne)關系。當然,還有OneToOne、ManyToMany關系,這個如有興趣大家就自己研究下了。

一、為實體類ProcessBlock和Node添加一對多的關聯關系。在我這個示例中ProcessBlock是流程定義類,一個流程定義對應多個流程節點,即Node表。下面給出代碼。  

   1、ProcessBlock實體定義

@Entity(name = "nbpm_processblock")
@NamedQuery(name = "ProcessBlock.findByName", query = "select p from nbpm_processblock p where p.name=?1")
public class ProcessBlock implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "id")
    long id;
    @Column(name = "name")
    String name;
    @Column(name = "description")
    String description;
    @OneToMany(mappedBy
="processblock",cascade=CascadeType.ALL,fetch = FetchType.EAGER) Set<Node> nodeSet = new HashSet<Node>(); public long getId() { return id; }
……

  定義Set<Node>屬性 nodeSet,為其添加註解@OneToMany,意思就是一個ProcessBlock實體對應多個Node節點。註解屬性 mappedBy,是指關聯關系可以從Node類中的processblock屬性上獲取;cascade屬性設置成CascadeType.ALL,是說主從表全面建立級聯關系;fetch屬性設置成FetchType.EAGER,是指加載規則是即時加載。查看源碼會發現默認情況下,OneToMany註解的fetch屬性設置的是FetchType.LAZY。

  2、Node實體定義

@Entity(name = "nbpm_node")
public class Node implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "id")
    long id;
    @Column(name = "name")
    String name;
    @Column(name = "description")
    String description;
    @Column(name = "subclass")
    String subclass;
    /**
     * 關聯關系必須存在
     */
    @ManyToOne(optional = true)
    @JoinColumn(name="processblock")
    ProcessBlock processblock;
    public long getId() {
        return id;
    }   
……

   定義ProcessBlock屬性 processblock,為其添加註解@ManyToOne,意思就是一個Node實體對應一個ProcessBlock流程定義。添加註解@JoinColumn,屬性name設置成"processblock",意思是nbpm_node表中的processblock字段是外鍵,與ProcessBlock的主鍵建議關聯關系。需要說明一下 ManyToOne註解的fetch屬性 默認是FetchType.EAGER,查詢時會立馬抽取對應的主表數據。這塊的考慮還是很細致的,大致就是關聯數據是集合時就懶加載,是單條數據時就實時加載。

 二、編寫查詢示例並展示結果。查詢使用自定義的JpaUtil工具,工具實現請參考《自定義JpaUtil,快速完成Hql執行邏輯(一)》

  1、查詢ProcessBlock,主幹流程數據,因為設置的nodeSet屬性是實時加載加載,查詢完成,斷點查看確實拿到了流程對應的所有節點信息。

Map<String, Object> params = new HashMap<>();
        params.put("name", "主幹流程");
        List<ProcessBlock> list = ApplicationContextUtil.instance.getJpaUtil().list(
                "select u from simm.spring.entity.ProcessBlock u where u.name=:name", params, ProcessBlock.class);
        
        Set<Node> nodes = list.get(0).getNodeSet();

  技術分享圖片

-- Hibernate框架生成的查詢語句 -----------------
Hibernate: select processblo0_.id as id1_2_, processblo0_.description as descript2_2_, processblo0_.name as name3_2_ from nbpm_processblock processblo0_ where processblo0_.name=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_, 
                  nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_ 
           from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_, 
                  nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_ 
           from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_, 
                  nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_ 
           from nbpm_node nodeset0_ where nodeset0_.processblock=?
Hibernate: select nodeset0_.processblock as processb5_1_0_, nodeset0_.id as id1_1_0_, nodeset0_.id as id1_1_1_, nodeset0_.description as descript2_1_1_, nodeset0_.name as name3_1_1_, 
                  nodeset0_.processblock as processb5_1_1_, nodeset0_.subclass as subclass4_1_1_ 
           from nbpm_node nodeset0_ where nodeset0_.processblock=?

  2、將ProcessBlock類的屬性nodeSet設置成懶加載LAZY,查詢後,發現對應的節點列表信息獲取不到了。這裏懶加載的數據讀取不到是因為主查詢的session已經結束。

@OneToMany(mappedBy="processblock",cascade=CascadeType.ALL,fetch = FetchType.LAZY)
    //@JoinColumn(name="processblock_id")
    Set<Node> nodeSet = new HashSet<Node>();

   技術分享圖片

-- Hibernate框架生成的查詢語句 -----------------
Hibernate: select processblo0_.id as id1_2_, processblo0_.description as descript2_2_, processblo0_.name as name3_2_ from nbpm_processblock processblo0_ where processblo0_.name=?

  3、Hql直接查詢關聯屬性

List<Node> nodeList = ApplicationContextUtil.instance.getJpaUtil().list(
                "select u.nodeSet from simm.spring.entity.ProcessBlock u where u.id=1", null, Node.class);
-- Hibernate框架生成的查詢語句 -----------------
Hibernate: select nodeset1_.id as id1_1_, nodeset1_.description as descript2_1_, nodeset1_.name as name3_1_, nodeset1_.processblock as processb5_1_, nodeset1_.subclass as subclass4_1_ 
           nbpm_processblock processblo0_ inner join nbpm_node nodeset1_ on processblo0_.id=nodeset1_.processblock where processblo0_.id=1
Hibernate: select processblo0_.id as id1_2_0_, processblo0_.description as descript2_2_0_, processblo0_.name as name3_2_0_ 
           from nbpm_processblock processblo0_ where processblo0_.id=?

JPA數據懶加載LAZY和實時加載EAGER(轉)