在Spring中为Web应用程序创建一个restful api非常简单。 假设我们有一个电影实体,名称,年份,类型列表和演员列表。为了以json格式返回所有电影的列表,我们只需在某个控制器中创建一个方法,该方法将查询数据库并将列表作为ResponseEntity的主体返回。 Spring将神奇地序列化它,并且一切都很好:)
但是,如果我在某些情况下希望将电影中的演员列表序列化,而不是在其他场景中呢?在其他一些情况下,除了电影类的字段外,我还需要为列表中的每部电影添加一些其他属性,哪些值是动态生成的?
我目前的解决方案是在某些字段上使用@JsonIgnore,或者创建一个MovieResponse类,其中包含Movie类中的字段和所需的其他字段,并且每次都要从Movie转换为MovieResponse类。
有更好的方法吗?
答案 0 :(得分:2)
JSONIgnore注释的要点是告诉DispatcherServlet(或Spring处理呈现响应的任何组件)忽略某些字段(如果这些字段为null或以其他方式省略)。
这可以为您在某些情况下向客户端公开的数据提供一些灵活性。
向JSONIgnore下行:
然而,使用我最近在自己的项目中遇到的这个注释有一些缺点。这主要适用于PUT方法和控制器序列化数据的对象与用于在数据库中存储该数据的对象相同的情况。
PUT方法意味着您要么在服务器上创建新集合,要么使用您正在更新的新集合替换服务器上的集合。
在服务器上替换集合的示例:
想象一下,你正在向服务器发出PUT请求,而RequestBody包含一个序列化的Movie实体,但是这个Movie实体不包含actor,因为你已经省略了它们!稍后,您将实现一项新功能,允许您的用户编辑和更正电影描述中的拼写错误,并使用PUT将Movie实体发送回服务器,然后更新数据库。
但是,让我们说 - 因为你将JSONIgnore添加到你的对象已经很久了 - 你忘记了某些字段是可选的。在客户端,您忘记包含演员集合,现在您的用户不小心用演员B,C和D覆盖了电影A,电影A没有任何演员!
为什么JSONIgnore选择加入?
理所当然地,迫使您选择不再需要某些字段的目的正是为了避免这些类型的数据完整性问题。在您不使用JSONIgnore的世界中,您保证您的数据永远不会被部分数据替换,除非您明确地自己设置该数据。使用JSONIgnore,您可以删除这些安全措施。
据说,JSONIgnore非常有价值,我自己也以同样的方式使用它来减少发送给客户端的有效负载的大小。但是,我开始重新考虑这个策略,而是选择在单独的层中使用POJO类将数据发送到前端而不是用于与数据库交互的方法。
可能更好的设置?:
理想的设置 - 根据我处理此特定问题的经验 - 是为您的Entity对象而不是setter使用构造函数注入。强制自己必须在实例化时传递每个参数,以便您的实体永远不会被部分填充。如果你试图部分填充它们,编译器会阻止你做一些你可能会后悔的事情。
要将数据发送到客户端,您可能希望省略某些数据,可以使用单独的,断开连接的实体POJO,或使用org.json中的JSONObject。
当从客户端向服务器发送数据时,您的前端实体对象会从模型数据库层接收部分或全部数据,因为您并不真正关心前端是否获得部分数据。但是,当将数据存储在数据存储区中时,您首先要从数据存储区中获取已存储的对象,更新其属性,然后将其存储回数据存储区。换句话说,如果您错过了演员,那么无关紧要,因为您从数据存储区更新的对象已经将角色分配给它的属性。 因此,您只需替换明确要替换的字段。
虽然这个设置会有更多的维护开销和复杂性,但你会获得一个强大的优势:Java编译器会让你回来!它不会让你或者甚至不幸的同事做代码中可能危及数据存储区中数据的任何内容。如果您尝试在模型层中动态创建实体,则将被迫使用构造函数,并强制提供所有数据。如果您没有所有数据并且无法实例化该对象,那么您将需要传递空值(这应该向您发出红色标记)或首先从数据存储区中获取该数据。
答案 1 :(得分:0)
我遇到了这个问题,并且真的想继续使用@JsonIgnore,但也使用了实体/ POJO来在JSON调用中使用。
经过大量挖掘后,我想出了在对象映射器的每次调用中自动从数据库中检索被忽略字段的解决方案。
当然,这个解决方案需要一些要求。就像你必须使用存储库一样,但就我而言,这只是我需要的方式。
为此,您需要确保截获MappingJackson2HttpMessageConverter中的ObjectMapper,并填写标有@JsonIgnore的字段。因此,我们需要自己的MappingJackson2HttpMessageConverter bean:
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter)converter).setObjectMapper(objectMapper());
}
}
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new FillIgnoredFieldsObjectMapper();
Jackson2ObjectMapperBuilder.json().configure(objectMapper);
return objectMapper;
}
}
我们自己的objectMapper将每个JSON请求转换为一个对象,它通过从存储库中检索它们来填充被忽略的字段:
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter)converter).setObjectMapper(objectMapper());
}
}
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new FillIgnoredFieldsObjectMapper();
Jackson2ObjectMapperBuilder.json().configure(objectMapper);
return objectMapper;
}
}
在所有情况下,也许不是最干净的解决方案,但就我而言,它只是按照我希望的方式行事。