2013-01-02 7 views
11

ConfigPPV bestehen Einheiten mit einer zu vielen Beziehung

  • EcliplseLink 2.3.2
  • JPA 2.0
  • Die Einheiten werden automatisch aus dem Db-Schema von NetBeans mit Entitätsklassen aus der Datenbank erstellt ... Assistent.
  • Die Controller-Klassen Auto werden von Netbeans mit JPA-Controller Klassen von Entity-Klassen erstellt ... Assistent

Kurzversion Frage

In einem klassischen Szenario zwei Tabellen mit einer bis viele Beziehungen. Ich erstelle die übergeordnete Entität und dann die untergeordnete Entität, und ich füge das untergeordnete Element an die Auflistung des übergeordneten Objekts an. Wenn ich (Controller-Methode) die übergeordnete Entität erstellen, erwarte ich, dass die untergeordnete Entität erstellt und mit übergeordneten verknüpft wird. Warum passiert das nicht?

Lange Version

Geordnete Klasse

@Entity 
@XmlRootElement 
public class Device implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    private Integer id; 
    @Column(unique=true) 
    private String name; 
    @Temporal(TemporalType.TIMESTAMP) 
    private Date updated; 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "deviceId") 
    private Collection<NetworkInterface> networkInterfaceCollection; 

    public Device() { 
    } 

    public Device(String name) { 
     this.name = name; 
     updated = new Date(); 
    } 

    // setters and getters... 

    @XmlTransient 
    public Collection<NetworkInterface> getNetworkInterfaceCollection() { 
     return networkInterfaceCollection; 
    } 

    public void setNetworkInterfaceCollection(Collection<NetworkInterface> networkInterfaceCollection) { 
     this.networkInterfaceCollection = networkInterfaceCollection; 
    } 

    public void addNetworkInterface(NetworkInterface net) { 
     this.networkInterfaceCollection.add(net); 
    } 

    public void removeNetworkInterface(NetworkInterface net) { 
     this.networkInterfaceCollection.remove(net); 
    } 
    // other methods 
} 

Kinder Klasse

@Entity 
@Table(name = "NETWORK_INTERFACE") 
@XmlRootElement 
public class NetworkInterface implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    private Integer id; 
    private String name; 
    @Temporal(TemporalType.TIMESTAMP) 
    private Date updated; 
    @JoinColumn(name = "DEVICE_ID", referencedColumnName = "ID") 
    @ManyToOne(optional = false) 
    private Device deviceId; 

    public NetworkInterface() { 
    } 

    public NetworkInterface(String name) { 
     this.name = name; 
     this.updated = new Date(); 
    } 

    // setter and getter methods... 

    public Device getDeviceId() { 
     return deviceId; 
    } 

    public void setDeviceId(Device deviceId) { 
     this.deviceId = deviceId; 
    } 
} 

Hauptklasse

public class Main { 
    public static void main(String[] args) { 
     EntityManagerFactory emf = Persistence.createEntityManagerFactory("wifi-dbPU"); 
     DeviceJpaController deviceController = new DeviceJpaController(emf); 
     NetworkInterfaceJpaController netController = new NetworkInterfaceJpaController(emf); 

     Device device = new Device("laptop"); 
     NetworkInterface net = new NetworkInterface("eth0"); 

     device.getNetworkInterfaceCollection().add(net); 
     deviceController.create(device); 
    } 
} 

Diese cl ass wirft eine Nullpointer in Zeile: device.getNetworkInterfaceCollection().add(net);

Das System weiß, dass es eine neue Einheit ist device und es hat ein Element net in seiner Sammlung. Ich erwartete, dass es device in db schreibt, Geräte-ID abruft, es an net anhängt und es in db schreibt.

Statt dessen fand ich, dass das sind die Schritte, die ich tun muss:

deviceController.create(device); 
net.setDeviceId(device); 
device.getNetworkInterfaceCollection().add(net); 
netController.create(net); 

Warum muss ich das Kind schaffen müssen, wenn die übergeordnete Klasse weiß, dass es Kind ist, und es sollte es für mich schaffen ?

