如何在使用Open-Session in View時避免效能損失?
Open-Session In View會在你即使沒有使用惰性實體情況下載入且初始化並獲取它們,這會導致嚴重的效能損失。
Open-Session in View 反模式在Spring Boot中預設是啟用的。如果您更喜歡使用它,那麼需要嘗試儘可能減輕效能損失:一種優化是將標記Connection設定為只讀,這將允許資料庫伺服器避免寫入事務日誌;另一個優化包括在您不希望某些實體被懶惰地初始化時,顯式明確設定實體。
1. application.properties配置:
spring.datasource.url=jdbc:mysql:<font><i>//localhost:3306/db_tennis?createDatabaseIfNotExist=true</i></font><font> spring.datasource.username=root spring.datasource.password=root spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=<b>true</b> spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect spring.datasource.initialization-mode=always spring.datasource.platform=mysql # <b>this</b> is <b>default</b> anyway spring.jpa.open-in-view=<b>true</b> </font>
2. 父實體Tournament ,注意setXXX方法:
@Entity <b>public</b> <b>class</b> Tournament implements Serializable { <b>private</b> <b>static</b> <b>final</b> <b>long</b> serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) <b>private</b> Long id; <b>private</b> String name; @OneToMany(cascade = CascadeType.ALL, mappedBy = <font>"tournament"</font><font>, orphanRemoval = <b>true</b>) @JsonManagedReference <b>private</b> List<TennisPlayer> tennisPlayers = <b>new</b> ArrayList<>(); </font><font><i>//注意這裡,手工明確設定子實體集合</i></font><font> <b>public</b> <b>void</b> setTennisPlayers(List<TennisPlayer> tennisPlayers) { <b>this</b>.tennisPlayers = tennisPlayers; } } </font>
子實體TennisPlayer :
@Entity <b>public</b> <b>class</b> TennisPlayer implements Serializable { <b>private</b> <b>static</b> <b>final</b> <b>long</b> serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) <b>private</b> Long id; <b>private</b> String name; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = <font>"tournament_id"</font><font>) @JsonBackReference <b>private</b> Tournament tournament; } </font>
3. 獲取實體並顯式設定惰性屬性,通過呼叫tournament.setTennisPlayers,可以在服務或控制器層中執行此操作,具體取決於它更適合您的情況,但在顯式事務之外:
@RequestMapping(<font>"/tournament_no_players"</font><font>) <b>public</b> Tournament tournamentWithoutPlayers() { Tournament tournament = tennisService.fetchTournament(); </font><font><i>// 顯式明確設定實體:explicitly set Players of the Tournament</i></font><font> </font><font><i>// 為了避免從資料庫中獲取它們</i></font><font> tournament.setTennisPlayers(Collections.emptyList()); <b>return</b> tournament; } </font>
這為什麼有效?為什麼我們可以設定託管實體的屬性而不觸發重新整理?那麼,答案可以在其文件中找到OpenSessionInViewFilter :
注意:預設情況下,此過濾器不會重新整理Hibernate會話,重新整理模式設定為FlushMode.NEVER。它假定與關注重新整理的服務層事務結合使用:活動事務管理器將臨時更改重新整理在讀寫事務期間模式為FlushMode.AUTO,使用重新整理模式,在每個事務結束時重置為FlushMode.NEVER。如果您打算在沒有事務的情況下使用此過濾器,請考慮更改預設重新整理模式(flushMode屬性)
下圖上面是通過預設OSIV強制載入,結果執行了兩條SQL語句,它將子實體集合也載入了。下圖是使用上面方式之後, )