欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
25 | 經(jīng)典的 N+1 SQL 問(wèn)題如何正確解決?(上)

在 JPA 的使用過(guò)程中,N+1 SQL 是很常見(jiàn)的問(wèn)題,相信很多程序員都遇到過(guò)這一問(wèn)題,我看見(jiàn)很多同事處理起來(lái)束手無(wú)策,那么它究竟真的有那么麻煩嗎?這一講我會(huì )幫助你梳理思路,看看到底如何解決這個(gè)經(jīng)典問(wèn)題。

注:由于內容較多,我將這部分內容拆分成兩講,方便你學(xué)習。

什么是 N+1 SQL 問(wèn)題?

想要解決一個(gè)問(wèn)題,必須要知道它是什么、如何產(chǎn)生的,這樣才能有方法、有邏輯地去解決它。下面通過(guò)一個(gè)例子來(lái)看一下什么是 N+1 的 SQL 問(wèn)題。

假設一個(gè) UserInfo 實(shí)體對象和 Address 是一對多的關(guān)系,即一個(gè)用戶(hù)有多個(gè)地址,我們首先看一下一般實(shí)體里面的關(guān)聯(lián)關(guān)系會(huì )怎么寫(xiě)。兩個(gè)實(shí)體對象如下述代碼所示。

復制代碼
  1. //UserInfo實(shí)體對象如下:
  2. @Entity
  3. @Data
  4. @SuperBuilder
  5. @AllArgsConstructor
  6. @NoArgsConstructor
  7. @Table
  8. @ToString(exclude = "addressList")//exclued防止 toString打印日志的時(shí)候死循環(huán)
  9. public class UserInfo extends BaseEntity {
  10. private String name;
  11. private String telephone;
  12. // UserInfo實(shí)體對象的關(guān)聯(lián)關(guān)系由Address對象里面的userInfo字段維護,默認是lazy加載模式,為了方便演示fetch取EAGER模式。此處是一對多關(guān)聯(lián)關(guān)系
  13. @OneToMany(mappedBy = "userInfo",fetch = FetchType.EAGER)
  14. private List<Address> addressList;
  15. }
  16. //Address對象如下:
  17. @Entity
  18. @Table
  19. @Data
  20. @SuperBuilder
  21. @AllArgsConstructor
  22. @NoArgsConstructor
  23. @ToString(exclude = "userInfo")
  24. public class Address extends BaseEntity {
  25. private String city;
  26. //維護UserInfo和Address的外鍵關(guān)系,方便演示也采用EAGER模式;
  27. @ManyToOne(fetch = FetchType.EAGER)
  28. @JsonBackReference //此注解防止JSON死循環(huán)
  29. private UserInfo userInfo;
  30. }

其次,我們假設數據庫里面有三條 UserInfo 的數據,ID 分別為 3、6、9,如下圖所示。

其中,每個(gè) UserInfo 分別有兩條 Address 數據,也就是一共 6 條 Address 的數據,如下圖所示。

然后,我們請求通過(guò) UserInfoRepository 查詢(xún)所有的 UserInfo 信息,方法如下面這行代碼所示。

復制代碼
  1. userInfoRepository.findAll()

現在,我們的控制臺將會(huì )得到四個(gè) SQL,如下所示。

