Frage

Ich verwende Hibernate mit Anmerkungen (im Frühjahr), und ich habe ein Objekt, das eine geordnete hat, viele-zu-eins-Beziehung, die ein Kind Objekt, das einen zusammengesetzten Primärschlüssel hat, von denen eine Komponente ein Fremdschlüssel ist zurück in die ID des übergeordneten Objekts.

Die Struktur sieht wie folgt aus:

+=============+                 +================+
| ParentObj   |                 | ObjectChild    |
+-------------+ 1          0..* +----------------+
| id (pk)     |-----------------| parentId       |
| ...         |                 | name           |
+=============+                 | pos            |
                                | ...            |
                                +================+

ich eine Vielzahl von Kombinationen von Annotationen versucht haben, von denen keine zu funktionieren scheint. Dies ist die nächste, die ich habe in der Lage zu kommen mit:

@Entity
public class ParentObject {
    @Column(nullable=false, updatable=false)
    @Id @GeneratedValue(generator="...")
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    ...
}

@Entity
public class ChildObject {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(nullable=false, updatable=false)
        private String parentId;

        @Column(nullable=false, updatable=false)
        private String name;

        @Column(nullable=false, updatable=false)
        private int pos;

        @Override
        public String toString() {
            return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
        }

        ...
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name="parentId")
    private ParentObject parent;

    ...
}

kam ich an dieser nach einem langen Kampf des Experimentierens, in denen die meisten meiner anderen Versuche ergaben Einheiten, die Hibernate nicht einmal aus verschiedenen Gründen nicht laden.

UPDATE: Vielen Dank an Alle für die Kommentare; Ich habe einige Fortschritte gemacht. Ich habe ein paar Veränderungen gemacht und ich denke, es ist näher (ich habe den Code oben aktualisiert). Nun aber ist die Frage auf Einsatz. Das übergeordnete Objekt scheint in Ordnung zu retten, aber die untergeordneten Objekte sparen nicht, und was ich in der Lage gewesen, ist festzustellen, dass Hibernate nicht den parentId Teil des (Composite) Primärschlüssels der untergeordneten Objekte ausfüllen, so dass ich‘ m einen nicht-eindeutigen Fehler bekommen:

