【问题标题】:Serialize model without relation序列化没有关系的模型
【发布时间】:2017-05-09 14:18:02
【问题描述】:

我有两个模型:

@Entity
class ModelA {
  ...
  @OneToMany(mappedBy = "modelA")
  @JsonManagedReference
  private List<ModelB> modelBs = new ArrayList<ModelB>();
  ...
}

@Entity
class ModelB {
  ...
  @ManyToOne
  @JoinColumn(name = "modela_id")
  @JsonBackReference
  private ModelA modela;
  ...
}

还有一个用于 ModelA 的 REST/Resource 控制器,它有一个列表方法:

@GetMapping("list")
public @ResponseBody ArrayList<ModelA> list() {
  ArrayList<ModelA> modelas = new ArrayList();

  for (ModelA moa : modelARepository.findAll()) {
    modelas.add(moa);
  }

  return modelas;
}

现在默认情况下,来自该控制器的响应将包含 ModelA 对象的 JSON 数组,但这些对象也将包含 ModelB。有没有办法选择您想要序列化的关系以及何时序列化?

来自 Django/Laravel,我将使用专用序列化解决此问题,例如,我将有一个 ModelAWithRelatiionsSerializer,我将在其中序列化 ModelA + 它的所有关系或 ModelAWithModelBSerializer,这将返回使用一级 ModelB 序列化的 ModelA 等.

我知道我可以忽略注释不包括它,但这真的不一样,我没有看到关于 Spring 包中的序列化。这样做的最佳或最佳方式是什么?

【问题讨论】:

  • 您可以使用视图 (spring.io/blog/2014/12/02/…) 或 DTO。
  • 必须明确分离关注点。在多个用例中使用相同的实体,并且只应公开相关数据。因此,使用 DTO 始终是最佳实践。

标签: java spring spring-mvc spring-boot jackson


【解决方案1】:

对于其他从 Laravel(PHP)、Django(Python)、Rails(Ruby) 迁移的人:

我创建了一个包serializers,其中我的每个模型都有一个序列化程序类。

public class UserSerializer {

  // Default user values
  public interface User {}

  // User values with users posts
  public interface UserWithPosts extends User {}  

}

现在在您的模型中,您要做的是注释它们所属的序列化器的字段:

@Entity  
class User {

  @Id
  @GeneratedValue
  @JsonView(UserSerializer.User.class)
  private Long id;

  @JsonView(UserSerializer.User.class)
  private String username;

  @JsonView(UserSerializer.User.class)
  private String email;

  @OneToMany(mappedBy = "user")
  @JsonManagedReference  // This prevents infinite loop  
  @JsonView(UserSerializer.UserWithPosts.class)  
  private List<Post> posts;

  // .. getters and setters bellow
}

在您的控制器中,您可以使用 @JsonView(..) 注释您的方法,以确定您要使用哪个序列化程序。

@RestController  
@RequestMapping("users")  
public class UsersController {

  private UserRepository userRepository;

  public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
  }  

  /**
   * In list method we only want users default values to keep the payload smaller.
   */
  @GetMapping("list")
  @JsonView(UserSerializer.User.class)  
  public @ResponseBody ArrayList<User> list() {
    ArrayList<User> users = new ArrayList<>();

    for (User user : userRepository.findAll()) {
      users.add(user);
    }

    return users;
  }


  /**
   * In this method we are only fetching a single user so payload is smaller so we can add the users posts as well. You could also toggle this with a url filter/parameter.  
   */
  @GetMapping("show")
  @JsonView(UserSerializer.UserWithPosts.class)  
  public @ResponseBody ArrayList<User> show(@RequestParam(name = "id", required = true) Long id) {
    return userRepository.findOne(id):
  }

}  

配置!这很容易理解,但是当我第一次这样做时,没有任何示例/教程告诉/显示您也需要更新配置。如果你不这样做,那么序列化你的关系就会出现问题,序列化器仍然可以工作,但是你会在 JSON 响应中得到类似的东西:

...
"posts": [
  {},
  {},
  {},..
]
...

所以它会为每个用户返回一个 JSON 对象,但没有值来解决这个问题我创建了 config 包添加了一个 WebConfiguration 类:

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().defaultViewInclusion(true).build();
        converters.add(new MappingJackson2HttpMessageConverter(mapper));
    }

}  

您也可以在 XML 配置中执行此操作。但我个人更喜欢使用基于 Java 类的配置。如果 Spring 文档为新手提供更好的解释,那就太好了。

【讨论】:

    【解决方案2】:

    取一个没有类似关系的模型

     $this->user = $user->withoutRelations();
    

    【讨论】:

      猜你喜欢
      • 2017-10-30
      • 2017-12-14
      • 1970-01-01
      • 2020-07-27
      • 2020-05-20
      • 2019-09-27
      • 1970-01-01
      • 2018-07-12
      • 1970-01-01
      相关资源
      最近更新 更多