復制代碼
  1. org.hibernate.SQL :
  2. select userinfo0_.id as id1_1_,
  3. userinfo0_.create_time as create_t2_1_,
  4. userinfo0_.create_user_id as create_u3_1_,
  5. userinfo0_.last_modified_time as last_mod4_1_,
  6. userinfo0_.last_modified_user_id as last_mod5_1_,
  7. userinfo0_.version as version6_1_,
  8. userinfo0_.ages as ages7_1_,
  9. userinfo0_.email_address as email_ad8_1_,
  10. userinfo0_.last_name as last_nam9_1_,
  11. userinfo0_.name as name10_1_,
  12. userinfo0_.telephone as telepho11_1_
  13. from user_info userinfo0_ org.hibernate.SQL :
  14. select addresslis0_.user_info_id as user_inf8_0_0_,
  15. addresslis0_.id as id1_0_0_,
  16. addresslis0_.id as id1_0_1_,
  17. addresslis0_.create_time as create_t2_0_1_,
  18. addresslis0_.create_user_id as create_u3_0_1_,
  19. addresslis0_.last_modified_time as last_mod4_0_1_,
  20. addresslis0_.last_modified_user_id as last_mod5_0_1_,
  21. addresslis0_.version as version6_0_1_,
  22. addresslis0_.city as city7_0_1_,
  23. addresslis0_.user_info_id as user_inf8_0_1_
  24. from address addresslis0_
  25. where addresslis0_.user_info_id = ? org.hibernate.SQL :
  26. select addresslis0_.user_info_id as user_inf8_0_0_,
  27. addresslis0_.id as id1_0_0_,
  28. addresslis0_.id as id1_0_1_,
  29. addresslis0_.create_time as create_t2_0_1_,
  30. addresslis0_.create_user_id as create_u3_0_1_,
  31. addresslis0_.last_modified_time as last_mod4_0_1_,
  32. addresslis0_.last_modified_user_id as last_mod5_0_1_,
  33. addresslis0_.version as version6_0_1_,
  34. addresslis0_.city as city7_0_1_,
  35. addresslis0_.user_info_id as user_inf8_0_1_
  36. from address addresslis0_
  37. where addresslis0_.user_info_id = ? org.hibernate.SQL :
  38. select addresslis0_.user_info_id as user_inf8_0_0_,
  39. addresslis0_.id as id1_0_0_,
  40. addresslis0_.id as id1_0_1_,
  41. addresslis0_.create_time as create_t2_0_1_,
  42. addresslis0_.create_user_id as create_u3_0_1_,
  43. addresslis0_.last_modified_time as last_mod4_0_1_,
  44. addresslis0_.last_modified_user_id as last_mod5_0_1_,
  45. addresslis0_.version as version6_0_1_,
  46. addresslis0_.city as city7_0_1_,
  47. addresslis0_.user_info_id as user_inf8_0_1_
  48. from address addresslis0_
  49. where addresslis0_.user_info_id = ?

通過(guò) SQL 我們可以看得出來(lái),當取 UserInfo 的時(shí)候,有多少條 UserInfo 數據就會(huì )觸發(fā)多少條查詢(xún) Address 的 SQL。

那么所謂的 N+1 的 SQL,此時(shí) 1 代表的是一條 SQL 查詢(xún) UserInfo 信息;N 條 SQL 查詢(xún) Address 的信息。你可以想象一下,如果有 100 條 UserInfo 信息,可能會(huì )觸發(fā) 100 條查詢(xún) Address 的 SQL,性能多差呀。

很簡(jiǎn)單,這就是我們常說(shuō)的 N+1 SQL 問(wèn)題。我們這里使用的是 EAGER 模式,當使用 LAZY 的時(shí)候也是一樣的道理,只是生成 N 條 SQL 的時(shí)機是不一樣的。

上面我演示了 @OneToMany 的情況,那么我們再看一下 @ManyToOne 的情況。利用 AddressRepository 查詢(xún)所有的 Address 信息,方法如下面這行代碼所示。

復制代碼
  1. addressRepository.findAll();

這個(gè)時(shí)候我們再看一下控制臺,會(huì )產(chǎn)生如下 SQL。

