12

設定

  • EcliplseLink 2.3.2
  • JPA2.0
  • エンティティは、データベースからのエンティティ クラス...ウィザードを使用して、netbeans の db スキーマから自動作成されます。
  • コントローラ クラスは、エンティティ クラスからの JPA コントローラ クラスを使用して netbeans から自動作成されます...ウィザード

質問の短いバージョン

古典的なシナリオでは、1 対多の関係を持つ 2 つのテーブル。親エンティティを作成してから子エンティティを作成し、子を親のコレクションにアタッチします。親エンティティを(コントローラ メソッドで)作成すると、子エンティティが作成され、親に関連付けられることが期待されます。なぜそれが起こらないのですか?

ロングバージョン

親クラス

@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
}

子クラス

@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;
    }
}

メインクラス

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);
    }
}

このクラスは、次の行で NullPointerException をスローします。device.getNetworkInterfaceCollection().add(net);

システムは、新しいエンティティがあり、そのコレクションにdevice要素があることを認識してnetいます。device私はそれがdbに書き込み、デバイスのIDを取得し、それを接続してdbに書き込むことを期待していnetました。

これの代わりに、これらが私がしなければならないステップであることがわかりました:

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

親クラスがそれが子であることを知っていて、それを作成する必要があるのに、なぜ子を作成する必要があるのですか?

DeviceJpaController からの create メソッド (フィールド内の長い名前については申し訳ありませんが、それらは自動生成されます)。

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

5 に答える 5

22

1 対多のエンティティを永続化する背後にあるロジックをようやく理解できました。プロセスは次のとおりです。

  1. 親クラスを作成する
  2. しつこく
  3. 子クラスを作成
  4. 子を親に関連付ける
  5. 子の永続化 (親コレクションが更新されます)

コード付き:

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     
    }
}

これで、(ID などを使用して) デバイスを見つけることができ、そのすべての子を使用して取得できます。

Collection<NetworkInterface> netCollection = device.getNetworkInterfaceCollection()

質問に投稿したデバイス エンティティ クラスでは、メソッドaddNetworkInterfaceremoveNetwokrInterface.

于 2013-01-04T08:01:56.530 に答える
4

@Dima Kは彼らの言うことで正しいです。これを行う場合:

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

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

デバイスのコレクションは初期化されていないため、追加しようとすると NPE が発生します。Deviceクラスで を宣言するときに、Collectionそれを初期化することもできます。

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

永続性に関しては、あなたの仮定は正しいですが、実行は間違っていると思います。デバイスを作成したら、すぐに JPA で永続化します (必要に応じてトランザクション管理を行います)。

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

NetworkInterface についても同じことを行います。

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

両方のエンティティが永続化されているため、一方を他方に追加できます。

device.getNetworkInterfaceCollection().add(net);

JPA は、他の永続化を呼び出さなくても残りを処理する必要があります。

于 2013-01-02T22:37:06.317 に答える
2

これは、コレクション データ メンバーの既知の動作です。最も簡単な解決策は、コレクションのゲッターを変更してコレクションを遅延作成することです。

@XmlTransient
public Collection<NetworkInterface> getNetworkInterfaceCollection() {
    if (networkInterfaceCollection == null) {
        networkInterfaceCollection = new Some_Collection_Type<NetworkInterface>();
    }
    return networkInterfaceCollection;
}

また、getter メソッドを介してのみこのデータ メンバーを参照することを忘れないでください。

于 2013-01-02T22:18:25.897 に答える
0

この例外は、まだ永続化されていないエンティティを (おそらく em.getReference() によって) 検索しようとしていることを意味します。まだ PK を持たないエンティティに対して em.getReference() または em.find() を実行することはできません。

于 2013-01-02T22:42:14.727 に答える