0

質問:

EntityManager外国の実体を再挿入しようとせずにマージする方法を知っている人はいますか?

シナリオ:

私のケースにぴったり一致するシナリオを設定するためだけに:私には2つのエンティティがあります

@Entity
@Table(name = "login", catalog = "friends", uniqueConstraints =
@UniqueConstraint(columnNames = "username"))
public class Login implements java.io.Serializable{

   private static final long serialVersionUID = 1L;
   @Id
   @GeneratedValue(strategy = IDENTITY)
   @Column(name = "id", unique = true, nullable = false)
   private Integer id;
   @Column(name = "username", unique = true, nullable = false, length = 50)
   private String username;
   @Column(name = "password", nullable = false, length = 250)
   private String password;
}

@Entity
@Table(name = "friendshiptype", catalog = "friends")
public class FriendshipType implements java.io.Serializable{

   private static final long serialVersionUID = 1L;
   @Id
   @GeneratedValue(strategy = IDENTITY)
   @Column(name = "id", unique = true, nullable = false)
   private Integer id;
   @OneToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "username")
   private Login login;
      @Column(name = "type", unique = true, length = 32)
   private String type;
   ...//other fields go here
}

Loginエンティティとエンティティの両方がFriendshipType別々にデータベースに永続化されます。次に、後で、Login行を行とマージする必要がありFriendshipTypeます。を呼び出すとentityManager.merge(friendship)、新しいものを挿入しようとしLoginますが、もちろん次のエラーが発生します

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'myUserName1350319637687' for key 'username'
Error Code: 1062
Call: INSERT INTO friends.login (password, username) VALUES (?, ?)

繰り返しになりますが、私の質問は、enityManagerが外部オブジェクトを再挿入しようとせずに2つのオブジェクトをマージするにはどうすればよいですか?

4

4 に答える 4

5

これが私が問題を解決する方法です。最後に、マージが解決されない理由は、login.id が JPA によって自動生成されるためだと考えています。自動生成された id フィールドは本当に必要ないので、スキーマから削除してフィールドusernameとして使用します。@id

@Entity
@Table(name = "login", catalog = "friends", uniqueConstraints =
@UniqueConstraint(columnNames = "username"))
public class Login implements java.io.Serializable{

   private static final long serialVersionUID = 1L;
   @Id
   @Column(name = "username", unique = true, nullable = false, length = 50)
   private String username;
   @Column(name = "password", nullable = false, length = 250)
   private String password;
}

私が実装しなかったが、自動生成された id フィールドが必要な場合は、他の誰かを助けるかもしれない別の解決策が思い浮かびました。

Login合併のために のインスタンスを作成する代わりに、データベースからインスタンスを取得します。つまり、代わりに

Login login = new Login(); login.setUsername(username); login.setPassword(password);

むしろする

Login login = loginDao.getByUsername(username);

そうすれば、新しい id フィールドが生成されず、エンティティが異なるように見えます。

助けてくれたみんな、特に @mijer がとても辛抱強くしてくれたことに感謝し、賛成票を投じます。

于 2012-10-15T22:14:06.650 に答える
2

@JoinColumn更新不可にすることができます:

@JoinColumn(name = "login_id", updatable = false) // or
@JoinColumn(name = "username", referencedColumnName = "username", updatable= false) 

Loginまたは、エンティティをマージする前に、エンティティを再度更新/フェッチしてみてくださいFriendshipType:

// either this
entityManager.refresh(friendship.getLogin());
// or this
final Login login = entityManager
          .getReference(Login.class, friendship.getLogin().getId());
friendship.setLogin(login);
// and then
entityManager.merge(friendship);

しかし、他の人が示唆したように、それは関係によって、またはおそらくEmbeddableまたはElementCollectionFriendshipTypeによってよりよく表現されると信じています@ManyToOne


アップデート

さらに別のオプションは、所有側を変更することです。

public class Login implements java.io.Serializable {
    @OneToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "friendshiptype_id")
    private FriendshipType friendshipType;
    // Other stuff 
}

public class FriendshipType implements java.io.Serializable {
    @OneToOne(fetch=FetchType.LAZY, mappedBy="friendshipType")
    private Login login;
    // Other stuff 
}

これはデータ モデルに影響しますが (ログイン テーブルにはfriendshiptype_id列があり、その逆ではありません)、関係は常に所有側で維持されるため、発生しているエラーを防ぐことができます。

于 2012-10-15T21:41:38.053 に答える
1

試しましたcascade=MERGEか?いえ

@OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
@JoinColumn(name = "username")
private Login login;

アップデート

別の可能なオプションは使用することです@ManyToOne(関連付けが一意であるため保存されます)

@ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
@JoinColumn(name = "username")
private Login login;
于 2012-10-15T20:31:46.393 に答える
0

元の @Id 設定でそれを行うことができます。すなわち

 @Id
 @GeneratedValue(strategy = IDENTITY)
 @Column(name = "id", unique = true, nullable = false)
 private Integer id;

できますが、次のように変更する必要はありません。

 @Id
 @Column(name = "username", unique = true, nullable = false, length = 50)
 private String username;

秘訣は、em.find(...) または em.createQuery(...) を介して、DB からロードすることから始めなければならないということです。その後、ID には DB からの正しい値が入力されることが保証されます。

次に、トランザクションを終了するか (セッション Bean のトランザクション スコープのエンティティ マネージャの場合)、em.detach(ent) または em.clear() を呼び出すか、エンティティをシリアル化して通信網。

その後、元の id 値を維持しながら、エンティティを更新できます。

次に、 em.merge(ent) を呼び出すことができ、正しい ID を取得できます。ただし、この時点でエンティティ マネージャーの永続的なコンテキストにエンティティが既に存在している必要があると思います。そうしないと、(手動で入力された ID を持つ) 新しいエンティティがあると見なされ、トランザクションのフラッシュ/コミットで INSERT が試行されます。

したがって、2 番目のトリックは、マージの時点でエンティティが確実に読み込まれるようにすることです (元のコンテキストではなく新しい永続コンテキストがある場合は、em.find(...) または em.query(...) を介して)。 .

:-)

于 2012-10-16T02:20:20.350 に答える