復制代碼
  1. org.hibernate.SQL :
  2. select address0_.id as id1_0_,
  3. address0_.create_time as create_t2_0_,
  4. address0_.create_user_id as create_u3_0_,
  5. address0_.last_modified_time as last_mod4_0_,
  6. address0_.last_modified_user_id as last_mod5_0_,
  7. address0_.version as version6_0_,
  8. address0_.city as city7_0_,
  9. address0_.user_info_id as user_inf8_0_
  10. from address address0_
  11. org.hibernate.SQL :
  12. select userinfo0_.id as id1_1_0_,
  13. userinfo0_.create_time as create_t2_1_0_,
  14. userinfo0_.create_user_id as create_u3_1_0_,
  15. userinfo0_.last_modified_time as last_mod4_1_0_,
  16. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  17. userinfo0_.version as version6_1_0_,
  18. userinfo0_.ages as ages7_1_0_,
  19. userinfo0_.email_address as email_ad8_1_0_,
  20. userinfo0_.last_name as last_nam9_1_0_,
  21. userinfo0_.name as name10_1_0_,
  22. userinfo0_.telephone as telepho11_1_0_,
  23. addresslis1_.user_info_id as user_inf8_0_1_,
  24. addresslis1_.id as id1_0_1_,
  25. addresslis1_.id as id1_0_2_,
  26. addresslis1_.create_time as create_t2_0_2_,
  27. addresslis1_.create_user_id as create_u3_0_2_,
  28. addresslis1_.last_modified_time as last_mod4_0_2_,
  29. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  30. addresslis1_.version as version6_0_2_,
  31. addresslis1_.city as city7_0_2_,
  32. addresslis1_.user_info_id as user_inf8_0_2_
  33. from user_info userinfo0_
  34. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  35. where userinfo0_.id = ?
  36. org.hibernate.SQL :
  37. select userinfo0_.id as id1_1_0_,
  38. userinfo0_.create_time as create_t2_1_0_,
  39. userinfo0_.create_user_id as create_u3_1_0_,
  40. userinfo0_.last_modified_time as last_mod4_1_0_,
  41. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  42. userinfo0_.version as version6_1_0_,
  43. userinfo0_.ages as ages7_1_0_,
  44. userinfo0_.email_address as email_ad8_1_0_,
  45. userinfo0_.last_name as last_nam9_1_0_,
  46. userinfo0_.name as name10_1_0_,
  47. userinfo0_.telephone as telepho11_1_0_,
  48. addresslis1_.user_info_id as user_inf8_0_1_,
  49. addresslis1_.id as id1_0_1_,
  50. addresslis1_.id as id1_0_2_,
  51. addresslis1_.create_time as create_t2_0_2_,
  52. addresslis1_.create_user_id as create_u3_0_2_,
  53. addresslis1_.last_modified_time as last_mod4_0_2_,
  54. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  55. addresslis1_.version as version6_0_2_,
  56. addresslis1_.city as city7_0_2_,
  57. addresslis1_.user_info_id as user_inf8_0_2_
  58. from user_info userinfo0_
  59. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  60. where userinfo0_.id = ?
  61. org.hibernate.SQL :
  62. select userinfo0_.id as id1_1_0_,
  63. userinfo0_.create_time as create_t2_1_0_,
  64. userinfo0_.create_user_id as create_u3_1_0_,
  65. userinfo0_.last_modified_time as last_mod4_1_0_,
  66. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  67. userinfo0_.version as version6_1_0_,
  68. userinfo0_.ages as ages7_1_0_,
  69. userinfo0_.email_address as email_ad8_1_0_,
  70. userinfo0_.last_name as last_nam9_1_0_,
  71. userinfo0_.name as name10_1_0_,
  72. userinfo0_.telephone as telepho11_1_0_,
  73. addresslis1_.user_info_id as user_inf8_0_1_,
  74. addresslis1_.id as id1_0_1_,
  75. addresslis1_.id as id1_0_2_,
  76. addresslis1_.create_time as create_t2_0_2_,
  77. addresslis1_.create_user_id as create_u3_0_2_,
  78. addresslis1_.last_modified_time as last_mod4_0_2_,
  79. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  80. addresslis1_.version as version6_0_2_,
  81. addresslis1_.city as city7_0_2_,
  82. addresslis1_.user_info_id as user_inf8_0_2_
  83. from user_info userinfo0_
  84. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  85. where userinfo0_.id = ?

這里通過(guò) SQL 我們可以看得出來(lái),當取 Address 的時(shí)候,Address 里面有多少個(gè) user_info_id,就會(huì )觸發(fā)多少條查詢(xún) UserInfo 的 SQL。

那么所謂的 N+1 的 SQL,此時(shí) 1 就代表一條 SQL 查詢(xún) Address 信息;N 條 SQL 查詢(xún) UserInfo 的信息。同樣,你可以想象一下,如果我們有 100 條 Address 信息,分別有不同的 user_info_id 可能會(huì )觸發(fā) 100 條查詢(xún) UserInfo 的 SQL,性能依然很差。

這也是我們常說(shuō)的 N+1 SQL 問(wèn)題,我只是給你演示了 @OneToMany 和 @ManyToOne 的情況,@ManyToMany 和 @OneToOne 也是一樣的道理,都是當我們查詢(xún)主體信息時(shí)候,1 條 SQL 會(huì )衍生出來(lái)關(guān)聯(lián)關(guān)系的 N 條 SQL。

現在你認識了這個(gè)問(wèn)題,下一步該思考,怎么解決才更合理呢?有沒(méi)有什么辦法可以減少 SQL 條數呢?

