Spring Data Rest:目标 bean 不是持久实体的类型
Spring Data Rest: Target bean is not of type of the persistent entity
我有一个名为 Project
的数据模型,它是通过 Spring Data Rest 从 Angular 应用程序中获取生命周期的。 Project
有 Scene
s,场景有 UpstreamKey
s 保存在 Map<Integer, UpstreamKey>
和 UpstreamKey
是一个抽象 class 有两个实现 ChromaKey
和 LumaKey
。 (见下面的代码)
当我编辑 (PUT
) 和地图中带有 ChromaKey
的现有 Project
,并将其更改为 LumaKey
时,我在后端:
Caused by: java.lang.IllegalArgumentException: Target bean of type io.mewald.notime.designer.model.scene.usk.LumaKey is not of type of the persistent entity (io.mewald.notime.designer.model.scene.usk.chroma.ChromaKey)!: io.mewald.notime.designer.model.scene.usk.LumaKey
at org.springframework.util.Assert.instanceCheckFailed(Assert.java:702) ~[spring-core-5.3.10.jar:5.3.10]
at org.springframework.util.Assert.isInstanceOf(Assert.java:621) ~[spring-core-5.3.10.jar:5.3.10]
at org.springframework.data.mapping.model.BasicPersistentEntity.verifyBeanType(BasicPersistentEntity.java:584) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.getPropertyAccessor(BasicPersistentEntity.java:458) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.<init>(DomainObjectReader.java:639) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:141) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeMaps(DomainObjectReader.java:429) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeMaps(DomainObjectReader.java:418) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.access[=11=]0(DomainObjectReader.java:65) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.doWithPersistentProperty(DomainObjectReader.java:673) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:374) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:143) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeCollections(DomainObjectReader.java:469) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeCollections(DomainObjectReader.java:452) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.access0(DomainObjectReader.java:65) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.doWithPersistentProperty(DomainObjectReader.java:675) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:374) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:143) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.readPut(DomainObjectReader.java:116) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyPut(JsonPatchHandler.java:100) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate(PersistentEntityResourceHandlerMethodArgumentResolver.java:234) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
... 90 common frames omitted
我的解释是,它试图以某种方式将 LumaKey
合并到 ChromaKey
中,当然,这是行不通的。为什么不简单地替换 bean?有什么办法可以解决这个问题?
这里是与这个问题相关的数据模型的结构:
@NoArgsConstructor @Data @SuperBuilder @AllArgsConstructor
@Document
public class Project {
@Id
private String id;
...
private List<Scene> scenes;
}
@NoArgsConstructor @Data @SuperBuilder @AllArgsConstructor @EqualsAndHashCode(callSuper = false)
public class Scene {
...
private Map<Integer, UpstreamKey> upstreamKeys;
}
@Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ChromaKey.class, name = "ChromaKey"),
@JsonSubTypes.Type(value = LumaKey.class, name = "LumaKey")
})
public abstract class UpstreamKey {
...
public abstract String getType();
}
@Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
public class ChromaKey extends UpstreamKey {
...
@Override
public String getType() {
return "ChromaKey";
}
}
@Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
public class LumaKey extends UpstreamKey {
...
@Override
public String getType() {
return "LumaKey";
}
}
的文档
The PUT method replaces the state of the target resource with the supplied request body.
文档明确讨论了 replace
,所以我想知道为什么会涉及一个名为 mergeForPut
的方法。
编辑:刚刚意识到我的 spring-boot-starter-parent
有点老了。我更新到 2.6.4
但错误仍然存在。
编辑:我只是重构了所有内容,以便 scene.upstreamKeys
可以是 List
而不是 Map
以防万一这可能导致它。但事实并非如此。还是一样的错误。
编辑:根据要求,我创建了一个最小项目来复制我在上面描述的错误:https://github.com/mathias-ewald/demo-sdr-target-bean-is-not-of-type
虽然我可以在spring data mongodb 中实现更新,但现在在spring data rest 中是不可能的。在当前的 spring 数据剩余实现中,PUT 请求尝试通过调用 DomainObjectReader.mergeForPut(...)
来合并嵌套集合成员,但由于类型不匹配而失败,而不是替换它。看到这个 github issue and pull request.
所以解决方法是自己实现一个控制器。
编辑 2022-04-05
用@Immutable
注释UpstreamKey
使spring数据休息执行替换而不是合并。此解决方案仅在嵌套集合成员只是值(没有 id、没有审计数据、没有 @JsonIgnore 来隐藏服务器端数据)而不是实体(带有 id)时才有效。
找到 updated project and the diff
下面是put操作后的结果mongo数据。
{
_id: ObjectId('624b9fe3f32185579ff56849'),
name: 'Project 1',
scenes: [
{
name: 'Scene 1',
usks: [
{
c: 3,
a: 1,
_class: 'com.example.demo.LumaKey'
}
]
}
],
_class: 'com.example.demo.Project'
}
您很快想要在 LumaKey 和 ChromaKey 之间进行类型转换。所以 Java.
不行
你可以查看这个答案。我调试了你的代码,这就是你想要的。
编辑 1
您尝试删除 ChromaKey 并添加 LumaKey。
编辑 2
请调试这个
PersistentEntityResourceHandlerMethodArgumentResolver.java(第 158 行 [readPutForUpdate 方法])
在此方法中请求 json 和 mongo json 反序列化。 Object Mapper 映射它们。
当 mongodb 对象的 usks 字段类型为 ChromaKey 但您的请求类型为 LumaKey。
然后spring调用mapper方法进行映射
mapper.readerFor(target.getClass()).readValue(source);
对于此行,目标类型是 ChromaKey,但源类型是 LumaKey。
因此映射器无法将您的 ChromaKey 转换为 LumaKey。
我尝试编写自定义 JsonDeserializer,但它没有解决您的问题。也许你可以这样试试
我觉得是逻辑问题
我有一个名为 Project
的数据模型,它是通过 Spring Data Rest 从 Angular 应用程序中获取生命周期的。 Project
有 Scene
s,场景有 UpstreamKey
s 保存在 Map<Integer, UpstreamKey>
和 UpstreamKey
是一个抽象 class 有两个实现 ChromaKey
和 LumaKey
。 (见下面的代码)
当我编辑 (PUT
) 和地图中带有 ChromaKey
的现有 Project
,并将其更改为 LumaKey
时,我在后端:
Caused by: java.lang.IllegalArgumentException: Target bean of type io.mewald.notime.designer.model.scene.usk.LumaKey is not of type of the persistent entity (io.mewald.notime.designer.model.scene.usk.chroma.ChromaKey)!: io.mewald.notime.designer.model.scene.usk.LumaKey
at org.springframework.util.Assert.instanceCheckFailed(Assert.java:702) ~[spring-core-5.3.10.jar:5.3.10]
at org.springframework.util.Assert.isInstanceOf(Assert.java:621) ~[spring-core-5.3.10.jar:5.3.10]
at org.springframework.data.mapping.model.BasicPersistentEntity.verifyBeanType(BasicPersistentEntity.java:584) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.getPropertyAccessor(BasicPersistentEntity.java:458) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.<init>(DomainObjectReader.java:639) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:141) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeMaps(DomainObjectReader.java:429) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeMaps(DomainObjectReader.java:418) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.access[=11=]0(DomainObjectReader.java:65) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.doWithPersistentProperty(DomainObjectReader.java:673) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:374) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:143) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeCollections(DomainObjectReader.java:469) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeCollections(DomainObjectReader.java:452) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.access0(DomainObjectReader.java:65) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader$MergingPropertyHandler.doWithPersistentProperty(DomainObjectReader.java:675) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:374) ~[spring-data-commons-2.5.5.jar:2.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.lambda$mergeForPut(DomainObjectReader.java:143) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.mergeForPut(DomainObjectReader.java:139) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.json.DomainObjectReader.readPut(DomainObjectReader.java:116) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyPut(JsonPatchHandler.java:100) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate(PersistentEntityResourceHandlerMethodArgumentResolver.java:234) ~[spring-data-rest-webmvc-3.5.5.jar:3.5.5]
... 90 common frames omitted
我的解释是,它试图以某种方式将 LumaKey
合并到 ChromaKey
中,当然,这是行不通的。为什么不简单地替换 bean?有什么办法可以解决这个问题?
这里是与这个问题相关的数据模型的结构:
@NoArgsConstructor @Data @SuperBuilder @AllArgsConstructor
@Document
public class Project {
@Id
private String id;
...
private List<Scene> scenes;
}
@NoArgsConstructor @Data @SuperBuilder @AllArgsConstructor @EqualsAndHashCode(callSuper = false)
public class Scene {
...
private Map<Integer, UpstreamKey> upstreamKeys;
}
@Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ChromaKey.class, name = "ChromaKey"),
@JsonSubTypes.Type(value = LumaKey.class, name = "LumaKey")
})
public abstract class UpstreamKey {
...
public abstract String getType();
}
@Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
public class ChromaKey extends UpstreamKey {
...
@Override
public String getType() {
return "ChromaKey";
}
}
@Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @SuperBuilder(toBuilder = true)
public class LumaKey extends UpstreamKey {
...
@Override
public String getType() {
return "LumaKey";
}
}
的文档
The PUT method replaces the state of the target resource with the supplied request body.
文档明确讨论了 replace
,所以我想知道为什么会涉及一个名为 mergeForPut
的方法。
编辑:刚刚意识到我的 spring-boot-starter-parent
有点老了。我更新到 2.6.4
但错误仍然存在。
编辑:我只是重构了所有内容,以便 scene.upstreamKeys
可以是 List
而不是 Map
以防万一这可能导致它。但事实并非如此。还是一样的错误。
编辑:根据要求,我创建了一个最小项目来复制我在上面描述的错误:https://github.com/mathias-ewald/demo-sdr-target-bean-is-not-of-type
虽然我可以在spring data mongodb 中实现更新,但现在在spring data rest 中是不可能的。在当前的 spring 数据剩余实现中,PUT 请求尝试通过调用 DomainObjectReader.mergeForPut(...)
来合并嵌套集合成员,但由于类型不匹配而失败,而不是替换它。看到这个 github issue and pull request.
所以解决方法是自己实现一个控制器。
编辑 2022-04-05
用@Immutable
注释UpstreamKey
使spring数据休息执行替换而不是合并。此解决方案仅在嵌套集合成员只是值(没有 id、没有审计数据、没有 @JsonIgnore 来隐藏服务器端数据)而不是实体(带有 id)时才有效。
找到 updated project and the diff
下面是put操作后的结果mongo数据。
{
_id: ObjectId('624b9fe3f32185579ff56849'),
name: 'Project 1',
scenes: [
{
name: 'Scene 1',
usks: [
{
c: 3,
a: 1,
_class: 'com.example.demo.LumaKey'
}
]
}
],
_class: 'com.example.demo.Project'
}
您很快想要在 LumaKey 和 ChromaKey 之间进行类型转换。所以 Java.
不行你可以查看这个答案。我调试了你的代码,这就是你想要的。
编辑 1 您尝试删除 ChromaKey 并添加 LumaKey。
编辑 2
请调试这个 PersistentEntityResourceHandlerMethodArgumentResolver.java(第 158 行 [readPutForUpdate 方法])
在此方法中请求 json 和 mongo json 反序列化。 Object Mapper 映射它们。
当 mongodb 对象的 usks 字段类型为 ChromaKey 但您的请求类型为 LumaKey。
然后spring调用mapper方法进行映射
mapper.readerFor(target.getClass()).readValue(source);
对于此行,目标类型是 ChromaKey,但源类型是 LumaKey。
因此映射器无法将您的 ChromaKey 转换为 LumaKey。
我尝试编写自定义 JsonDeserializer,但它没有解决您的问题。也许你可以这样试试
我觉得是逻辑问题