org.hibernate.exception.ConstraintViolationException при запросе POST в двусторонних отношениях ManyToOne - PullRequest
0 голосов
/ 30 апреля 2020

Я пытаюсь создать нового пользователя, используя следующий запрос POST для конечной точки / addNewUser : -

Запрос на публикацию

{
    "username"   : "abc",
    "password"   : "123",
    "enabled"    : "true",
    "authorities": [{ 
                        "authority" : "ROLE_USER" 
                    }]
}

UserController класс: -

@PostMapping("/addNewUser")
    public UserModel addNewUser(@RequestBody UserModel user) {
        return userService.addNewUser(user);
    }

UserService класс: -

public UserModel addNewUser(UserModel user) {
        return userRepository.saveAndFlush(user);
    }

UserRepository интерфейс

public interface UserRepository extends JpaRepository<UserModel, String> {

}

Но я получаю следующее исключение: -

2020-04-30 21:14:00.441 ERROR 23653 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: null value in column "authority" violates not-null constraint
  Detail: Failing row contains (null, null).
2020-04-30 21:14:00.450 ERROR 23653 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [authority]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

org.postgresql.util.PSQLException: ERROR: null value in column "authority" violates not-null constraint
  Detail: Failing row contains (null, null).

UserModel class

@Entity
@Table(name = "users")
public class UserModel {
    @Id
    @Column(name = "username", unique = true, nullable = false)
    private String username;

    @Column(name = "password")
    private String password;

    @Column(name = "enabled")
    private boolean enabled;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<AuthoritiesModel> authorities;

    //getters and setters
}

AuthoritiesModel class: -

@Entity
@Table(name = "authorities")
public class AuthoritiesModel implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name="username", unique = false, nullable=false)
    private UserModel user;

    @Id
    @Column(name = "authority") 
    private String authority;

    //getters and setters
}

Изначально я подумал, что возникла некоторая проблема с моим пост-запросом или десериализацией с помощью API Джексона, поэтому я распечатал значения полей UserModel в методе контроллера и все значения были установлены правильно, включая AuthoritiesModel. Но когда я пытаюсь сохранить объект UserModel, он выдает исключение.

Обновление 1:

Если я отправляю запрос без полномочий, он не выдает никаких исключений, но В ответ я получаю права доступа как нулевые.

Почтовый запрос

{
    "username"   : "abc",
    "password"   : "123",
    "enabled"    : "true"
}

Почтовый ответ

{
    "username": "abc",
    "password": "123",
    "enabled": true,
    "authorities": null
}

Ответы [ 2 ]

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

Прежде чем я начну отвечать, позвольте мне прояснить очевидную проблему, которой у меня не было: -

Сначала я подумал, что hibernate оборачивает код прокси-сервером, а код привязки Джексона также использует отражение, а не вызов традиционные добытчики и сеттеры. Кроме того, Hibernate по существу использует флаги, чтобы определить, был ли объект затронут, посмотрев, какие методы были вызваны через геттеры и сеттеры, поля для сопоставления один ко многим не выглядят так, потому что они были установлены посредством отражения. Поэтому я попытался поместить несколько простых system.out.printlns в сеттеры и увидел, что их на самом деле вызывают. Так что это не было проблемой в моем случае.

Во-вторых, я использовал модульный тест и не выполнял POST-запрос, а непосредственно вызывал метод на уровне сервиса и проверял, происходит ли запись в базу данных. Но это не так.

Итак, ответ был очевиден: -

Уровень обслуживания должен вызывать два объекта уровня репозитория. Объекты уровня сервиса должны быть объектами репозитория «один ко многим». Это стандартная практика, и она часто используется и при реализации через веб-сервисы.

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

0 голосов
/ 30 апреля 2020

У AuthoritiesModel не установлено UserModel, когда десериализовано тело запроса.

Вы можете попробовать что-то вроде:

public UserModel addNewUser(UserModel user) {
    List<AuthorityModel> authorities = user.getAuthorities();
    authorities.stream().forEach(authority -> authority.setUser(user);
    return userRepository.saveAndFlush(user);
}
...