JPA persiste des entités avec une à plusieurs relations
Config
- EcliplseLink 2.3.2
- JPA 2.0
- Les entités sont automatiquement créés à partir de la db schéma à partir de netbeans avec les Classes d'Entité de la Base de données... assistant.
- Les classes de contrôleur sont automatiquement créés à partir de netbeans avec JPA Contrôleur de Classes à partir de Classes d'Entité... assistant
Version courte de la question
Dans un scénario classique, deux tables avec un à plusieurs " de la relation. - Je créer l'entité mère, l'enfant de l'entité et j'attache de l'enfant du parent à la collection. Quand je créer (méthode de contrôleur) l'entité mère, j'attends de l'enfant de l'entité à être créé et associé avec les parents. Pourquoi n'est-il pas arrivé?
Version longue
Classe Parent
@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
}
Enfant de la classe
@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;
}
}
Classe principale
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);
}
}
Cette classe lève une exception NullPointerException en ligne: device.getNetworkInterfaceCollection().add(net);
Le système sait qu'il y a une nouvelle entité device
et il a un élément net
dans sa collection. Je m'attendais à écrire device
en db, obtenir de l'appareil id, l'attacher à net
et de l'écrire en db.
Au lieu de cela, j'ai trouvé que ce sont ces étapes que j'ai à faire:
deviceController.create(device);
net.setDeviceId(device);
device.getNetworkInterfaceCollection().add(net);
netController.create(net);
Pourquoi dois-je créer l'enfant lorsque le parent de la classe sait qu'il est enfant et qu'il doit créer il pour moi?
La méthode de création de DeviceJpaController (désolé pour les noms longs dans les champs, ils sont générés automatiquement).
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();
}
}
}
source d'informationauteur Aris F.
Vous devez vous connecter pour publier un commentaire.
J'ai enfin compris la logique qui sous-tend la persistance de un à plusieurs entités. Le processus est le suivant:
Avec le code:
Maintenant, je peux trouver un appareil (avec l'id par exemple) et je peux obtenir tous les enfants à l'aide de
Dans l'appareil classe d'entité que j'ai posté dans la question, il n'est pas nécessaire pour les méthodes
addNetworkInterface
etremoveNetwokrInterface
.@Dima K est correct dans ce qu'ils disent. Lorsque vous faites cela:
De la collection dans le dispositif n'a pas été initialisé et ainsi, vous obtenez un NPE lorsque vous essayez d'ajouter à elle. Dans votre
Device
classe, lors de la déclaration de votreCollection
vous pouvez également initialiser:Comme pour la persistance, vos hypothèses sont correctes, mais je pense que l'exécution est faux. Lorsque vous créez votre appareil, de le rendre persistant avec JPA (faire la gestion des transactions où c'est nécessaire).
Faire de même pour les cartes réseau:
Maintenant depuis vos deux entités sont persistantes, vous pouvez en ajouter un à l'autre.
device.getNetworkInterfaceCollection().add(net);
JPA doit prendre soin de tout le reste, sans avoir à appeler n'importe quel autre persiste.
C'est un comportement connu de la collecte de données des membres.
La solution la plus simple est de modifier votre collection de lecture paresseusement de créer la collection.
Aussi, n'oubliez pas de consulter ces données pour membres seulement par le biais de la méthode de lecture.
Cette exception signifie que vous essayez de localiser une entité (probablement par em.getReference()) qui n'a pas encore été persisté.
Vous ne pouvez pas vous em.getReference() ou em.find() sur les entités qui n'ont toujours pas de PK.
Afin de permettre à économiser de capacité sur un @OneToMany relation, par exemple,
Ensuite, vous avez à dire à vos @ManyToOne relation qu'il est autorisé à mettre à jour myTable comme ceci pouvant être mis à jour = true