Die create-Methode von DeviceJpaController (Entschuldigung für die langen Namen in Feldern, sie sind automatisch generiert).

public EntityManager getEntityManager() { 
    return emf.createEntityManager(); 
} 

public void create(Device device) { 
    if (device.getNetworkInterfaceCollection() == null) { 
     device.setNetworkInterfaceCollection(new ArrayList<NetworkInterface>()); 
    } 
    EntityManager em = null; 
    try { 
     em = getEntityManager(); 
     em.getTransaction().begin(); 
     Collection<NetworkInterface> attachedNetworkInterfaceCollection = new ArrayList<NetworkInterface>(); 
     for (NetworkInterface networkInterfaceCollectionNetworkInterfaceToAttach : device.getNetworkInterfaceCollection()) { 
      networkInterfaceCollectionNetworkInterfaceToAttach = em.getReference(networkInterfaceCollectionNetworkInterfaceToAttach.getClass(), networkInterfaceCollectionNetworkInterfaceToAttach.getId()); 
      attachedNetworkInterfaceCollection.add(networkInterfaceCollectionNetworkInterfaceToAttach); 
     } 
     device.setNetworkInterfaceCollection(attachedNetworkInterfaceCollection); 
     em.persist(device); 
     for (NetworkInterface networkInterfaceCollectionNetworkInterface : device.getNetworkInterfaceCollection()) { 
      Device oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface = networkInterfaceCollectionNetworkInterface.getDeviceId(); 
      networkInterfaceCollectionNetworkInterface.setDeviceId(device); 
      networkInterfaceCollectionNetworkInterface = em.merge(networkInterfaceCollectionNetworkInterface); 
      if (oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface != null) { 
       oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface.getNetworkInterfaceCollection().remove(networkInterfaceCollectionNetworkInterface); 
       oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface = em.merge(oldDeviceIdOfNetworkInterfaceCollectionNetworkInterface); 
      } 
     } 
     em.getTransaction().commit(); 
    } finally { 
     if (em != null) { 
      em.close(); 
     } 
    } 
} 

Antwort

21

Ich verstand endlich die Logik hinter persistent einer zu vielen Entitäten. Der Prozess ist:

  1. Elternklasse erstellen
  2. es Persist
  3. Kind Klasse erstellen
  4. Mitarbeiterin Kind mit seinen Eltern
  5. Kind Persist (die Mutter Sammlungen aktualisiert)

Mit Code:

public class Main { 
    public static void main(String[] args) { 
     EntityManagerFactory emf = Persistence.createEntityManagerFactory("wifi-dbPU"); 
     DeviceJpaController deviceController = new DeviceJpaController(emf); 
     NetworkInterfaceJpaController netController = new NetworkInterfaceJpaController(emf); 

     Device device = new Device("laptop");     // 1 
     deviceController.create(device);      // 2 

     NetworkInterface net = new NetworkInterface("eth0"); // 3 
     net.setDeviceId(device.getId());      // 4 
     netController.create(net);       // 5 
     // The parent collection is updated by the above create  
    } 
} 
Jetzt

, kann ich ein Gerät finden (mit id zum Beispiel), und ich kann alles, es ist Kind mit

Collection<NetworkInterface> netCollection = device.getNetworkInterfaceCollection() 

In der Einrichtung Entitätsklasse bekommen, die ich in der Frage gestellt, gibt es keine Notwendigkeit für die Verfahren und removeNetwokrInterface.

+1

Nach was ich aus dem Buch gelesen habe. 'cascade = CascadeType.Persist' sollte alle Beziehungen beibehalten und Sie müssen nur eine Entität aktualisieren. JPA navigiert durch die Relationen und aktualisiert die zugehörigen Entitäten. Aber ... ich konnte es nicht funktionieren lassen ... –

2

Dies ist ein bekanntes Verhalten von Auflistungsdatenelementen. Die einfachste Lösung besteht darin, den Sammlungs-Getter zu modifizieren, um die Sammlung zu erstellen.

Denken Sie auch daran, sich auf dieses Datenelement nur über die Getter-Methode zu beziehen.

