エンティティ「Car」と「Pakete」の間に多対多の関係があり、両端から削除して、それらの間のリンクを削除しようとしています。
public Pakete removeAllLinksFromPacket(int packetId) {
Pakete p = em.find(Pakete.class, packetId);
if ( null != p ) {
for(Car c : p.getCars() ) {
c.getPakets().remove(p);
// em.merge(c);
}
p.getCars().clear();
// em.merge(p);
// em.flush();
}
return p;
}
上記のコードを実行すると、現在のセッションの永続化コンテキスト内でリンクが削除されますが、変更はデータベースに同期されません。その後、セッションを停止して再起動すると、リンクが再び表示されます:-(。コメントアウトされた em.merge() および em.flush()es は、必死の試みにすぎません。
サーバーはグラスフィッシュ 3.1.2 です
エンティティは次のようになります。
@Table(name="cars")
public class Car implements Serializable {
@EmbeddedId
private CarPK id;
//bi-directional many-to-many association to Pakete
@ManyToMany
@JoinTable(
name="cars_pakete"
, joinColumns={
@JoinColumn(name="uid", referencedColumnName="uid"),
@JoinColumn(name="car_name", referencedColumnName="name"),
}
, inverseJoinColumns={
@JoinColumn(name="paket_id")
}
)
private Set<Pakete> paketes;
public Car() {
}
public CarPK getId() {
return this.id;
}
public void setId(CarPK id) {
this.id = id;
}
// ... other members getter/setter removed
}
と
@Table(name="pakete")
public class Pakete implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
// ... some member variables removed
//bi-directional many-to-many association to Car
@ManyToMany(mappedBy="paketes")
private Set<Car> cars;
public Pakete() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public Set<Car> getCars() {
return this.cars;
}
public void setCars(Set<Car> cars) {
this.cars = cars;
}
// ... some getter/setter removed
}
既存の DB があり、Eclipse jpa ツールを使用して上記のエンティティを生成しました。
何が欠けていますか。回避策として、結合テーブルでネイティブ クエリを実行できます。しかし、私は誰かがここで何が起こっているのかを説明してくれるという希望を捨てていません.
前もって感謝します。
コードを次のように変更しました。
public Pakete removeAllLinksFromPacket(int packetId) {
Pakete p = em.find(Pakete.class, packetId);
if ( null != p ) {
List<Car> lc = new ArrayList<Car>();
lc.addAll(p.getCars());
if ( lc.isEmpty() )
throw new NullPointerException("no links");
for(Car c : lc ) {
if ( !c.getPaketes().contains(p) )
throw new NullPointerException("no packet");
c.getPaketes().remove(p);
if ( !p.getCars().contains(c) )
throw new NullPointerException("no car");
p.getCars().remove(c);
}
} else
throw new NullPointerException("No packet found");
return p;
}
念のため、アサートのような例外をいくつか追加しました。何の手がかりも与えませんでした。
あるユースケースでは、最初にすべてのリンクを削除してから新しいリンクを追加する別の関数によって関数自体が呼び出されます。次のようになります。
@Override
public void mapCarsToPacket(int packetId,
List<String> carNames) {
// 1. remove all existing links
Pakete p = eao.removeAllLinksFromPacket(packetId);
// 2. add the new links
for (String cn : carNames ) {
List<Car> cars = eao.getCarsWithName(cn);
for(Car c : cars ) {
p.getCars().add(c);
c.getPaketes().add(p);
}
}
}
したがって、この関数はトランザクション内で実行され、実行すると、古いパケットはデータベースから削除されませんが、新しいリンクが挿入されます。データベースには、新しいリストと古いリストが含まれます。リストが重複している場合 (たとえば、新しいリストに既にリンクされている車が含まれている場合)、一意の制約違反が原因で DBException が発生します。
ただし、パケット自体が次のように削除された場合:
public void deletePacketAndAllItsLinks(int id) {
Pakete p = removeAllLinksFromPacket(id);
if ( null != p ) {
em.remove(p);
}
}
不思議なことに、結合テーブル内のパケットとそのすべてのリンクが削除されます。
唖然...
とりあえず:あきらめます!これは本当にひどいですが、うまくいきます:
public Pakete removeAllLinksFromPacket(int packetId) {
em.flush();
em.clear();
em.createNativeQuery("DELETE FROM cars_pakete WHERE paket_id=?").setParameter(1, packetId).executeUpdate();
Pakete p = em.find(Pakete.class, packetId);
if ( null != p ) {
List<Car> lc = new ArrayList<Car>();
lc.addAll(p.getCars());
for(Car c : lc ) {
c.getPaketes().remove(p);
p.getCars().remove(c);
}
}
return p;
}
私は永続化コンテキストを自分で同期させています。これは確かにJPAの精神ではなく、私は本当に嫌いです。しかし、私は自分のプロジェクトを進めることができます。助けようとした人に感謝します。JPA準拠のソリューションにつながる将来の提案は大歓迎です。