減少 N+1 SQL 的條數

最容易想到,就是有沒(méi)有什么機制可以減少 N 對應的 SQL 條數呢?從原理分析會(huì )知道,不管是 LAZY 還是 EAGER 都是沒(méi)有用的,因為這兩個(gè)只是決定了 N 條 SQL 的觸發(fā)時(shí)機,而不能減少 SQL 的條數。

不知道你是否還記得在第 20 講(Spring JPA 中的 Hibernate 加載過(guò)程與配置項是怎么回事)中,我們介紹過(guò)的 Hibernate 的配置項有哪些,如果你回過(guò)頭去看,會(huì )發(fā)現有個(gè)配置可以改變每次批量取數據的大小。

hibernate.default_batch_fetch_size 配置

hibernate.default_batch_fetch_size 配置在 AvailableSettings.class 里面,指的是批量獲取數據的大小,默認是 -1,表示默認沒(méi)有匹配取數據。那么我們把這個(gè)值改成 20 看一下效果,只需要在 application.properties 里面增加如下配置即可。

復制代碼
  1. # 更改批量取數據的大小為20
  2. spring.jpa.properties.hibernate.default_batch_fetch_size= 20

在實(shí)體類(lèi)不發(fā)生任何改變的前提下,我們再執行如下兩個(gè)方法,分別看一下 SQL 的生成情況。

復制代碼
  1. userInfoRepository.findAll();

還是先查詢(xún)所有的 UserInfo 信息,看一下 SQL 的執行情況,代碼如下所示。

復制代碼
  1. org.hibernate.SQL :
  2. select userinfo0_.id as id1_1_,
  3. userinfo0_.create_time as create_t2_1_,
  4. userinfo0_.create_user_id as create_u3_1_,
  5. userinfo0_.last_modified_time as last_mod4_1_,
  6. userinfo0_.last_modified_user_id as last_mod5_1_,
  7. userinfo0_.version as version6_1_,
  8. userinfo0_.ages as ages7_1_,
  9. userinfo0_.email_address as email_ad8_1_,
  10. userinfo0_.last_name as last_nam9_1_,
  11. userinfo0_.name as name10_1_,
  12. userinfo0_.telephone as telepho11_1_
  13. from user_info userinfo0_ org.hibernate.SQL :
  14. select addresslis0_.user_info_id as user_inf8_0_1_,
  15. addresslis0_.id as id1_0_1_,
  16. addresslis0_.id as id1_0_0_,
  17. addresslis0_.create_time as create_t2_0_0_,
  18. addresslis0_.create_user_id as create_u3_0_0_,
  19. addresslis0_.last_modified_time as last_mod4_0_0_,
  20. addresslis0_.last_modified_user_id as last_mod5_0_0_,
  21. addresslis0_.version as version6_0_0_,
  22. addresslis0_.city as city7_0_0_,
  23. addresslis0_.user_info_id as user_inf8_0_0_
  24. from address addresslis0_
  25. where addresslis0_.user_info_id in (?, ?, ?)

我們可以看到 SQL 直接減少到兩條了,其中查詢(xún) Address 的地方查詢(xún)條件變成了 in(?,?,?)。

想象一下,如果我們有 20 條 UserInfo 信息,那么產(chǎn)生的 SQL 也是兩條,此時(shí)要比 20+1 條 SQL 性能高太多了。

接著(zhù)我們再執行另一個(gè)方法,看一下 @ManyToOne 的情況,代碼如下所示。

復制代碼
  1. addressRepository.findAll()

關(guān)于執行的 SQL 情況如下所示。

