JSON解析错误:处理一对一关系时无法构造“自定义对象”的实例

时间:2019-02-04 20:02:12

标签: java json hibernate spring-boot jpa

我正在使用Java和Spring Boot开发REST API。

有两个具有一对一关系的实体:

用户:

@Entity
@Table(name = "users")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "first_name", nullable = false)
    private String firstName;

    @Column(name = "last_name", nullable = false)
    private String lastName;

    @Column(name = "email", nullable = false, unique = true)
    private String email;

    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "employee")
    @JsonBackReference
    private Company company;

    // constructors, getters & setters...

公司:

@Entity
@Table(name = "companies")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Company {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "company_name", nullable = false)
    private String companyName;

    @Column(name = "company_info", nullable = false)
    private String companyInfo;

    @CreationTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at", nullable = false)
    private Date createdAt;

    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "employee_id", nullable = false)
    @JsonManagedReference
    private User employee;

   // constructors, getters & setters...

我希望能够通过 POST 方法创建公司,因此我已经在CompanyController中创建了一个公司(我按期望的方式在控制器中省略了GET方法):

@RestController
@RequestMapping(value = "/api/v1")
public class CompanyController {

    private static final Logger logger = LoggerFactory.getLogger(CompanyController.class);

    @Autowired
    private CompanyRepository companyRepository;

    @PostMapping(path = "company", produces = "application/JSON")
    public ResponseEntity<?> createCompany(@RequestBody Company company){
        logger.info("Request to save new Company: {}", company);

        Company result = companyRepository.save(company);
        return ResponseEntity.ok().body(result);
    }

当我使用JSON发送如下请求时,createCompany方法可以正常工作:

{
"companyName" : "Big Client Company 2",
"companyInfo" : "unspecified",
"employee" : {
    "id": 17,
    "firstName": "someName",
    "lastName": "someLastName",
    "email": "someEmail@a.ru"
  }
}

但是,我希望能够发送 整个员工字段但没有id的JSON:

{
"companyName" : "Big Client Company 2",
"companyInfo" : "unspecified",
"employee" : 17
}

当我这样做时,我会得到一个错误:

  

JSON解析错误:无法构造model.User的实例(尽管存在至少一个Creator):没有可以从Number值(17)反序列化的int / Int参数构造函数/工厂方法

因此,问题有什么办法可以做到而无需将公司类“雇员”更改为String(并摆脱一对一的关系)? / p>

我试图找到编写自定义JSON解串器的示例,但没有找到适合我的示例。

2 个答案:

答案 0 :(得分:0)

问题是您的用户在创建字段时必须提供必填字段(firstNamelastNameemail)。

如果没有使用现有定义的用户,则无法创建用户。

您可以在这些字段上使用nullable = true,但最终可能会导致要保留在数据库中的数据不一致。如果让这些字段为空,那么我建议采用这种方法。

此外,请注意,混合REST控制器层和存储库层通常是一种不好的做法。您应该通过服务层将这些层分开。

答案 1 :(得分:0)

您可以使用自定义反序列化:

public class CompanyDeserializer extends StdDeserializer<Company> { 

@Autowired
private UserRepository userRepository;

public CompanyDeserializer() { 
    this(null); 
} 

public CompanyDeserializer(Class<?> vc) { 
    super(vc); 
}

@Override
public Company deserialize(JsonParser jp, DeserializationContext ctxt) 
  throws IOException, JsonProcessingException {
    JsonNode node = jp.getCodec().readTree(jp);
    Company c = new Company();
    //Set the rest of the attributes.
    Long empId = (Long) ((IntNode) node.get("employee")).numberValue();
    c.setEmployee(userRepository.findById(empId).orElse(null)));
    return c;
}

}