2016-12-01 36 views
3

我有字符串ID的實體:它N + 1時ID是串(JpaRepository)

@Table 
@Entity 
public class Stock { 

    @Id 
    @Column(nullable = false, length = 64) 
    private String index; 

    @Column(nullable = false) 
    private Integer price; 

} 

而且JpaRepository:

public interface StockRepository extends JpaRepository<Stock, String> { 
} 

當我打電話stockRepository::findAll,我有N + 1問題:

日誌簡化

select s.index,s.price from stock s
select s.index,s.price from stock s where where s.index =?

引用的最後一行調用大約5K次(表的大小)。另外,當我更新價格時,我在下一步:

stockRepository.save(listOfStocksWithUpdatedPrices); 

在日誌中我有N插入。
當id是數字時,我還沒有看到類似的行爲。
P.S.在我的情況下,將ID的類型設置爲數字不是最好的解決方案。

UPDATE1:
我忘了,更不用說還存在有許多對許多關係與StockTrade類:

@Table 
@Entity 
public class Trade { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Integer id; 

    @Column 
    @Enumerated(EnumType.STRING) 
    private TradeType type; 

    @Column 
    @Enumerated(EnumType.STRING) 
    private TradeState state; 

    @MapKey(name = "index") 
    @ManyToMany(fetch = FetchType.EAGER) 
    @JoinTable(name = "trade_stock", 
     joinColumns = { @JoinColumn(name = "id", referencedColumnName = "id") }, 
     inverseJoinColumns = { @JoinColumn(name = "stock_index", referencedColumnName = "index") }) 
    private Map<String, Stock> stocks = new HashMap<>(); 

} 

UPDATE2:
我加入了很多對 - 關係爲Stock方:

@ManyToMany(cascade = CascadeType.ALL, mappedBy = "stocks") //lazy by default 
Set<Trade> trades = new HashSet<>(); 

但現在它離開了交易(但他們很懶),以及所有交易的收藏(他們也很懶)。但是,生成的Stock::toString方法拋出了LazyInitializationException異常。

+0

請發佈整個股票類。 SQL說從股票s中選擇s.name,s.price,屬性名稱在哪裏? –

+0

對不起,它是'索引'。我編輯了這個問題。 – Feeco

+0

「日誌」是什麼意思是簡單的? –

回答

5

相關答案:JPA eager fetch does not join

你基本上需要設置@Fetch(FetchMode.JOIN)語句,因爲取= FetchType.EAGER只是指定的關係將被加載,而不是如何。

還什麼可能與您的問題幫助是 @BatchSize註解,它指定多少懶的集合將被加載,當請求第一個。例如,如果您在內存中有100筆交易(股票未初始化)@BatchSize(size = 50)將確保只使用2個查詢。有效地改變n + 1到(n + 1)/ 50。 https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/BatchSize.html

對於嵌入式,你可能要設置 hibernate.jdbc.batch_size屬性和設置order_inserts和order_updates到也是如此。 https://vladmihalcea.com/how-to-batch-insert-and-update-statements-with-hibernate/

0

然而,產生庫存:: toString方法拋出 LazyInitializationException中異常。

好了,從這個我使用兩種或龍目基於類的各個領域的IDE發生器假設你已經產生toString()(最有可能equals()hashcode()方法)。

不要以這種方式在JPA環境覆蓋equals()hashcode()toString(),因爲它有(一)觸發你所看到的,如果的toString(例外的可能)訪問事務和外部的延遲加載的集合( b)在事務中使用時觸發加載極大的數據量。寫一個合理的字符串,不涉及關聯,並實現equals()和hashcode()使用(a)一些商業密鑰,如果有的話,(b)ID(知道如果可能的問題或(c)不能覆蓋他們。

所以,首先清除這些生成的方法,看看是否有改善的事情一點。

0

至於到插入,我注意到一兩件事,往往是在JPA忽視。我不知道你使用的是什麼數據庫,但你必須小心

@GeneratedValue(strategy = GenerationType.AUTO) 

For MySQL我認爲所有的JPA實現都映射到一個auto_incremented字段,一旦你知道JPA如何工作,這有兩個含義。

  • 每個插入將包含兩個查詢。首先插入,然後選擇查詢(MySQL的LAST_INSERT_ID)來獲取生成的主鍵。
  • 它還可以防止任何批量查詢優化,因爲每個查詢都需要在自己的插入中完成。

如果插入大量對象,並且想要良好的性能,我會建議使用表生成的序列,讓JPA在大塊中預先分配ID,這也允許SQL驅動程序執行批量插入(...)VALUES(...)優化。

另一個建議(並非所有人都同意這一點)。就我個人而言,我從不使用ManyToMany,我總是將它分解爲OneToMany和ManyToOne,並將連接表作爲一個真正的實體。我喜歡通過級聯和提取添加的控制,並且避免了一些存在雙向關係的ManyToMany陷阱。

相關問題