復制代碼
  1. 2020-11-29 23:11:27.381 DEBUG 30870 --- [nio-8087-exec-5] org.hibernate.SQL                        :
  2. select address0_.id as id1_0_,
  3. address0_.create_time as create_t2_0_,
  4. address0_.create_user_id as create_u3_0_,
  5. address0_.last_modified_time as last_mod4_0_,
  6. address0_.last_modified_user_id as last_mod5_0_,
  7. address0_.version as version6_0_,
  8. address0_.city as city7_0_,
  9. address0_.user_info_id as user_inf8_0_
  10. from address address0_
  11. 2020-11-29 23:11:27.383 DEBUG 30870 --- [nio-8087-exec-5] org.hibernate.SQL                        :
  12. select userinfo0_.id as id1_1_0_,
  13. userinfo0_.create_time as create_t2_1_0_,
  14. userinfo0_.create_user_id as create_u3_1_0_,
  15. userinfo0_.last_modified_time as last_mod4_1_0_,
  16. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  17. userinfo0_.version as version6_1_0_,
  18. userinfo0_.ages as ages7_1_0_,
  19. userinfo0_.email_address as email_ad8_1_0_,
  20. userinfo0_.last_name as last_nam9_1_0_,
  21. userinfo0_.name as name10_1_0_,
  22. userinfo0_.telephone as telepho11_1_0_,
  23. addresslis1_.user_info_id as user_inf8_0_1_,
  24. addresslis1_.id as id1_0_1_,
  25. addresslis1_.id as id1_0_2_,
  26. addresslis1_.create_time as create_t2_0_2_,
  27. addresslis1_.create_user_id as create_u3_0_2_,
  28. addresslis1_.last_modified_time as last_mod4_0_2_,
  29. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  30. addresslis1_.version as version6_0_2_,
  31. addresslis1_.city as city7_0_2_,
  32. addresslis1_.user_info_id as user_inf8_0_2_
  33. from user_info userinfo0_
  34. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  35. where userinfo0_.id in (?, ?, ?)

從代碼中可以看到,我們查詢(xún)所有的 Address 信息也只產(chǎn)生了 2 條 SQL;而當我們查詢(xún) UserInfo 的時(shí)候,SQL 最后的查詢(xún)條件也變成了 in (?,?,?),同樣的道理這樣也會(huì )提升不少性能。

而 hibernate.default_batch_fetch_size 的經(jīng)驗參考值,可以設置成 20、30、50、100 等,太高了也沒(méi)有意義。一個(gè)請求執行一次,產(chǎn)生的 SQL 數量為 3-5 條基本上都算合理情況,這樣通過(guò)設置 default_batch_fetch_size 就可以很好地避免大部分業(yè)務(wù)場(chǎng)景下的 N+1 條 SQL 的性能問(wèn)題了。

此時(shí)你還需要注意一點(diǎn)就是,在實(shí)際工作中,一定要知道我們一次操作會(huì )產(chǎn)生多少 SQL,有沒(méi)有預期之外的 SQL 參數,這是需要關(guān)注的重點(diǎn),這種情況可以利用我們之前說(shuō)過(guò)的如下配置來(lái)開(kāi)啟打印 SQL,請看代碼。

復制代碼
  1. ## 顯示sql的執行日志,如果開(kāi)了這個(gè),show_sql就可以不用了,show_sql沒(méi)有上下文,多線(xiàn)程情況下,分不清楚是誰(shuí)打印的,所有我推薦如下配置項:
  2. logging.level.org.hibernate.SQL=debug

但是這種配置也有個(gè)缺陷,就是只能全局配置,沒(méi)辦法針對不通過(guò)的實(shí)體管理關(guān)系配置不同的 Fetch Size 的值。

而與之類(lèi)似的 Hibernate 里面也提供了一個(gè)注解 @BatchSize 可以解決此問(wèn)題。

@BatchSize 注解

@BatchSize 注解是 Hibernate 提供的用來(lái)解決查詢(xún)關(guān)聯(lián)關(guān)系的批量處理大小,默認無(wú),可以配置在實(shí)體上,也可以配置在關(guān)聯(lián)關(guān)系上面。此注解里面只有一個(gè)屬性 size,用來(lái)指定關(guān)聯(lián)關(guān)系 LAZY 或者是 EAGER 一次性取數據的大小。

我們還是將上面的例子中的 UserInfo 實(shí)體做一下改造,在里面增加兩次 @BatchSize 注解,代碼如下所示。

