多対多の関係でエンティティを永続化したいと思います。しかし、私は永続化プロセス中にいくつかの問題があります。
私の実体:
@Entity
@Table(name = "USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long userId;
@Column(name = "NAME", unique = true, nullable = false)
String userName;
@Column(name = "FORNAME")
String userForname;
@Column(name = "EMAIL")
String userEmail;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "USER_USER_ROLES", joinColumns = @JoinColumn(name = "ID_USER"), inverseJoinColumns = @JoinColumn(name = "ID_ROLE"))
List<UserRoles> userRoles = new ArrayList<UserRoles>();
// getter et setter
}
そして
@Entity
@Table(name = "USER_ROLES")
public class UserRoles implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long userRolesId;
@Column(unique = true, nullable = false, name = "ROLE_NAME")
String roleName;
// getter et setter
}
サービスコード:
User user = new User();
UserRoles role;
try {
role = userRolesServices.getUserRoleByName("ROLE_USER"); // find jpql - transaction
} catch (RuntimeException e) {
LOGGER.debug("No Roles found");
role = new UserRoles("ROLE_USER"); // create new
}
user.addUserRole(role);
user.setUserName(urlId);
user.setUserForname(fullName);
user.setUserEmail(email);
userServices.createUser(user); // em.persist(user) - transaction
初めて、UserRoles "ROLE_USER"でユーザーを永続化しようとすると、問題ありません。 UserとUserRolesおよび結合テーブルが挿入されます。
私の問題は、同じUserRolesを持つ2番目のユーザーを永続化しようとしたときです。 UserRolesが存在するかどうかを確認します(userRolesServices.getUserRoleByName(...))。存在する場合->このUserRolesをユーザーリスト(ID +ロール名)に追加します。そうでない場合は、新しいユーザーロールを作成します(ロール名のみ)。
2番目のユーザーを永続化しようとすると、次の例外が発生します:"永続化するデタッチされたエンティティ:..... UserRoles"(おそらくgetUserRoleByNameが別のトランザクションで実行されるため)
getUserRoleByName(* new UserRoles( "ROLE_USER"); *のみ)を使用しない場合、次の例外が発生します:"。 ..ConstraintViolation: 'ROLE_NAME'の重複したエントリ... "
それで、@ ManyToMany関係でエンティティを適切に永続化する方法は?
上記の問題について、私はあなたの実体関連カスケードが間違っていると言うでしょう。これを考慮してください:ユーザーは複数の役割を持つことができますが、システムに存在できる役割の数は固定されている可能性があります。したがって、User
エンティティのライフサイクルはUserRoles
エンティティのライフサイクルに依存してはならないため、User
エンティティからのCASCADEALLは意味がありません。例えば。 User
を削除しても、UserRoles
は削除されません。
永続化するデタッチエンティティ例外は、主キーがすでに永続化するように設定されているオブジェクトを渡した場合にのみ発生します。
カスケードを削除すると、問題は解決されます。決定する必要があるのは、ユーザーロールをどのように挿入するかだけです。私によると、そうするための別個の機能があるはずです。
また、ArrayList
は使用せず、HashSet
を使用してください。 ArrayList
は重複を許可します。
誰かが私と作者に同じタイプの問題を抱えている場合、私は私の答えを提供します。
基本的に私が直面していたのは、ある種の[〜#〜]定数[〜#〜]値である1つのテーブルがある状況でした。もう一方は変更されますが、(_many to many
_)をそれらの[〜#〜]定数[〜#〜]にマップする必要があります。
正確な問題はUSERS
で、それはROLES
です。
Roles
は既知であり、システムの起動時に追加されるため、が削除されることはありません。 Role
を持つユーザーがいない場合でも、システム内に存在する必要があります。
ユーザー:
_@Entity
@Table(name = "USERS")
public class User{
@Id
private String login;
private String name;
private String password;
@ManyToMany(cascade = {CascadeType.MERGE})
private Set<Role> roles = new HashSet<>();
_
役割:
_@Entity
@Table(name = "ROLE")
public class Role {
@Id
@Enumerated(value = EnumType.STRING)
private RoleEnum name;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
_
この設定により、Role
をUser
に簡単に追加/削除できます。配列を渡すだけです。例:user.getRoles().add(new Role("ADMIN"));
およびmerge
user
。 削除は空のリストを渡すことで機能します。
ユーザーに追加する前にRole
を追加するのを忘れると、次のようなエラーが発生する可能性があります。
_javax.persistence.RollbackException: Java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: com.storage.entities.Role@246de37e.
_
mappedBy
属性が子エンティティに追加されます関係を両方向にマッピングすることを選択した場合、一方の方向を所有者として定義し、もう一方の方向をmappedBy属性を使用してそのマッピングを定義する必要があります。 (...)
cascade = {CascadeType.MERGE}
_が適切なカスケードに追加されました JPA DocsEntityManager.merge()操作をカスケードしました。親でmerge()が呼び出されると、子もマージされます。これは通常、依存関係に使用する必要があります。これはマージのカスケードにのみ影響し、関係参照自体は常にマージされることに注意してください。
(おそらくgetUserRoleByNameが別のトランザクションで実行されるため)
それは問題のように思われます。同じトランザクション/エンティティマネージャでクエリを実行してください。それ以外の場合は、find()を使用して現在のトランザクションで再検索します。