Spring Data Rest:目标bean不是持久实体的类型
我有一个名为 Project
的数据模型,它是通过 Spring Data Rest 从 Angular 应用程序进行生命周期的。 Project
有 Scene
,场景有 UpstreamKey
保存在 Map
和 >UpstreamKey
是一个抽象类,具有两个实现 ChromaKey
和 LumaKey
。 (参见下面的代码)
当我在地图中使用 ChromaKey
编辑 (PUT
) 和现有 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$1(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$6(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$000(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$1(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$7(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.access$100(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$1(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.
该文档明确讨论了替换 所以我想知道为什么会涉及一个名为
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
I have a data model called Project
which is lifecycled from an Angular app via Spring Data Rest. The Project
has Scene
s, Scenes have UpstreamKey
s held in a Map<Integer, UpstreamKey>
and UpstreamKey
is an abstract class with two implementations ChromaKey
and LumaKey
. (see code below)
When I edit (PUT
) and existing Project
with a ChromaKey
in the map, and change that to a LumaKey
, I get an error message in the backend:
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$1(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$6(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$000(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$1(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$7(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.access$100(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$1(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
My interpretation is that it is somehow trying to merge the LumaKey
into the ChromaKey
and - of course - that doesn't work. Why is it not simply replacing the bean? What would be a fix to get this working?
Here's the structure of the data model relevant to this questions:
@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";
}
}
EDIT: Taken from the documentation at https://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.item-resource
The PUT method replaces the state of the target resource with the supplied request body.
The documentation explicitly talks about replace
so I am wondering why a method called mergeForPut
is involved at all.
EDIT: Just realised my spring-boot-starter-parent
was a bit old. I updated to 2.6.4
but the error remains.
EDIT: I just refactored everything so that scene.upstreamKeys
can be a List
instead of a Map
just in case this might cause it. But it does not. Still the same error.
EDIT: As requested, I created a minimum project that replicates the error I am describing above: https://github.com/mathias-ewald/demo-sdr-target-bean-is-not-of-type
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
虽然我可以在 Spring Data MongoDB 中实现更新,但现在在 Spring Data Rest 中不可能实现。在当前的 Spring Data Rest 实现中,PUT 请求尝试通过调用
DomainObjectReader.mergeForPut(...)
来合并嵌套集合成员,这会因类型不匹配而失败,而不是替换它。请参阅此 github 问题 和 拉取请求。所以解决方法是自己实现一个控制器。
编辑 2022-04-05
用
@Immutable
注解UpstreamKey
使 Spring Data Rest 执行替换而不是合并。仅当嵌套集合成员只是值(没有 id、没有审核数据、没有 @JsonIgnore 来隐藏服务器端数据)而不是实体(带有 id)时,此解决方案才有效。找到更新后的项目和diff
下面是 put 操作后的结果 mongo 数据。
Though I can achieve the update in spring data mongodb, it's not possible in spring data rest now. In current spring data rest implementation, a PUT request try to merge the nested collection member by calling
DomainObjectReader.mergeForPut(...)
which fails on type mismatch, rather than replace it. See this github issue and pull request.So the workaround is implement a controller yourself.
Edit 2022-04-05
Annotate
UpstreamKey
with@Immutable
make spring data rest perform replacement rather than merge. This solution only works if the nested collection member is just value (no id, no audit data, no @JsonIgnore to hide server side data) instead of entity (with id).Find the updated project and the diff
Below is result mongo data after the put opertaion.
很快您想要在 LumaKey 和 ChromaKey 之间进行类型转换。所以用Java不行。
你可以检查一下这个答案。我调试你的代码,这就是你想要的。
https://stackoverflow.com/a/17004028/8909313
编辑 1
您尝试删除 ChromaKey 并添加 LumaKey。
编辑2
请调试此
PersistentEntityResourceHandlerMethodArgumentResolver.java(第158行[readPutForUpdate方法])
在这个方法中request json和mongo json反序列化。对象映射器映射它们。
当 mongodb 对象的 usks 字段的类型是 ChromaKey 但您的请求的类型是 LumaKey 时。
然后spring调用mapper方法进行映射。
对于该行,目标的类型是 ChromaKey,但源的类型是 LumaKey。
因此映射器无法将您的 ChromaKey 转换为 LumaKey。
我尝试编写自定义 JsonDeserializer 但它没有解决您的问题。也许你可以尝试这个方法。
我认为这是一个逻辑问题。
Shortly you want to do type casting between LumaKey and ChromaKey. So it's not ok with Java.
You can check this answer. I debug your code and this is what you want.
https://stackoverflow.com/a/17004028/8909313
Edit 1
You try to delete ChromaKey and add LumaKey.
Edit 2
Please debug this
PersistentEntityResourceHandlerMethodArgumentResolver.java (line 158 [readPutForUpdate method])
In this method request json and mongo json deserialize. And Object Mapper map them.
When mongodb object's usks field's type is ChromaKey but your request's type LumaKey.
And then spring call mapper method for mapping.
for this line target's type is ChromaKey but source's type is LumaKey.
So mapper cannot cast your ChromaKey to LumaKey.
I try to write custom JsonDeserializer but it didn't solve your problem. Maybe you can try this way.
I think it's a logical problem.