Speichern Sie untergeordnete Objekte automatisch mit JPA Hibernate

78

Ich habe eine Eins-zu-Viele-Beziehung zwischen Eltern- und Kindertabelle. Im übergeordneten Objekt habe ich eine

List<Child> setChildren(List<Child> childs)

Ich habe auch einen Fremdschlüssel in der Child-Tabelle. Dieser Fremdschlüssel ist eine ID, die auf eine übergeordnete Zeile in der Datenbank verweist. In meiner Datenbankkonfiguration kann dieser Fremdschlüssel also nicht NULL sein. Auch dieser Fremdschlüssel ist der Primärschlüssel in der übergeordneten Tabelle.

Meine Frage ist also, wie ich die untergeordneten Objekte automatisch speichern kann, indem ich so etwas mache:

session.save(parent);

Ich habe das oben Genannte versucht, erhalte jedoch einen Datenbankfehler, der sich darüber beschwert, dass das Fremdschlüsselfeld in der untergeordneten Tabelle nicht NULL sein kann. Gibt es eine Möglichkeit, JPA anzuweisen, diesen Fremdschlüssel automatisch in das untergeordnete Objekt zu setzen, damit untergeordnete Objekte automatisch gespeichert werden können?

Danke im Voraus.

Marquinio
quelle
Zeigen Sie uns die Zuordnung, den Code und den ursprünglichen Fehler.
Adeel Ansari

Antworten:

88

Ich habe das oben Genannte versucht, erhalte jedoch einen Datenbankfehler, der sich darüber beschwert, dass das Fremdschlüsselfeld in der untergeordneten Tabelle nicht NULL sein kann. Gibt es eine Möglichkeit, JPA anzuweisen, diesen Fremdschlüssel automatisch in das untergeordnete Objekt zu setzen, damit untergeordnete Objekte automatisch gespeichert werden können?

Hier gibt es zwei Dinge.

Zuerst müssen Sie den Speichervorgang kaskadieren (aber ich verstehe, dass Sie dies tun oder dass Sie beim Einfügen in die "untergeordnete" Tabelle keine Verletzung der FK-Einschränkung erhalten würden).

Zweitens haben Sie wahrscheinlich eine bidirektionale Zuordnung und ich denke, dass Sie "beide Seiten des Links" nicht richtig einstellen. Sie sollen so etwas tun:

Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);

List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChildren(children);

session.save(parent);

Ein gängiges Muster ist die Verwendung von Link-Management-Methoden:

@Entity
public class Parent {
    @Id private Long id;

    @OneToMany(mappedBy="parent")
    private List<Child> children = new ArrayList<Child>();

    ...

    protected void setChildren(List<Child> children) {
        this.children = children;
    }

    public void addToChildren(Child child) {
        child.setParent(this);
        this.children.add(child);
    }
}

Und der Code wird:

Parent parent = new Parent();
...
Child c1 = new Child();
...

parent.addToChildren(c1);

session.save(parent);
Verweise
Pascal Thivent
quelle
1
Ja, mein Problem war, dass ich eigentlich ein unidirektionales Mapping wollte, aber stattdessen so etwas wie ein bidirektionales Mapping hatte. Das Problem war, dass ich den Fremdschlüssel in der untergeordneten Tabelle zugeordnet hatte. Also habe ich es vom Kindertisch entfernt und es hat funktioniert. Ich habe @OneToMany und @JoinColumn in der übergeordneten Tabelle verwendet. Vielen Dank.
Marquinio
@pascal Tolle Erklärung! Danke Pascal. Ich verstehe, dass Sie beim Einstellen von der übergeordneten Seite die oben genannten Schritte ausführen müssen. Aber wenn wir von der untergeordneten Seite festlegen, ist das folgende ausreichend child.setParent (Eltern)?
HopeKing
32

Ich glaube, Sie müssen die Kaskadenoption in Ihrem Mapping über XML / Annotation festlegen. Siehe Referenzbeispiel für den Ruhezustand hier .

Wenn Sie Anmerkungen verwenden, müssen Sie Folgendes tun:

@OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..
Adeel Ansari
quelle
Richtig, das Standardkaskadenverhalten ist nur NICHTS.
Masterziv
10
Ich denke es sollte sein @OneToMany(cascade = CascadeType.ALL), Insert existiert nicht!
Tommy Müller
newnoise: Ich bin heutzutage nicht in Kontakt mit Hibernate. Aber dieses Mal war es eine der verfügbaren Optionen.
Adeel Ansari
2
CascadeType.INSERTwird ersetzt durch CascadeType.PERSIST.
Adeel Ansari
Ich habe versucht zu benutzen CascadeType.PERSIST. Es hat nicht funktioniert. Das Ersetzen durch hat CascadeType.ALLdas Problem behoben.
Jelle den Burger
2

Das folgende Programm beschreibt, wie bidirektionale Beziehungen im Ruhezustand funktionieren.

Wenn Eltern ihre Liste der untergeordneten Objekte speichern, wird diese automatisch gespeichert.

