2011-06-24 85 views
5

我有以下的域模型錯誤刷新JPA實體

Currency ----<Price>---- Product 

或英文

A Product has one or more Prices. Each Price is denominated in a particular Currency.

Price具有其由外鍵的一個複合主鍵(由下面PricePK代表) CurrencyProduct。在JPA註解的Java類的相關章節低於(getter和setter大多省略):

@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
public class Currency { 

    @Id 
    private Integer ix; 

    @Column 
    private String name; 

    @OneToMany(mappedBy = "pricePK.currency", cascade = CascadeType.ALL, orphanRemoval = true) 
    @LazyCollection(LazyCollectionOption.FALSE) 
    private Collection<Price> prices = new ArrayList<Price>(); 
} 

@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
public class Product { 

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

    @OneToMany(mappedBy = "pricePK.product", cascade = CascadeType.ALL, orphanRemoval = true) 
    @LazyCollection(LazyCollectionOption.FALSE) 
    private Collection<Price> defaultPrices = new ArrayList<Price>(); 
} 

@Embeddable 
public class PricePK implements Serializable { 

    private Product product;  
    private Currency currency; 

    @ManyToOne(optional = false) 
    public Product getProduct() { 
     return product; 
    } 

    @ManyToOne(optional = false) 
    public Currency getCurrency() { 
     return currency; 
    }  
} 

@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
public class Price { 

    private PricePK pricePK = new PricePK(); 

    private BigDecimal amount; 

    @Column(nullable = false) 
    public BigDecimal getAmount() {  
     return amount; 
    } 

    public void setAmount(BigDecimal amount) {  
     this.amount = amount; 
    } 

    @EmbeddedId 
    public PricePK getPricePK() { 
     return pricePK; 
    }  

    @Transient 
    public Product getProduct() { 
     return pricePK.getProduct(); 
    } 

    public void setProduct(Product product) { 
     pricePK.setProduct(product); 
    } 

    @Transient 
    public Currency getCurrency() { 
     return pricePK.getCurrency(); 
    } 

    public void setCurrency(Currency currency) { 
     pricePK.setCurrency(currency); 
    } 
} 

當我嘗試refreshProduct一個實例,我得到的StackOverflowError,所以我懷疑有某種週期(或其他錯誤)在上面的映射中,任何人都可以發現它嗎?

+0

+1,很好地提出問題。不過,我很好奇域模型。看起來很奇怪的是,'價格'是由'產品'+'貨幣'唯一標識的,而不是(標量)值+'貨幣'。 –

+0

感謝Matt,'Price'類中實際上有'BigDecimal amount'字段,但我在這裏省略了它,因爲它與問題無關,並且我希望儘可能縮短代碼清單 –

回答

2

我已經看過這個錯誤幾次,但我不記得確切的解決方案。我認爲你需要從PricePK中刪除映射(都是@ManyToOne),並用Price上的@AssociationOverrides替換它。

@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
@AssociationOverrides({ 
    @AssociationOverride(name = "pricePK.product", 
         joinColumns = @JoinColumn(name = "product_id")), 
    @AssociationOverride(name = "pricePK.currency", 
         joinColumns = @JoinColumn(name = "currency_id")) 
}) 
public class Price extends VersionedEntity { 
    [...] 
} 

請檢查列名都OK,因爲我不能看到產品或貨幣的ID列。

+0

我已經更新了類以顯示ID列 –

+0

@Don您是否嘗試過此解決方案?另外,我沒有正確表達自己,我提到的*列名*是Price上的列名。所以我假設在價格表上有兩列** product_id **和** currency_id ** – Augusto

+0

我試過了您的建議,但是它導致了這個錯誤:'無法確定類型爲:com.example.Currency,在表格:PRICE,列:[org.hibernate.mapping.Column(貨幣)]' –

0

難道你不應該直接在價格實體中聲明從價格到產品和貨幣的關係作爲ManyToOne,並用@IdClass(PricePK)對價格進行註釋?

我不知道Hibernate如何處理這個,但我已經成功地使用OpenJPA實現了這一點。在這種情況下,PricePK必須使用與Price中相同的名稱聲明其字段,但使用簡單類型(而不是貨幣或產品)。在價格中,您可能需要使用@Id註釋產品和貨幣。請注意,這不符合JPA規範(@IdClass僅支持簡單字段)。

1

我遇到了類似的問題,它證明是由OneToMany註釋中定義的CascadeType.ALL引起的。當您刷新產品時,它將嘗試刷新持久性上下文中的價格。

根據不同的情況,你可以不必當產品在實體管理器刷新價格刷新度日,如:

@OneToMany(mappedBy = "pricePK.product", cascade = { 
     CascadeType.PERSIST, CascadeType.MERGE 
    }, orphanRemoval = true) 
@LazyCollection(LazyCollectionOption.FALSE) 
private Collection<Price> defaultPrices = new ArrayList<Price>();