spring-data-jpa 入門二:常用技術使用之關聯關係查詢配置

系列文章:
spring-data-jpa 入門
spring-data-jpa 入門三:常用技術使用之複雜查詢

在上文中我們介紹了spring-data-jpa簡單的實現原理、簡單的增刪改查、以及簡單的分頁查找、排序。基本上通過上文介紹,我們可以寫一些簡單的增刪改查了!但是日常開發中肯定不是僅僅單表查詢。那麼我們將繼續探討spring-data-jpa一些其他使用放法:

  • 常用技術使用
    • 多表關聯關係查詢
    • 原生sql查詢
    • 動態sql(兩種方式:Criteria、繼承JpaSpecificationExecutor)
    • 多表多條件複雜查詢
    • 動態條件查詢(複雜條件 in、join 等)
  • 批量操作、EntityManager狀態分析
  • 常用註解總結

多表關聯關係查詢

說道orm框架基本上離不開多表關聯關係查詢,例如:一對一,一對多,多對多。無論在mybatis或者hibernate中都是支持註解或者配置查詢的,jpa也不例外,本文我們將着重說下spring-data-jpa關聯關係查詢,已經我在配置中碰到的一些坑。


oneToOne雙向查詢

有兩張表 user、user_details,用戶信息表和用戶詳情表是一對一的關係,用戶詳情表關聯了用戶信息表的ID,並配置了外鍵,具體表結構如下:

