Как определить однонаправленный объект типа "один ко многим" в Spring jpa

Я несколько дней пытался решить эту проблему, но не могу понять. У меня есть 3 таблицы: пользователи, сотрудники (расширяет пользователей) и роли. У меня проблема в том, что я не могу найти способ заставить спящий режим понять, что мне нужен однонаправленный ко многим между пользователями и ролями. Я не хочу получать всех пользователей из таблицы ролей. В приведенной ниже конфигурации, когда я пытаюсь вставить новое значение в таблицу «Пользователи», вместо использования существующих значений из ролей он вставляет новое, создавая повторяющиеся значения с разными идентификаторами. Есть ли у вас какие-либо предложения? Спасибо. Ниже приведены классы, которые я использую:

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

@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.JOINED) // used for the Employees table
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
private String e_mail;
private String phoneNumber;
private String name;
private String surname;
private Date birthDate;
private String adress;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;

public User() { }

public User(String username, String password, String e_mail, String phoneNumber, String name,
            String surname, Date birthDate, String adress, Role role) {
    this.username = username;
    this.password = password;
    this.e_mail = e_mail;
    this.phoneNumber = phoneNumber;
    this.name = name;
    this.surname = surname;
    this.birthDate = birthDate;
    this.adress = adress;
    this.role = role;
}

//getters and setters
}

Ролевая сущность:

@Entity
@Table(name = "roles")
public class Role {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

public Role() { }

public Role(String name) {
    this.name = name;
}

//getters and setters
}

UserDto:

public class UserDto {

@JsonIgnore
private Long id;
private String username;
private String password;
private String e_mail;
private String phoneNumber;
private String name;
private String surname;
private Date birthDate;
private String adress;
@JsonIgnore
private RoleDto role = new RoleDto();

public UserDto() { }

// getters and setters
}

RoleDto:

public class RoleDto {

@JsonIgnore
private Long id;
private String name;

public RoleDto() { }

// getters and setters
}

UserService:

@Service
public class UserService {

private UserRepository userRepository;
private ModelMapper modelMapper = new ModelMapper();

public UserService(RoleService roleService, UserRepository userRepository, RoleRepository roleRepository) {
    this.roleService = roleService;
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
}

public void addUser(UserDto userDto) {
    userDto.setRole(roleService.getBuyerRoleDto());
    userRepository.save(modelMapper.map(userDto, User.class));
}

// other services...
}

RoleService:

@Service
public class RoleService {

private RoleRepository roleRepository;
private ModelMapper modelMapper = new ModelMapper();

public RoleService(RoleRepository roleRepository) {
    this.roleRepository = roleRepository;
}

/*
* i've tested the queries getById and getBuyerId and they are working as they should
*/
public RoleDto getBuyerRoleDto() {
    return modelMapper.map(roleRepository.getbyId(roleRepository.getBuyerId()), RoleDto.class);
}

// other services...
}

person trabantul    schedule 02.05.2020    source источник
comment
Можете ли вы отладить, какой User объект вы действительно сохраняете в БД? Вы устанавливаете @JsonIgnore в id полях, поэтому они могут быть пропущены modelMapper - так что userDto имеет roleDto без идентификатора в addUser - вот почему вы пытаетесь сохранить сущность User без идентификатора, содержащую сущность Role без идентификатора, тем самым создавая новую роль .   -  person Alex Rudenko    schedule 02.05.2020
comment
Я удалил @JsonIgnore во всех полях Dtos и только в roleDto, и результат кажется таким же. Для каждого пользователя, которого я вставляю, спящий режим вставляет новую строку в таблицу ролей. Он ведет себя так, как будто это зависимость один к одному.   -  person trabantul    schedule 02.05.2020


Ответы (3)


Вы должны предоставить атрибут @ManyToOne(optional = false), чтобы эта связь стала обязательной.

person Alex Rudenko    schedule 02.05.2020
comment
Он по-прежнему делает то же самое, вставляя новую строку в таблицу ролей. - person trabantul; 02.05.2020

Каскадный тип не требуется для роли. Измените приведенный ниже код с этого ....

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;

....к этому:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;
person Hemant    schedule 03.05.2020
comment
Если я удаляю каскад, я получаю: org.hibernate.TransientPropertyValueException: свойство Not-null ссылается на временное значение - временный экземпляр должен быть сохранен перед текущей операцией: com.example.project.model.User.role - ›com.example.project .model.Role; вложенное исключение - java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: свойство Not-null ссылается на временное значение - временный экземпляр должен быть сохранен перед текущей операцией: com.example.project.model.User.role - ›com.example. project.model.Role - person trabantul; 03.05.2020
comment
Я думаю, что одна проблема заключается в том, что вместо сопоставления с существующей моделью он создает новую. - person trabantul; 03.05.2020

Проблема в том, что ваш преобразователь модели создает экземпляр роли, вызывая конструктор. Такой объект является «новым» для JPA / Hibernate, т. Е. Неуправляемым. Чтобы сослаться на существующий объект, вам нужно использовать entityManager.getReference(Role.class, roleId) или entityManager.find(Role.class, roleId) и использовать этот объект в вашем экземпляре User.

Это очень похоже на эту проблему: При сохранении объекта, преобразованного из DTO, спящий режим выдает исключение TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр

Эта проблема с отображением модели, с которой вы столкнулись, является идеальным вариантом использования для Blaze -Persistence Entity Views.

Я создал библиотеку, чтобы обеспечить простое сопоставление между моделями JPA и пользовательским интерфейсом или моделями, определяемыми абстрактным классом, что-то вроде Spring Data Projection на стероидах. Идея состоит в том, что вы определяете свою целевую структуру (модель домена / dto) так, как вам нравится, и сопоставляете атрибуты (геттеры) через выражения JPQL с моделью сущности.

Представления сущностей также могут быть обновляемыми и / или созданными, т. Е. Поддерживать сброс изменений обратно.

@CreatableEntityView
@EntityView(User.class)
public abstract class UserDto {
  @JsonIgnore
  Long getId();
  String getUsername();
  void setUsername(String username);
  String getPassword();
  void setPassword(String password);
  String getEmail();
  void setEmail(String email);
  String getPhoneNumber();
  void setPhoneNumber(String phoneNumber);
  String getName();
  void setName(String name);
  String getSurname();
  void setSurname(String surname);
  Date getBirthDate();
  void setBirthDate(Date birthDate);
  String getAdress();
  void setAdress(String adress);
  @JsonIgnore
  RoleDto getRole();
  void setRole(RoleDto role);
}
@EntityView(Role.class)
public interface RoleDto {
  @JsonIgnore
  Long getId();
  String getName();
}

Сохранить новый объект очень просто, поскольку Blaze-Persistence Entity Views позаботится о сопоставлении графа объекта с моделью entity / db с помощью средств JPA для вас.

entityViewManager.save(entityManager, user);

Благодаря интеграции Spring Data и Spring Mvc использовать DTO очень просто и максимально быстро. См. Пример приложения здесь: https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-webmvc/src/main/java/com/blazebit/persistence/examples/spring/data/webmvc/controller/CatRestController.java#L80.

person Christian Beikov    schedule 03.05.2020