復制代碼
  1. @Entity
  2. @Data
  3. @SuperBuilder
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. @Table
  7. @ToString(exclude = "addressList")
  8. @BatchSize(size = 2)//實(shí)體類(lèi)上加@BatchSize注解,用來(lái)設置當被關(guān)聯(lián)關(guān)系的時(shí)候一次查詢(xún)的大小,我們設置成2,方便演示Address關(guān)聯(lián)UserInfo的時(shí)候的效果
  9. public class UserInfo extends BaseEntity {
  10. private String name;
  11. private String telephone;
  12. @OneToMany(mappedBy = "userInfo",cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
  13. @BatchSize(size = 20)//關(guān)聯(lián)關(guān)系的屬性上加@BatchSize注解,用來(lái)設置當通過(guò)UserInfo加載Address的時(shí)候一次取數據的大小
  14. private List<Address> addressList;
  15. }

我們通過(guò)改造 UserInfo 實(shí)體,可以直接演示 @BatchSize 應用在實(shí)體類(lèi)和屬性字段上的效果,所以 Address 實(shí)體可以不做任何改變,hibernate.default_batch_fetch_size 還改成默認值 -1,我們再分別執行一下兩個(gè) findAll 方法,看一下效果。

第一種:查詢(xún)所有 UserInfo,代碼如下面這行所示。

復制代碼
  1. userInfoRepository.findAll()

我們看一下 SQL 控制臺。

復制代碼
  1.  org.hibernate.SQL :
  2. select userinfo0_.id as id1_1_,
  3. userinfo0_.create_time as create_t2_1_,
  4. userinfo0_.create_user_id as create_u3_1_,
  5. userinfo0_.last_modified_time as last_mod4_1_,
  6. userinfo0_.last_modified_user_id as last_mod5_1_,
  7. userinfo0_.version as version6_1_,
  8. userinfo0_.ages as ages7_1_,
  9. userinfo0_.email_address as email_ad8_1_,
  10. userinfo0_.last_name as last_nam9_1_,
  11. userinfo0_.name as name10_1_,
  12. userinfo0_.telephone as telepho11_1_
  13. from user_info userinfo0_ org.hibernate.SQL :
  14. select addresslis0_.user_info_id as user_inf8_0_1_,
  15. addresslis0_.id as id1_0_1_,
  16. addresslis0_.id as id1_0_0_,
  17. addresslis0_.create_time as create_t2_0_0_,
  18. addresslis0_.create_user_id as create_u3_0_0_,
  19. addresslis0_.last_modified_time as last_mod4_0_0_,
  20. addresslis0_.last_modified_user_id as last_mod5_0_0_,
  21. addresslis0_.version as version6_0_0_,
  22. addresslis0_.city as city7_0_0_,
  23. addresslis0_.user_info_id as user_inf8_0_0_
  24. from address addresslis0_
  25. where addresslis0_.user_info_id in (?, ?, ?)

和剛才設置 hibernate.default_batch_fetch_size=20 的效果一模一樣,所以我們可以利用 @BatchSize 這個(gè)注解針對不同的關(guān)聯(lián)關(guān)系,配置不同的大小,從而提升 N+1 SQL 的性能。

第二種:查詢(xún)一下所有 Address,如下面這行代碼所示。

復制代碼
  1. addressRepository.findAll();

我們看一下控制臺的 SQL 情況,如下所示。

復制代碼
  1. org.hibernate.SQL :
  2. select address0_.id as id1_0_,
  3. address0_.create_time as create_t2_0_,
  4. address0_.create_user_id as create_u3_0_,
  5. address0_.last_modified_time as last_mod4_0_,
  6. address0_.last_modified_user_id as last_mod5_0_,
  7. address0_.version as version6_0_,
  8. address0_.city as city7_0_,
  9. address0_.user_info_id as user_inf8_0_
  10. from address address0_
  11. org.hibernate.SQL :
  12. select userinfo0_.id as id1_1_0_,
  13. userinfo0_.create_time as create_t2_1_0_,
  14. userinfo0_.create_user_id as create_u3_1_0_,
  15. userinfo0_.last_modified_time as last_mod4_1_0_,
  16. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  17. userinfo0_.version as version6_1_0_,
  18. userinfo0_.ages as ages7_1_0_,
  19. userinfo0_.email_address as email_ad8_1_0_,
  20. userinfo0_.last_name as last_nam9_1_0_,
  21. userinfo0_.name as name10_1_0_,
  22. userinfo0_.telephone as telepho11_1_0_,
  23. addresslis1_.user_info_id as user_inf8_0_1_,
  24. addresslis1_.id as id1_0_1_,
  25. addresslis1_.id as id1_0_2_,
  26. addresslis1_.create_time as create_t2_0_2_,
  27. addresslis1_.create_user_id as create_u3_0_2_,
  28. addresslis1_.last_modified_time as last_mod4_0_2_,
  29. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  30. addresslis1_.version as version6_0_2_,
  31. addresslis1_.city as city7_0_2_,
  32. addresslis1_.user_info_id as user_inf8_0_2_
  33. from user_info userinfo0_
  34. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  35. where userinfo0_.id in (?, ?)
  36. org.hibernate.SQL :
  37. select userinfo0_.id as id1_1_0_,
  38. userinfo0_.create_time as create_t2_1_0_,
  39. userinfo0_.create_user_id as create_u3_1_0_,
  40. userinfo0_.last_modified_time as last_mod4_1_0_,
  41. userinfo0_.last_modified_user_id as last_mod5_1_0_,
  42. userinfo0_.version as version6_1_0_,
  43. userinfo0_.ages as ages7_1_0_,
  44. userinfo0_.email_address as email_ad8_1_0_,
  45. userinfo0_.last_name as last_nam9_1_0_,
  46. userinfo0_.name as name10_1_0_,
  47. userinfo0_.telephone as telepho11_1_0_,
  48. addresslis1_.user_info_id as user_inf8_0_1_,
  49. addresslis1_.id as id1_0_1_,
  50. addresslis1_.id as id1_0_2_,
  51. addresslis1_.create_time as create_t2_0_2_,
  52. addresslis1_.create_user_id as create_u3_0_2_,
  53. addresslis1_.last_modified_time as last_mod4_0_2_,
  54. addresslis1_.last_modified_user_id as last_mod5_0_2_,
  55. addresslis1_.version as version6_0_2_,
  56. addresslis1_.city as city7_0_2_,
  57. addresslis1_.user_info_id as user_inf8_0_2_
  58. from user_info userinfo0_
  59. left outer join address addresslis1_ on userinfo0_.id = addresslis1_.user_info_id
  60. where userinfo0_.id = ?

這里可以看到,由于我們在 UserInfo 的實(shí)體上設置了 @BatchSize(size = 2),表示所有關(guān)聯(lián)關(guān)系到 UserInfo 的時(shí)候一次取兩條數據,所以就會(huì )發(fā)現這次我查詢(xún) Address 加載 UserInfo 的時(shí)候,產(chǎn)生了 3 條 SQL。

其中通過(guò)關(guān)聯(lián)關(guān)系查詢(xún) UserInfo 產(chǎn)生了 2 條 SQL,由于我們 UserInfo 在數據庫里面有三條數據,所以第一條 UserInfo 的 SQL 受 @BatchSize(size = 2) 控制,從而 in (?,?) 只支持了兩個(gè)參數,同時(shí)也產(chǎn)生了第二條查 UserInfo 的 SQL。

從上面的例子中我們可以看到 @BatchSize 和 hibernate.default_batch_fetch_size 的效果是一樣的,只不過(guò)一個(gè)是全局配置、一個(gè)是局部設置,這是可以減少 N+1 SQL 最直接、最方便的兩種方式。

注意事項:

@BatchSize 的使用具有局限性,不能作用于 @ManyToOne 和 @OneToOne 的關(guān)聯(lián)關(guān)系上,那樣代碼是不起作用的,如下所示。

復制代碼
  1. public class Address extends BaseEntity {
  2. private String city;
  3. @ManyToOne(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
  4. @BatchSize(size = 30) //由于是@ManyToOne的關(guān)聯(lián)關(guān)系所有沒(méi)有作用
  5. private UserInfo userInfo;
  6. }

因此,你要注意 @BatchSize 只能作用在 @ManyToMany、@OneToMany、實(shí)體類(lèi)這三個(gè)地方。
此外,Hibernate 中還提供了一種 FetchMode 的策略,包含三種模式,分別為 FetchMode.SELECT、FetchMode.JOIN,以及 FetchMode.Subselect。由于內容較多,我怕你一次性不好消化,所以會(huì )在下一講繼續為你介紹。到時(shí)見(jiàn)。

點(diǎn)擊下方鏈接查看源碼(不定時(shí)更新)
https://github.com/zhangzhenhuajack/spring-boot-guide/tree/master/spring-data/spring-data-jpa

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
hibernate 自增 IDENTITY
oracle建立自動(dòng)增長(cháng)字段
學(xué)習Java6(六) ---嵌入式數據庫 Derby
(轉)hibernate常用API詳解
iBatis 到 MyBatis區別
ibatis 到 MyBatis區別
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久