Как определить однонаправленный один ко многим dto весной jpa - PullRequest
0 голосов
/ 02 мая 2020

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

Сущность пользователя:

@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
}

Служба пользователя:

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

Служба роли:

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

Ответы [ 3 ]

0 голосов
/ 03 мая 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;
0 голосов
/ 03 мая 2020

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

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

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

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

0 голосов
/ 02 мая 2020

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

...