我在更新现有实体的单向外键字段时遇到问题。客户端发送包含完整实体数据(包括父实体和子实体)的 PUT 请求,但在保存时仅更新原始值,而不会更新关联实体的引用。
我想知道是否缺少注释或配置,或者我需要自己实现该功能,如果是,如何实现。我这么问是因为现有代码表明这应该可以开箱即用。
作为参考,我有一个较旧的代码库,最近从
升级我有以下简化的实体:
@Data
@Entity
@Audited
@EqualsAndHashCode(callSuper = true)
@Table(name = "PERSON")
// The listener listens for PrePersist and PreUpdate, but does not touch the problematic fields.
@EntityListeners(PersonListener.class)
public class Person {
@Id
@Column(name = "id", nullable = false, insertable = false, updatable = false)
private Long id;
@Column(name = "name", nullable = false)
private String name;
@ManyToOne(targetEntity = Country.class, fetch = FetchType.LAZY)
@JoinColumn(name = "birth_country_id", referencedColumnName = "id", foreignKey = @ForeignKey(name = "fk_birth_country"))
@RestResource(exported = false)
private Country birthCountry;
@ManyToMany(targetEntity = Address.class, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.LAZY)
@JoinTable(name = "person_address",
joinColumns = @JoinColumn(name = "person_id"),
inverseJoinColumns = @JoinColumn(name = "address_id")
)
private List<Address> addresses = new ArrayList<>();
// Other fields omitted for brevity.
}
birthCountry
和 addresses
都曾经有 @LazyToOne(value = LazyToOneOption.NO_PROXY)
注释。
@Data
@Entity
@Audited
@Table(name = "COUNTRY")
@EntityListeners(AuditingEntityListener.class)
public class Country implements Serializable {
@Id
@Column(name = "id", nullable = false, length = 19)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
private String name;
}
@Data
@Entity
@Audited
@Table(name = "ADDRESS")
@EqualsAndHashCode(callSuper = true)
public class Address {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(targetEntity = Country.class, fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "country_id", referencedColumnName = "id", nullable = false, foreignKey = @ForeignKey(name = "fk_country"))
private Country country;
@Column(name="street", nullable = false)
private String street;
}
对于
Person
和 Country
我有一个简单的 Rest 存储库,如下所示。 Address
没有存储库。
@RepositoryRestResource(collectionResourceRel = "person", path = "person")
public interface PersonRepository extends JpaRepository<Person, Long> {
}
例如,我有以下
Person
:
{
"id": 1,
"name": "John Smith",
"birthCountry": {
"id": 10,
"name": "United States"
},
"addresses": [{
"id": 100,
"country": {
"id": 10,
"name": "United States"
},
"name": "Wall Street"
}]
}
当用户选择一个新的国家/地区并修改地址时,我通过 PUT 从客户端收到以下 json。这就是我期望的最终结果:
{
"id": 1,
"name": "Oliver Smith",
"birthCountry": {
"id": 11,
"name": "Canada"
},
"addresses": [{
"id": 100,
"country": {
"id": 12,
"name": "Mexico"
},
"name": "Avenida Francisco I. Madero"
}]
}
调用 SimpleJpaRepository.save
,但仅合并原始值的变化。这是数据库中的最终结果:
{
"id": 1,
"name": "Oliver Smith",
"birthCountry": {
"id": 10,
"name": "United States"
},
"addresses": [{
"id": 100,
"country": {
"id": 10,
"name": "United States"
},
"name": "Avenida Francisco I. Madero"
}]
}
我可以在sql调试日志中看到Hibernate生成的更新语句包含
id=10
,而不是我从客户端收到的内容。SimpleJpaRepository.save
方法并查看调试器视图中的 Person
实体时,我在相关实体中看到了带有 id=null
的新原始值。但是,如果我评估查询相同内容的表达式,代码片段将返回旧值。
这里没有错误。根据 HATEOAS 风格,要更改
birthCountry
对象中的 Person
,您需要向 PUT
发送 /person/1/birthCountry
请求,并引用 Country
对象的地址:
PUT /person/1/birthCountry
Content-Type: text/uri-list
/country/11
有关更多信息,请参阅 Spring Data REST。