+0

Diese im 'DeviceJpaController' erstellen erfolgt zu aktualisieren. Siehe das if am Anfang. Wenn die Auflistung null ist, erstellt sie eine neue und setzt sie mit der Methode 'device.setNetworkInterfaceCollection'. Ich habe versucht, schlägt Ihnen vor, und ich bekomme: 'IllegalArgumentException: Eine Instanz eines Null-PK wurde für diesen Suchvorgang falsch bereitgestellt. –

4

@ Dima K ist richtig in dem, was sie sagen.Wenn Sie dies tun:

Device device = new Device("laptop"); 
    NetworkInterface net = new NetworkInterface("eth0"); 

    device.getNetworkInterfaceCollection().add(net); 
    deviceController.create(device); 

Die Sammlung in Gerät wurde erhalten Sie nicht initialisiert und so eine NPE, wenn sie versuchen, um es hinzuzufügen. In Ihrer Device Klasse, wenn Ihr Collection deklarieren, können Sie es auch initialisieren:

private Collection<NetworkInterface> networkInterfaceCollection = new CollectionType<>(); 

Wie für persistierende, sind Ihre Annahmen richtig, aber ich denke, dass die Ausführung nicht stimmt. Wenn Sie Ihr Gerät erstellen, sollten Sie es sofort mit JPA persistent machen (Transaktionsverwaltung, wo immer es benötigt wird).

Device device = new Device("laptop"); 
getEntityManager().persist(device); 

Machen Sie dasselbe für den Network:

NetworkInterface net = new NetworkInterface("eth0"); 
getEntityManager().persist(net); 

Da nun beide Ihre Entitäten beibehalten werden, Sie einen zum anderen hinzufügen können.

device.getNetworkInterfaceCollection().add(net);

PPV sollte der Rest kümmern, ohne dass Sie irgendwelche anderen verharrt anrufen müssen.

+1

Sie haben beide Recht mit der Initialisierung der Sammlung. Aber das ist meine Frage.Warum muss ich zwei Entitäten beibehalten, wenn ich nur die Sammlung erstellen muss? Sollte die JPA nicht alle Entitäten in der Sammlung beibehalten und sie der übergeordneten Klasse zuordnen? –

+0

JPA kennt die Entitäten und den Status, in dem sie sich befinden. Das Erstellen einer Geräteentität und einer Netzwerkschnittstellenentität führt genau das aus. JPA wird über zwei Entitäten wissen. Es liegt in Ihrer Verantwortung, die Beziehung zwischen den beiden zu erzählen. Ich verstehe, was Sie meinen, wenn Sie das Gerät beibehalten und Elemente zu seiner Sammlung hinzufügen, sollte JPA diese auch beibehalten. Wenn die Sammlung gefüllt wird, wenn Sie das Gerät persistieren (oder umgekehrt), werden das Gerät und seine Auflistungselemente beibehalten. –

0

Diese Ausnahme bedeutet, dass Sie versuchen, eine Entität zu finden (wahrscheinlich durch em.getReference()), die noch nicht persistent ist. Sie können em.getReference() oder em.find() nicht für Entitäten verwenden, die noch kein PK haben.

+0

Sie haben Recht. Es versucht, die ID des Netzes in einem Aufruf em.getReference() zu finden. Die ID ist null, weil die Entität noch nicht beibehalten wurde. Bitte beachten Sie den Kommentar bei @Sotirios Delimanolis Post. –

-1

Um die Speicherfähigkeit für eine @ OneToMany-Beziehung zu aktivieren, z.

@OneToMany(mappedBy="myTable", cascade=CascadeType.ALL) 
private List<item> items; 

Dann müssen Sie Ihre @ManyToOne Beziehung zu sagen, dass es erlaubt ist, myTable wie diese aktualisierbar = true

@ManyToOne @JoinColumn(name="fk_myTable", nullable = false, updatable = true, insertable = true) 
+0

"updatable = true, insertable = true" sind sowieso defaults, das behebt das Problem nicht. –

Verwandte Themen