Auf der Elternseite:

    @Entity
    @Table(name="clients")
    public class Clients implements Serializable  {

         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)     
         @OneToMany(mappedBy="clients", cascade=CascadeType.ALL)
          List<SmsNumbers> smsNumbers;
    }

Und setzen Sie die folgende Anmerkung auf die untergeordnete Seite:

  @Entity
  @Table(name="smsnumbers")
  public class SmsNumbers implements Serializable {

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     int id;
     String number;
     String status;
     Date reg_date;
     @ManyToOne
     @JoinColumn(name = "client_id")
     private Clients clients;

    // and getter setter.

 }

Hauptklasse:

 public static void main(String arr[])
 {
    Session session = HibernateUtil.openSession();
      //getting transaction object from session object
    session.beginTransaction();

    Clients cl=new Clients("Murali", "1010101010");
    SmsNumbers sms1=new SmsNumbers("99999", "Active", cl);
    SmsNumbers sms2=new SmsNumbers("88888", "InActive", cl);
    SmsNumbers sms3=new SmsNumbers("77777", "Active", cl);
    List<SmsNumbers> lstSmsNumbers=new ArrayList<SmsNumbers>();
    lstSmsNumbers.add(sms1);
    lstSmsNumbers.add(sms2);
    lstSmsNumbers.add(sms3);
    cl.setSmsNumbers(lstSmsNumbers);
    session.saveOrUpdate(cl);
    session.getTransaction().commit(); 
    session.close();    

 }
Shriram
quelle
2
Bitte erläutern Sie Ihre Antwort, damit der Fragesteller weiß, wie und warum Ihre Antwort funktioniert.
UmarZaii
Es wird 1 Client und dann 3 SMS-Nummern eingefügt und danach wird fk von 3 SMS-Nummern aktualisiert. Dies ist nicht gut für die Leistung. Verwenden Sie LinkManagement in Bidrectional
Tarunjeet Singh Salh
1

In Ihren setChilds möchten Sie vielleicht versuchen, die Liste zu durchlaufen und so etwas zu tun

child.parent = this;

Sie sollten auch die Kaskade auf dem übergeordneten Element auf die entsprechenden Werte einstellen.

hvgotcodes
quelle
1

Hier sind die Möglichkeiten, ein übergeordnetes Objekt in einem untergeordneten Objekt mit bidirektionalen Beziehungen zuzuweisen.

Angenommen, Sie haben eine Beziehung, die Eins-zu-Viele sagt, dann existiert für jedes übergeordnete Objekt eine Reihe von untergeordneten Objekten. In bidirektionalen Beziehungen verweist jedes untergeordnete Objekt auf sein übergeordnetes Objekt.

eg : Each Department will have list of Employees and each Employee is part of some department.This is called Bi directional relations.

Um dies zu erreichen, besteht eine Möglichkeit darin, Eltern im untergeordneten Objekt zuzuweisen, während das übergeordnete Objekt beibehalten wird

Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);

List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChilds(children);

session.save(parent);

Eine andere Möglichkeit ist, Intercepter im Ruhezustand zu verwenden. Auf diese Weise können Sie nicht für alle Modelle den obigen Code schreiben.

Hibernate Interceptor bietet APIs, mit denen Sie Ihre eigene Arbeit erledigen können, bevor Sie eine DB-Operation ausführen. Ebenso können wir unter Speichern von Objekten übergeordnete Objekte in untergeordneten Objekten mithilfe von Reflektion zuweisen.

public class CustomEntityInterceptor extends EmptyInterceptor {

    @Override
    public boolean onSave(
            final Object entity, final Serializable id, final Object[] state, final String[] propertyNames,
            final Type[] types) {
        if (types != null) {
            for (int i = 0; i < types.length; i++) {
                if (types[i].isCollectionType()) {
                    String propertyName = propertyNames[i];
                    propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                    try {
                        Method method = entity.getClass().getMethod("get" + propertyName);
                        List<Object> objectList = (List<Object>) method.invoke(entity);

                        if (objectList != null) {
                            for (Object object : objectList) {
                                String entityName = entity.getClass().getSimpleName();
                                Method eachMethod = object.getClass().getMethod("set" + entityName, entity.getClass());
                                eachMethod.invoke(object, entity);
                            }
                        }

                    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return true;
    }

}

Und Sie können Intercepter für die Konfiguration als registrieren

new Configuration().setInterceptor( new CustomEntityInterceptor() );
Sunil Kumar
quelle
Yusuf
0

Verwenden Sie org.hibernate.annotationsdazu Cascade, wenn das hibernateund JPAzusammen verwendet wird, es sich irgendwie über das Speichern der untergeordneten Objekte beschwert.

Sandhya Nayak
quelle
0

Kurz gesagt, Kaskadentyp für alle, wird einen Job machen; Ein Beispiel in Ihrem Modell. Fügen Sie Code wie folgt hinzu. @OneToMany (mappedBy = "Quittung", cascade = CascadeType.ALL) private List saleSet;

Dila Gurung
quelle