OneToOne-Relation with Spring Boot Rest API and JPA => attempted to assign id from null one-to-one 属性
OneToOne-Relation with Spring Boot Rest API and JPA => attempted to assign id from null one-to-one property
在我的实际项目中,我使用 Angular 6 进行前端开发,使用 Spring Boot 2 进行后端开发。后端使用 Spring Boot JPA 连接到 Postgres 数据库,我也使用 Spring Boot Rest 创建 API,使用 Spring Boot Rest 创建的默认端点。所以没有手工服务或控制器。
我有一个具有一对一关系的个人实体和地址实体。所有实体都扩展了一个 BaseEntity:
@Data
@MappedSuperclass
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@EntityListeners({AuditingEntityListener.class})
public class BaseEntity {
@Id
@GeneratedValue(generator = "uuid2", strategy = GenerationType.AUTO)
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "uuid-binary")
private UUID id;
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@CreatedBy
private String createdBy;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@LastModifiedBy
private String lastModifiedBy;
}
人
@Data
@Entity
@Table(name = "person")
public class Person extends BaseEntity {
private String lastName;
private String firstName;
}
地址
@Data
@Entity
@Table(name = "address")
public class Address extends BaseEntity {
@OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
@MapsId
private Person person;
private String street;
private String houseNumber;
private String zipCode;
private String city;
private String state;
}
我的存储库如下所示:
public interface AddressRepository extends CrudRepository<Address, UUID> {
}
和
public interface PersonRepository extends CrudRepository<Person, UUID> {
}
当 posting
{
"street":"exStreet",
"housenumber":"121",
"zipcode":"14321",
"city":"exCity",
"state":"exState",
"person":{
"lastName":"ExampleLastName",
"firstName":"ExampleFirstName"}
}
到http://localhost:8080/addresses我总是得到以下错误
org.springframework.orm.jpa.JpaSystemException: attempted to assign id
from null one-to-one property [Address.person];
连同
org.hibernate.id.IdentifierGenerationException: attempted to assign id
from null one-to-one property [Address.person]
看起来服务器没有保留个人实体,尽管没有用于保留地址实体的 ID。
我尝试实现这一点:
前端:angular 表单 => 服务 => 使用显示的 json => post 调用 httpclient 到 spring 启动 rest api 服务器// 可行,我认为
后端:spring启动休息api服务器,反序列化json到一个地址和一个人实体=>address.setPerson(人)=>通过jpa保存到数据库
有没有办法通过对 jackson 或其他东西进行一些定制来实现这一点,或者我是否必须再次编写服务和控制器,注意链接,...?
我现在已经为此苦苦挣扎了两天,所以非常感谢各种帮助。
编辑
经过进一步调查,我确信问题出在映射上。但是我不知道如何注释我的实体或需要进行哪些配置。
好的,我终于成功了。问题确实是映射。
我实现了一个自定义反序列化器,并在地址实体上使用 @JsonDeserialize(using = AddressDeserializer.class) 注册了它。见下文。
@Component
public class AddressDeserializer extends StdDeserializer<Address> {
public AddressDeserializer() {
super(Address.class);
}
@Override
public Address deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode addressNode = jsonParser.getCodec().readTree(jsonParser);
Person person = new Person();
person.setLastName(addressNode.get("person").get("lastName").textValue());
person.setFirstName(addressNode.get("person").get("firstName").textValue());
System.out.println("##### ----- person.getLastName: " + person.getLastName());
Address address = new Address();
address.setPerson(person);
address.setStreet(addressNode.get("street").textValue());
address.setHouseNumber(addressNode.get("housenumber").textValue());
address.setZipCode(addressNode.get("zipcode").textValue());
address.setCity(addressNode.get("city").textValue());
address.setState(addressNode.get("state").textValue());
return address;
}
}
地址实体:
...
@JsonDeserialize(using = AddressDeserializer.class)
public class Address extends BaseEntity implements Serializable {
...
IntelliJ IDEA 内置 HTTP 客户端:
POST http://localhost:8080/addresses
内容类型:application/hal+json
接受:/
缓存控制:无缓存
{
"street":"exStreet",
"housenumber":"121",
"zipcode":"14321",
"city":"exCity",
"state":"exState",
"person":{
"lastName":"ExampleLastName",
"firstName":"ExampleFirstName"}
}
结果地址:
POST http://localhost:8080/addresses
HTTP/1.1 201
Last-Modified: Sat, 27 Oct 2018 13:50:16 GMT
Location: http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 27 Oct 2018 13:50:16 GMT
{
"id": "89f57064-b4b4-4e1a-adae-142d8c09d834",
"createdDate": "2018-10-27T13:50:16.803+0000",
"createdBy": "testsystem",
"lastModifiedDate": "2018-10-27T13:50:16.803+0000",
"lastModifiedBy": "testsystem",
"street": "exStreet",
"houseNumber": "121",
"zipCode": "14321",
"city": "exCity",
"state": "exState",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"address": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"person": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834/person"
}
}
}
Response code: 201; Time: 225ms; Content length: 693 bytes
结果人:
{
"id" : "89f57064-b4b4-4e1a-adae-142d8c09d834",
"createdDate" : "2018-10-27T13:50:16.808+0000",
"createdBy" : "testsystem",
"lastModifiedDate" : "2018-10-27T13:50:16.808+0000",
"lastModifiedBy" : "testsystem",
"lastName" : "ExampleLastName",
"firstName" : "ExampleFirstName",
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"person" : {
"href" : "http://localhost:8080/persons/89f57064-b4b4-4e1a-adae-142d8c09d834"
}
}
}
数据库:
SELECT * FROM ADDRESS;
CREATED_BY CREATED_DATE LAST_MODIFIED_BY LAST_MODIFIED_DATE CITY HOUSE_NUMBER STATE STREET ZIP_CODE PERSON_ID
testsystem 2018-10-27 15:50:16.803 testsystem 2018-10-27 15:50:16.803 exCity 121 exState exStreet 14321 89f57064b4b44e1aadae142d8c09d834
SELECT * FROM PERSON;
ID CREATED_BY CREATED_DATE LAST_MODIFIED_BY LAST_MODIFIED_DATE FIRST_NAME LAST_NAME
89f57064b4b44e1aadae142d8c09d834 testsystem 2018-10-27 15:50:16.808 testsystem 2018-10-27 15:50:16.808 ExampleFirstName ExampleLastName
就是这样。感谢阅读。
在我的实际项目中,我使用 Angular 6 进行前端开发,使用 Spring Boot 2 进行后端开发。后端使用 Spring Boot JPA 连接到 Postgres 数据库,我也使用 Spring Boot Rest 创建 API,使用 Spring Boot Rest 创建的默认端点。所以没有手工服务或控制器。
我有一个具有一对一关系的个人实体和地址实体。所有实体都扩展了一个 BaseEntity:
@Data
@MappedSuperclass
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@EntityListeners({AuditingEntityListener.class})
public class BaseEntity {
@Id
@GeneratedValue(generator = "uuid2", strategy = GenerationType.AUTO)
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "uuid-binary")
private UUID id;
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@CreatedBy
private String createdBy;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@LastModifiedBy
private String lastModifiedBy;
}
人
@Data
@Entity
@Table(name = "person")
public class Person extends BaseEntity {
private String lastName;
private String firstName;
}
地址
@Data
@Entity
@Table(name = "address")
public class Address extends BaseEntity {
@OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
@MapsId
private Person person;
private String street;
private String houseNumber;
private String zipCode;
private String city;
private String state;
}
我的存储库如下所示:
public interface AddressRepository extends CrudRepository<Address, UUID> {
}
和
public interface PersonRepository extends CrudRepository<Person, UUID> {
}
当 posting
{
"street":"exStreet",
"housenumber":"121",
"zipcode":"14321",
"city":"exCity",
"state":"exState",
"person":{
"lastName":"ExampleLastName",
"firstName":"ExampleFirstName"}
}
到http://localhost:8080/addresses我总是得到以下错误
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [Address.person];
连同
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [Address.person]
看起来服务器没有保留个人实体,尽管没有用于保留地址实体的 ID。 我尝试实现这一点:
前端:angular 表单 => 服务 => 使用显示的 json => post 调用 httpclient 到 spring 启动 rest api 服务器// 可行,我认为
后端:spring启动休息api服务器,反序列化json到一个地址和一个人实体=>address.setPerson(人)=>通过jpa保存到数据库
有没有办法通过对 jackson 或其他东西进行一些定制来实现这一点,或者我是否必须再次编写服务和控制器,注意链接,...? 我现在已经为此苦苦挣扎了两天,所以非常感谢各种帮助。
编辑 经过进一步调查,我确信问题出在映射上。但是我不知道如何注释我的实体或需要进行哪些配置。
好的,我终于成功了。问题确实是映射。 我实现了一个自定义反序列化器,并在地址实体上使用 @JsonDeserialize(using = AddressDeserializer.class) 注册了它。见下文。
@Component
public class AddressDeserializer extends StdDeserializer<Address> {
public AddressDeserializer() {
super(Address.class);
}
@Override
public Address deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode addressNode = jsonParser.getCodec().readTree(jsonParser);
Person person = new Person();
person.setLastName(addressNode.get("person").get("lastName").textValue());
person.setFirstName(addressNode.get("person").get("firstName").textValue());
System.out.println("##### ----- person.getLastName: " + person.getLastName());
Address address = new Address();
address.setPerson(person);
address.setStreet(addressNode.get("street").textValue());
address.setHouseNumber(addressNode.get("housenumber").textValue());
address.setZipCode(addressNode.get("zipcode").textValue());
address.setCity(addressNode.get("city").textValue());
address.setState(addressNode.get("state").textValue());
return address;
}
}
地址实体:
...
@JsonDeserialize(using = AddressDeserializer.class)
public class Address extends BaseEntity implements Serializable {
...
IntelliJ IDEA 内置 HTTP 客户端: POST http://localhost:8080/addresses 内容类型:application/hal+json 接受:/ 缓存控制:无缓存
{
"street":"exStreet",
"housenumber":"121",
"zipcode":"14321",
"city":"exCity",
"state":"exState",
"person":{
"lastName":"ExampleLastName",
"firstName":"ExampleFirstName"}
}
结果地址:
POST http://localhost:8080/addresses
HTTP/1.1 201
Last-Modified: Sat, 27 Oct 2018 13:50:16 GMT
Location: http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 27 Oct 2018 13:50:16 GMT
{
"id": "89f57064-b4b4-4e1a-adae-142d8c09d834",
"createdDate": "2018-10-27T13:50:16.803+0000",
"createdBy": "testsystem",
"lastModifiedDate": "2018-10-27T13:50:16.803+0000",
"lastModifiedBy": "testsystem",
"street": "exStreet",
"houseNumber": "121",
"zipCode": "14321",
"city": "exCity",
"state": "exState",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"address": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"person": {
"href": "http://localhost:8080/addresses/89f57064-b4b4-4e1a-adae-142d8c09d834/person"
}
}
}
Response code: 201; Time: 225ms; Content length: 693 bytes
结果人:
{
"id" : "89f57064-b4b4-4e1a-adae-142d8c09d834",
"createdDate" : "2018-10-27T13:50:16.808+0000",
"createdBy" : "testsystem",
"lastModifiedDate" : "2018-10-27T13:50:16.808+0000",
"lastModifiedBy" : "testsystem",
"lastName" : "ExampleLastName",
"firstName" : "ExampleFirstName",
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/89f57064-b4b4-4e1a-adae-142d8c09d834"
},
"person" : {
"href" : "http://localhost:8080/persons/89f57064-b4b4-4e1a-adae-142d8c09d834"
}
}
}
数据库:
SELECT * FROM ADDRESS;
CREATED_BY CREATED_DATE LAST_MODIFIED_BY LAST_MODIFIED_DATE CITY HOUSE_NUMBER STATE STREET ZIP_CODE PERSON_ID
testsystem 2018-10-27 15:50:16.803 testsystem 2018-10-27 15:50:16.803 exCity 121 exState exStreet 14321 89f57064b4b44e1aadae142d8c09d834
SELECT * FROM PERSON;
ID CREATED_BY CREATED_DATE LAST_MODIFIED_BY LAST_MODIFIED_DATE FIRST_NAME LAST_NAME
89f57064b4b44e1aadae142d8c09d834 testsystem 2018-10-27 15:50:16.808 testsystem 2018-10-27 15:50:16.808 ExampleFirstName ExampleLastName
就是这样。感谢阅读。