org.hibernate.NonUniqueObjectException:
   a different object with the same identifier value was already associated 
   with the session: [org.kpruden.ObjectChild#null.attr1[0]]

Ich bin bevölkern die name und pos Attribute in meinem eigenen Code, aber natürlich weiß ich nicht, die übergeordnete ID, weil sie noch nicht gespeichert wurde. Alle Ideen, wie Hibernate zu überzeugen, diese heraus zu füllen?

Danke!

War es hilfreich?

Lösung 4

Nach vielen Experimenten und Frustration, ich schließlich festgestellt, dass ich nicht genau tun kann, was ich will.

Schließlich ging ich weiter und gab das Kind Objekt seine eigenen synthetischen Schlüssel und lasse Hibernate verwalten. Es ist ein nicht ideal, da der Schlüssel fast so groß wie der Rest der Daten ist, aber es funktioniert.

Andere Tipps

Das Manning Buch Java Persistence mit Hibernate ein Beispiel hat darlegt, wie dies zu tun in Abschnitt 7.2 . Zum Glück, auch wenn Sie das Buch nicht tun besitzen, können Sie ein Quellcode Beispiel dafür sehen durch die PPV-Version des Emptor Caveat Beispielprojekt (direkter Link hier ) und die Klassen Category und CategorizedItem im auction.model Paket zu prüfen.

Ich werde auch die wichtigsten Anmerkungen unten zusammenfassen. Lassen Sie mich wissen, ob es noch ein No-Go.

Parent:

@Entity
public class ParentObject {
   @Id @GeneratedValue
   @Column(name = "parentId", nullable=false, updatable=false)
   private Long id;

   @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
   @IndexColumn(name = "pos", base=0)
   private List<ChildObject> attrs;

   public Long getId () { return id; }
   public List<ChildObject> getAttrs () { return attrs; }
}

Childobject:

@Entity
public class ChildObject {
   @Embeddable
   public static class Pk implements Serializable {
       @Column(name = "parentId", nullable=false, updatable=false)
       private Long objectId;

       @Column(nullable=false, updatable=false)
       private String name;

       @Column(nullable=false, updatable=false)
       private int pos;
       ...
   }

   @EmbeddedId
   private Pk id;

   @ManyToOne
   @JoinColumn(name="parentId", insertable = false, updatable = false)
   @org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
   private ParentObject parent;

   public Pk getId () { return id; }
   public ParentObject getParent () { return parent; }
}

Sie sollten die ParentObject Referenz nur in ChildObject.Pk integrieren, anstatt Karte Eltern und parentId getrennt:

(Getter, Setter, Attribute Hibernate nicht im Zusammenhang mit Problem und Mitglied Zugang Schlüsselwort weggelassen)

class ChildObject { 
    @Embeddable
    static class Pk {
        @ManyToOne...
        @JoinColumn(name="parentId")
        ParentObject parent;

        @Column...
        String name...
        ...
    }

    @EmbeddedId
    Pk id;
}

In ParentObject Sie dann @OneToMany(mappedBy="id.parent") gerade setzen und es funktioniert.

Zum einen in der ParentObject, "fix" das mappedBy Attribut, das auf "parent" gesetzt werden sollte. Auch (aber das ist vielleicht ein Tippfehler) fügen Sie eine @Id Anmerkung:

@Entity
public class ParentObject {
    @Id
    @GeneratedValue
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    // getters/setters
}

Dann in ObjectChild, fügen Sie ein name Attribut auf den objectId in dem Verbundschlüssel:

@Entity
public class ObjectChild {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(name = "parentId", nullable = false, updatable = false)
        private String objectId;

        @Column(nullable = false, updatable = false)
        private String name;

        @Column(nullable = false, updatable = false)
        private int pos;
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private ParentObject parent;

    // getters/setters

}

und auch insertable = false, updatable = false zum @JoinColumn hinzufügen, weil wir die parentId Spalt in der Abbildung dieser Einheit wiederholen.

Mit diesen Änderungen persistierenden und die Entitäten Lesen für mich funktioniert gut (mit Derby getestet).

diese Frage für die Antwort auf das Problem der Suche, aber es Antworten des nicht mein Problem lösen, weil ich für @OneToMany war auf der Suche, das nicht so gut von einem Sitz für die Tabellenstruktur, die ich nach würde. @ElementCollection ist die richtige Passform in meinem Fall. Einer der gotchas davon ich aber glaube, ist, dass es in der gesamten Reihe von Beziehungen als einzigartig betrachtet, nicht nur die Zeilen-ID.

@Entity
public class ParentObject {
@Column(nullable=false, updatable=false)
@Id @GeneratedValue(generator="...")
private String id;

@ElementCollection
@CollectionTable( name = "chidren", joinColumns = @JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;

...
}

@Embeddable
public static class ObjectChild implements Serializable {
    @Column(nullable=false, updatable=false)
    private String parentId;

    @Column(nullable=false, updatable=false)
    private String name;

    @Column(nullable=false, updatable=false)
    private int pos;

    @Override
    public String toString() {
        return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
    }

    ... getters and setters REQUIRED (at least they were for me)
}

Es scheint, dass Sie ziemlich nahe kommen, und ich versuche, die gleiche Sache in meinem aktuellen System zu tun. Ich begann mit dem Ersatzschlüssel aber möchte es einen zusammengesetzten Primärschlüssel zugunsten entfernen, bestehend aus den Eltern PK und den Index in der Liste.

konnte ich eine Eins-zu-Eins-Beziehung, dass die Aktien der PK aus der Master-Tabelle erhalten, indem einen „fremden“ Generator mit:

@Entity
@GenericGenerator(
    name = "Parent",
    strategy = "foreign",
    parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
    @Id
    @GeneratedValue(generator = "Parent")
    @Column(name = "parent_id")
    private int parentId;

    @OneToOne(mappedBy = "childObject")
    private ParentObject parentObject;
    ...
}

Ich frage mich, ob Sie die @GenericGenerator hinzufügen könnte und @GeneratedValue das Problem der Ruhezustand nicht die Zuordnung der Eltern zu lösen ist neu PK während des Einsetzens erworben.

Nach dem übergeordneten Objekt zu speichern, müssen Sie explizit die parentId im Kind Set-Objekte für die Einsätze auf der Child-Objekte an der Arbeit.

Nach drei Tagen auf das, ich glaube, ich habe eine Lösung gefunden, aber um ehrlich zu sein, mag ich es nicht, und es kann auf jeden Fall verbessert werden. Doch es funktioniert und löst unser Problem.

Hier ist Ihr Unternehmen Konstruktor, aber man könnte es auch in der Setter-Methode tun. Außerdem habe ich ein Collection-Objekt, aber es sollte mit Liste gleich oder ähnlich sein:

...
public ParentObject(Collection<ObjectChild> children) {
    Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
    for(ObjectChild obj:children){
        obj.setParent(this);
        occ.add(obj);
    }
    this.attrs = occ;
}
...

Im Grunde genommen als jemand anderes vorgeschlagen, müssen wir zuerst manuell setzen Sie alle Eltern id Kinder vor den Eltern Speicher (zusammen mit allen Kindern)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top