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();
}
}
}
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 ... –