Как сохранить отношение @ManyToMany - повторяющаяся запись или отдельная сущность - PullRequest
15 голосов
/ 22 марта 2012

Я хочу сохранить свою сущность с отношением ManyToMany.Но у меня есть некоторые проблемы во время постоянного процесса.

Мои объекты:

@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

ПервыйВремя, когда я пытаюсь сохранить пользователя с правами пользователя "ROLE_USER", не проблема.Вставлены пользовательские и пользовательские роли и таблицы объединения.

Моя проблема заключается в том, что я пытаюсь сохранить второго пользователя с такими же пользовательскими ролями.Я проверяю, существуют ли UserRoles, найдя его ( userRolesServices.getUserRoleByName (...) ).Если существует -> добавить эти UserRoles в список пользователей (id + имя роли), иначе я создаю новый (только имя роли).

Когда я пытаюсь сохранить второго пользователя, я получаю следующее исключение: «отдельная сущность для сохранения: ..... UserRoles» (возможно, потому что getUserRoleByName выполняется в другой транзакции)

Если я не использую getUserRoleByName (только *новые UserRoles ("ROLE_USER"); *), я получаю следующее исключение: "... ConstraintViolation: Дублированная запись для 'ROLE_NAME' ..."

Итак, как правильносохранить сущность с @ ManyToMany отношением?

Ответы [ 3 ]

23 голосов
/ 24 марта 2012

Для вышеуказанной проблемы я бы сказал, что ваш каскад отношений сущностей неправильный. Учтите это: у пользователя может быть несколько ролей, но в системе может существовать фиксированное количество ролей. Поэтому CASCADE ALL из User сущности не имеет никакого смысла, поскольку жизненный цикл UserRoles не должен зависеть от User жизненного цикла сущности. Например. когда мы удаляем User, UserRoles не должно быть удалено.

отсоединенная сущность для сохранения исключение произойдет, только если вы передаете объект, для которого первичный ключ уже настроен на сохранение.

Удалите каскад, и теперь ваша проблема будет решена, единственное, что вам нужно будет решить, - это как вы будете вставлять роли пользователя. По моему мнению, для этого должен быть отдельный функционал.

Также не используйте ArrayList, используйте HashSet. ArrayList разрешает дублирование.

8 голосов
/ 30 марта 2016

Я предоставлю свой ответ, если у кого-то возникнут проблемы со мной и автором.

В основном я столкнулся с ситуацией, когда у меня был один стол, который был своего рода ПОСТОЯННЫХ значений.И другой изменится, но он должен отобразить (many to many) на эти CONSTANTS .

Точная проблема USERS и ROLES.

Diagram

Roles будут известны и добавлены при запуске системы, поэтому они никогда не должны удалять .Даже если ни у одного пользователя не было бы Role, оно должно быть в системе .

Реализация класса с использованием JPA:

Пользователь :

@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 the 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.

Что и почему * Атрибут 1061 *

  • mappedBy добавляется к дочернему объекту, как описано в JPA Docs

Если вы решите отобразить отношения в обоих направлениях, то одно направление должно быть определено как владелец , а другое должно использовать атрибут mappedBy, чтобы определить его mapping (...)

Каскадная операция EntityManager.merge ().Если merge () вызывается для родителя, то дочерний объект также будет объединен.Обычно это следует использовать для зависимых отношений.Обратите внимание, что это влияет только на каскадирование объединения, сама ссылка на связь всегда будет объединена.

0 голосов
/ 22 марта 2012

(возможно, потому что getUserRoleByName выполняется в другой транзакции)

Это может показаться проблемой, выполнить запрос в той же диспетчере транзакций / сущностей.В противном случае повторно найдите его в текущей транзакции, используя find ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...