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.