开发者

error refreshing JPA Entity

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?


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.


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


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).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