CREATE TABLE `user_info` (
  `id` int(11) NOT NULL auto_increment,
  `uname` varchar(20) default NULL,
  `unumber` varchar(20) default NULL,
  `password` varchar(255) default NULL,
  `address` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `user_details` (
  `id` int(11) NOT NULL auto_increment,
  `user_id` int(11) NOT NULL,
  `email` varchar(50) default NULL,
  `address` varchar(255) default NULL,
  PRIMARY KEY  (`id`),
  KEY `FKbp1gkvqqbmw0a9j4w53c9q5gm` (`user_id`),
  CONSTRAINT `FKbp1gkvqqbmw0a9j4w53c9q5gm` FOREIGN KEY (`user_id`) REFERENCES `user_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶詳情表';

Entity層
爲了節省篇幅我就不把全部代碼貼進來了,只貼關鍵代碼
UserDetails中:

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private UserInfo userInfo;

在UserDetails實體中除了正常的結構屬性外,我們添加了一個UserInfo對象,並使用了註解@OneToOne,其中cascade(串聯、級聯)配置了級聯屬性爲所有,當然對應的還有保存、更新、刪除、刷新、分離等

package javax.persistence;

public enum CascadeType {
    ALL,
    PERSIST,
    MERGE,
    REMOVE,
    REFRESH,
    DETACH;

    private CascadeType() {
    }
}

@JoinColumn 設置關聯對應的字段,referencedColumnName 是設置關聯表的具體某個字段,如果關聯字段外鍵爲主鍵的話這個屬性可以忽略,整體這句話就是在說根據UserDetails中的user_id(外鍵),是與UserInfo 中的id(主鍵)是一一對應的。
UserInfo中:

    @JsonIgnoreProperties(value = "userInfo")//避免遞歸死循環
    @OneToOne(mappedBy = "userInfo", cascade = CascadeType.ALL)
    private UserDetails userDetails;

@OneToOne不用多說,mappedBy 這個屬性就是表示該實體是在整個一對一關係中屬於被維護端,想對應的userDetails是處於維護端(維護一對一的關係)。
@JsonIgnoreProperties(value = “userInfo”) 這個註解就是在一對一查詢的時候出現遞歸死循環的,例如 我查詢用戶信息時候會自動加載用戶詳情信息,但是尷尬的是用戶詳情信息中也有用戶信息,如此這般就成了遞歸死循環,報錯StackOverflowError。

到這一對一關係配置就搞定了,查詢用戶信息的時候會執行查詢對應的用戶詳情信息,我們配置的是雙向查詢,如果要單項的將對應屬性註釋掉就行。

OneToMany/ManyToOne雙向

一對多和多對一關係演示我們用 用戶(作者)、文章。一個作者可以發表多篇文章,一篇文章只能有一個作者。
用戶表結構和一對一的用戶信息表一樣,文章表結構如下

CREATE TABLE `article` (
  `id` int(11) NOT NULL auto_increment,
  `user_id` int(11) default NULL,
  `context` longtext,
  `create_time` datetime default NULL,
  `update_time` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `FKgfkys9w7qv3xcubq0drrayuu3` (`user_id`),
  CONSTRAINT `fore_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章信息表';

ArticleInfo 實體代碼關鍵配置如下:

     @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}, 
                 optional = false,fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private UserInfo userInfo;

@ManyToOne 表示這是個多對一的關係,cascade對應的級聯操作,optional(任選)表示是否接受爲空,false不接受。fetch( 取來) 加載模式,可選的有懶加載、立即加載。配置懶加載之後會有一定問題,需要另行配置,方案大概有四種,我選擇的是在web.xml中配置過濾器。

<!-- 解決懶加載的問題,該過濾器放置到SpringMVC的過濾器的前面 -->
    <filter>
        <filter-name>OpenEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>OpenEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

具體原因以及解決方法後面在解釋。

@JoinColumn 同一對一解釋。

對應的UserInfo實體中也需要添加配置:

    @JsonIgnoreProperties(value = "userInfo")//避免遞歸死循環
    @OneToMany(mappedBy = "userInfo", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<ArticleInfo> articleInfoList;

注意了因爲用戶是一 所以用@OneToMany註解,統同樣的mappedBy表示在一對多的關係中,它是關係被維護着。

這樣一對多雙向關聯就配置完畢,查詢會將對應關係的實體也查出來。

ManyToMany

多對多的關係的演示選擇的是 用戶、權限。 一個用戶可用有多個權限,一個權限對應多個用戶。
用戶表結構並沒有變,權限表結構如下:

CREATE TABLE `role` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(25) NOT NULL,
  `create_time` datetime default NULL,
  `update_time` datetime default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='權限表';

可用看出權限表並沒有與用戶表有任何關聯的地方,這是因爲在JPA中規定@ManyToMany表示多對多的關係,維護與被維護關係給誰都行,誰是主表誰的表名在前,並且使用一個第三方的關聯表來維護。關聯關係表不需要我們建立,只需要使用@JoinTabl註解,jpa會自動根據規則進行關聯表的生成。
關聯關係的規則
表名默認是:主表名+下劃線+從表名。(主表是指關係維護端對應的表,從表指關係被維護端對應的表)。這個關聯表只有兩個外鍵字段,分別指向主表ID和從表ID。
字段的名稱默認爲:主表名+下劃線+主表中的主鍵列名,從表名+下劃線+從表中的主鍵列名

對應用戶實體實體配置:

    @JsonIgnoreProperties(value = "userInfoList")//避免遞歸死循環
    @ManyToMany
    @JoinTable(name = "user_info_role",
            joinColumns = {@JoinColumn(name = "user_info_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roleList;

@JsonIgnorePropertie 避免遞歸死循環
@ManyToMany 表示的是多對多的關係
@JoinTable 就是上面我們說的生成第三方關聯關係表,其中name就是表名,joinColumns、inverseJoinColumns都是配置對應的列名,這個關聯關係表中只有兩列(上面生成規則說明)。

對應的權限實體關鍵配置如下:

     @ManyToMany(mappedBy = "roleList")
     private List<UserInfo> userInfoList;

這個配置就更簡單了,到這一對多配置就搞定了,這裏需要注意的是:

  1. 可以隨意指定一方爲關係維護端
  2. 多對多關係中一般不設置級聯保存、級聯刪除、級聯更新等操作
  3. 多對多的綁定關係、或者說第三方關聯關係表中的數據是由維護的來寫入的
  4. 解除關係僅僅只需要由維護端刪除即可,第三方關聯關係表中數據會自動刪除
  5. 如果關聯關係還在,是不能直接刪除被維護端的

其實jpa本身就是開發方便,所有在這種關聯關係配置中,非常簡單。當然,中間在配置懶加載時候還是出現了點問題的。

到此,我們通過兩篇文章的說明了解到spring-data-jpa是什麼、簡單的實現原理、簡單的增刪改查、以及簡單的分頁查找、排序、常用技術中的關聯關係配置(一對一、一對多、多對多),下文我們將繼續探討:

  • 常用技術
    • 原生sql查詢
    • 動態sql(兩種方式:Criteria、繼承JpaSpecificationExecutor)
    • 多表多條件複雜查詢
    • 動態條件查詢(複雜條件 in、join 等)
  • 批量操作、EntityManager狀態分析
  • 常用註解總結
  • json解析時延遲加載問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章