【问题标题】:org.hibernate.exception.ConstraintViolationException on POST request in ManyToOne Bidirectional relationship多对一双向关系中 POST 请求的 org.hibernate.exception.ConstraintViolationException
【发布时间】:2020-05-07 19:44:47
【问题描述】:

我正在尝试在 /addNewUser 端点上使用以下 POST 请求创建新用户:-

发布请求

{
    "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

@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 类:-

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

最初,我认为我的 post 请求或 Jackson API 的反序列化存在问题,因此我打印了控制器方法中的 UserModel 字段的值,并且所有值都已正确设置,包括 AuthoritiesModel。但是当我尝试持久化 UserModel 对象时,它会抛出异常。

更新 1:

如果我在没有权限的情况下发布请求,它不会引发任何异常,但我会得到权限为 null 作为响应。

发布请求

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

发布回复

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

【问题讨论】:

    标签: java spring hibernate jpa spring-data-jpa


    【解决方案1】:

    当请求正文被反序列化时,AuthoritiesModel 上没有设置UserModel

    你可以试试这样的:

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

    【讨论】:

    • 是的,我已经尝试过这种方法。但它不起作用。正如我在问题中已经提到的那样,我也认为这是反序列化问题,但后来我在控制台上打印了 UserModel 字段,它们被完美设置,包括 List 和 AuthoritiesModel 中的 UserModel。我认为我的映射存在一些问题,或者这就是 JPA 无法成功保存 AuthoritiesModel(子)实体的原因。
    • @JsonManagedReference@JsonBackReference 是否在正确的字段中?根据这个例子,它们应该被颠倒。 baeldung.com/…
    • 注释是正确的,因为在我的例子中,这种关系的所有者是 UserModel,一个用户可以有多个与之关联的权限。因此,作为托管参考的是 UserModel。此外,当我尝试获取所有用户及其权限时,这有助于解决无限递归问题。如果您反转注释,它将在反序列化请求时引发异常: - 反向引用类型(java.util.List)与托管类型(com.test.testing.model.Authorities)不兼容
    【解决方案2】:

    在我开始回答之前,让我先弄清楚我没有遇到的明显问题:-

    首先,我认为 hibernate 使用代理包装代码,Jackson 绑定代码也使用反射,而不是调用传统的 getter 和 setter。此外,Hibernate 本质上使用标志来通过查看通过 getter 和 setter 调用了哪些方法来确定对象是否已被触摸,一对多映射的字段似乎不是因为它们是通过反射设置的。因此,我尝试将一些简单的 system.out.printlns 放入设置器中,并看到它们实际上正在被调用。所以这不是我的问题。

    其次,我使用单元测试,不做POST请求,直接调用服务层中的方法,看看是否发生了对数据库的写入。但它没有。

    所以,答案很明显:-

    服务层应该调用两个存储库层对象。服务层对象应该是一对多的存储库对象。这是标准做法,也是跨 Web 服务实施时经常使用的做法。

    但是我不明白为什么我们需要添加一个单独的调用来添加已经在我们的实体及其映射中定义的东西。我不确定我是否做错了什么或理解错了什么,但我觉得hibernate应该足够聪明来处理这种情况。理想情况下,它应该首先持久化父对象,然后使用我们的映射从数据库中获取详细信息并根据它持久化子对象。但是这里的问题是您要求它知道对象中的活动不在数据库中,应该创建并绑定到该 UserModel 并且似乎级联应该这样做,但它不会。

    【讨论】:

      猜你喜欢
      • 2019-07-18
      • 1970-01-01
      • 2019-01-29
      • 2018-10-26
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 2018-01-13
      相关资源
      最近更新 更多