刷新 JPA 实体时出错

发布于 2024-11-17 04:55:18 字数 2518 浏览 3 评论 0原文

我有以下域模型

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

或者英文

产品有一个或多个价格。每个价格均以特定货币计价。

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);
    }
}

当我尝试 刷新 Product 的实例,我收到 StackOverflowError,所以我怀疑存在某种循环(或其他错误)在上面的映射中,有人能发现它吗?

I have the following domain model

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

Or in English

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

Price has a composite primary key (represent by PricePK below) which is composed of the foreign keys to Currency and Product. The relevant sections of the JPA-annotated Java classes are below (getters and setters mostly omitted):

@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);
    }
}

When I try to refresh an instance of Product, I get a StackOverflowError, so I suspect there's some kind of cycle (or other mistake) in the mapping above, can anyone spot it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一枫情书 2024-11-24 04:55:18

我已经多次看到这个错误,但我不记得确切的解决方案。我的想法是,您需要从 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 {
    [...]
}

请检查列名称是否正确,因为我看不到产品或货币上的 id 列。

I've seen this error a few times, but I can't remember the exact solution. I have the idea that you need to remove mapping from PricePK (both @ManyToOne) and replace that with @AssociationOverrides on Price.

@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 {
    [...]
}

please check that the column names are ok, as I can't see the id columns on Product or Currency.

很快妥协 2024-11-24 04:55:18

我遇到了类似的问题,结果是由 OneToMany 注释中定义的 CascadeType.ALL 引起的。当您刷新产品时,它将尝试刷新持久上下文中的价格。

根据情况,在实体管理器中刷新产品时,您也许可以不刷新价格,例如:

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

I encountered a similar problem and it turned out to be caused by the CascadeType.ALL defined in the OneToMany annotation. When you refresh product, it is going to try to refresh the prices that are in the persistent context.

Depending on the situation, you may be able to get by without having the prices refreshed when the product is refreshed in the entity manager, such as:

@OneToMany(mappedBy = "pricePK.product", cascade = {
        CascadeType.PERSIST, CascadeType.MERGE
    }, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Collection<Price> defaultPrices = new ArrayList<Price>();
你在看孤独的风景 2024-11-24 04:55:18

难道您不应该直接在 Price 实体中将 Price 到 Product 和Currency 的关系声明为 ManyToOne ,并用 @IdClass(PricePK) 注释 Price 吗?

我不知道 Hibernate 如何处理这个问题,但我已经使用 OpenJPA 成功实现了这个问题。在这种情况下,PricePK 必须使用与 Price 中相同的名称来声明其字段,但使用简单类型(整数而不是货币或产品)。在 Price 中,您可能需要使用 @Id 注释产品和货币。请注意,这不符合 JPA 规范(@IdClass 仅支持简单字段)。

Shouldn't you just declare the relations from Price to Product and Currency as ManyToOne directly in the Price entity, and annotate Price with @IdClass(PricePK)?

I don't know how Hibernate handles this though, but I have successfully implemented this using OpenJPA. In that case, PricePK must declare its fields with the same names as in Price, but using the simple type instead (Integer instead of Currency or Product). In Price, you might need to annotate product and currency with @Id. Note that this is out of the JPA spec (@IdClass only supports simple fields).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文