5 Stimmen

Fehler beim Aktualisieren der JPA-Entität

Ich habe das folgende Domänenmodell

Währung ----< Preis >---- Produkt

Oder auf Englisch

Ein Produkt hat einen oder mehrere Preise. Jeder Preis ist in einer bestimmten Währung angegeben.

Preis hat einen zusammengesetzten Primärschlüssel (dargestellt durch PricePK unten), der aus den Fremdschlüsseln zu Währung und Produkt besteht. Die relevanten Abschnitte der mit JPA annotierten Java-Klassen sind unten aufgeführt (meist ohne Getter und 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 prices = new ArrayList();
}

@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 defaultPrices = new ArrayList();
}

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

Wenn ich versuche, eine Instanz von Product zu aktualisieren, erhalte ich einen StackOverflowError, daher vermute ich, dass es irgendeine Art von Zyklus (oder anderer Fehler) in der obigen Zuordnung gibt, kann jemand das erkennen?

0 Stimmen

+1, schön formulierte Frage. Ich bin jedoch neugierig auf das Domänenmodell. Es scheint merkwürdig, dass ein Price eindeutig durch das Product+Currency und nicht etwa durch seinen (skalaren) Wert + Currency identifiziert wird.

0 Stimmen

Danke Matt, es gibt auch ein BigDecimal amount Feld in der Price Klasse, aber ich habe es hier weggelassen, weil es für die Frage nicht relevant ist und ich die Codeliste so kurz wie möglich halten wollte.

2voto

Augusto Punkte 27549

Ich habe diesen Fehler schon ein paar Mal gesehen, aber ich kann mich nicht mehr an die genaue Lösung erinnern. Ich habe die Idee, dass du das Mapping von PricePK (beides @ManyToOne) entfernen musst und das mit @AssociationOverrides auf Price ersetzen musst.

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

Bitte überprüfe, ob die Spaltennamen in Ordnung sind, da ich die ID-Spalten auf Product oder Currency nicht sehen kann.

0 Stimmen

@Don Hast du diese Lösung ausprobiert? Außerdem habe ich mich nicht richtig ausgedrückt, die Spaltennamen, die ich erwähnt habe, sind die Namen der Spalten des Preises. Also ich ging davon aus, dass du in der Preistabelle zwei Spalten mit den Namen product_id und currency_id hast.

0 Stimmen

Ich habe Ihren Vorschlag ausprobiert, aber er hat diesen Fehler verursacht: Konnte den Typ nicht bestimmen für: com.example.Currency, in der Tabelle: PREIS, für Spalten: [org.hibernate.mapping.Column(currency)]

0 Stimmen

@Don, bist du in der Lage, eine dieser Entitäten mit EntityManager.find(pk) zu laden, oder tritt der Fehler nur bei einem Refresh auf? Ich erinnere mich, dass ich das schon einmal gesehen habe, es war ein ähnlicher Fall, bei dem es eine zyklische Abhängigkeit bei der Entitätsinstanziierung gab, und soweit ich mich erinnere, behandelt Hibernate nur Abhängigkeitsschleifen in vielen-zu-vielen-Beziehungen. Ich bin mir sicher, dass du bemerken wirst, dass es einen subtilen Unterschied zwischen einer bidirektionalen Beziehung und einer zyklischen Abhängigkeit gibt.

1voto

Josh Punkte 41

Ich hatte ein ähnliches Problem und es stellte sich heraus, dass es durch CascadeType.ALL verursacht wurde, das in der OneToMany-Annotation definiert war. Wenn Sie ein Produkt aktualisieren, wird versucht, die Preise zu aktualisieren, die im persistenten Kontext sind.

Je nach Situation können Sie möglicherweise darauf verzichten, die Preise zu aktualisieren, wenn das Produkt im Entity Manager aktualisiert wird, wie zum Beispiel:

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

0voto

Didier L Punkte 15931

Sollten Sie nicht einfach die Beziehungen von Preis zu Produkt und Währung als ManyToOne direkt in der Preis-Entität deklarieren und Preis mit @IdClass(PricePK) kennzeichnen?

Ich weiß allerdings nicht, wie Hibernate damit umgeht, aber ich habe dies erfolgreich mit OpenJPA implementiert. In diesem Fall muss PricePK seine Felder mit den gleichen Namen wie in Price deklarieren, jedoch mit dem einfachen Typ (Integer anstelle von Währung oder Produkt). In Price müssen Sie möglicherweise Produkt und Währung mit @Id kennzeichnen. Beachten Sie, dass dies außerhalb der JPA-Spezifikation liegt (@IdClass unterstützt nur einfache Felder).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X