无法在 Spring 数据 REST 请求中保存 OneToMany/ManyToOne 关系

Can't save a OneToMany/ManyToOne relationship in Spring Data REST request

我正在使用 Spring Data JPA 和 Spring Data Rest。 发出 REST 请求以保留实体时,出现下一个错误:

org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value

我的数据模型有以下实体:

合同:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
public class Contract implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            fetch = FetchType.LAZY,
            mappedBy="contract"
    )
    private List<Participation> participants = new ArrayList<Participation>();

    private String name;

}   

参与人数:

@Entity
public class Participation implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
        
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(nullable = false) //By default the column will be CONTRACT_ID
    private Contract contract;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(nullable = false)
    private Contact contact;

    private String clauses;
}

联系人:

@Entity
public class Contact implements Serializable {

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

    private String name;
    private String emailAddress;
}

我有 2 个 JPARepositories:

public interface ContractRepository extends JpaRepository<Contract, Long> {
    List<Contract> findByNameContainsIgnoreCase(String name);
}

public interface ContactRepository extends JpaRepository<Contact, Long> {
}

要保存一个有几个参与的新合同,我正在 Postman 中执行后续步骤:

  1. 创建合约并获取其 href:

请求:POST http://localhost:8080/api/contracts

正文:

{
   "name": "Contract1"
}

响应成功:

201 Created
{
  "name": "Contract1",
  "participants": [],
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/contracts/4"
    },
    "contract": {
      "href": "http://localhost:8080/api/contracts/4"
    },
  }
}
  1. 到目前为止一切顺利。现在我已经保留了合同,我正在添加参与者: 联系人 1 已存在于数据库中。

请求:补丁 http://localhost:8080/api/contracts/4

正文:

{
    "participants": [
        {
            "clauses": "Bla bla bla",
            "contact": {
            "href": "http://localhost:8080/api/contacts/1"
            },
            "contract": {
            "href": "http://localhost:8080/api/contracts/4"
            }
        }
    ]
}

执行此请求时,系统在 field/fk 合同上抱怨:

{
    "cause": {
        "cause": null,
        "message": "not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract"
    },
    "message": "not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract"
}

我尝试了几种方式在参与中引用合同,例如:

"contract": "http://localhost:8080/api/contracts/4"

运气不好。由于某种原因,系统将字段留空而不是使用在步骤 1 中创建的实体的外键。 我做错了什么?

问题可以通过以下方式解决:

  1. 添加一个新的存储库 ParticipationRepository(扩展 JpaRepository);
  2. 首先创建一个没有参与的合约:

POST http://localhost:8080/api/contracts { "name": "Contract1" }

回复:

201 Created
{
  "name": "Contract1",
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/contracts/3"
    },
    "contract": {
      "href": "http://localhost:8080/api/contracts/3"
    },
    "participants": {
      "href": "http://localhost:8080/api/contracts/3/participants"
    }
  }
}
  1. 创建一个参与并使用刚刚创建的合约中的 URI 来设置 FK。假设联系人 1 已经存在于数据库中。

POST http://localhost:8080/api/participations { "clauses": "bla, bla, bla", "contract": "http://localhost:8080/api/contracts/3", "contact": "http://localhost:8080/api/contacts/1" }

回复:

201 Created
{
  "clauses": "bla, bla, bla",
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/participations/5"
    },
    "participation": {
      "href": "http://localhost:8080/api/participations/5"
    },
    "contract": {
      "href": "http://localhost:8080/api/participations/5/contract"
    },
    "contact": {
      "href": "http://localhost:8080/api/contacts/5/contact"
    }
  }
}