2011-06-24 86 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>();