Json 使用Spring Boot和Jackson避免两种不同的域模型

Json 使用Spring Boot和Jackson避免两种不同的域模型,json,hibernate,jpa,spring-boot,jackson,Json,Hibernate,Jpa,Spring Boot,Jackson,我正在设计一个由MySQL支持的SpringBootRESTAPI。我突然想到,实际上,我希望为我的所有域对象提供两个独立的模型: 模型1:在外部世界(REST客户端)和我的SpringREST控制器之间使用;及 模型2:Spring Boot应用程序和MySQL数据库之间内部使用的实体 例如,我可能有一个联系人表,用于保存个人/联系人信息: CREATE TABLE contacts ( contact_id BIGINT UNSIGNED NOT NULL AUTO_INCREMEN

我正在设计一个由MySQL支持的SpringBootRESTAPI。我突然想到,实际上,我希望为我的所有域对象提供两个独立的模型:

  • 模型1:在外部世界(REST客户端)和我的SpringREST控制器之间使用;及
  • 模型2:Spring Boot应用程序和MySQL数据库之间内部使用的实体
例如,我可能有一个
联系人
表,用于保存个人/联系人信息:

CREATE TABLE contacts (
  contact_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  contact_ref_id VARCHAR(36) NOT NULL,
  contact_first_name VARCHAR(100) NOT NULL,
  ...many more fields
);
相应的Spring/JPA/Hibernate实体可能如下所示:

// Groovy pseudo-code!
@Entity
class Contact {
  @Id
  @Column(name = "contact_id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Long id

  @Column(name = "contact_ref_id")
  UUID refId

  @Column(name = "contact_first_name")
  String firstName

  // ...etc.
}
如果我只有一个模型范例,那么当Jackson将一个
联系人
实例(可能从DB取回)序列化为JSON并发送回客户端时,他们会看到JSON如下所示:

{
  "id" : 45,
  "refId" : "067e6162-3b6f-4ae2-a171-2470b63dff00",
  "firstName" : "smeeb",
  ...
}
没有什么比向外界公开主键更重要的了!相反,我希望序列化的JSON省略
id
字段(以及其他字段)。另一个示例可能是查找/引用表,如
Colors

# Perhaps has 7 different color records for ROYGBIV
CREATE TABLE colors (
  color_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  color_name VARCHAR(20) NOT NULL,
  color_label VARCHAR(20) NOT NULL,
  color_hexcode VARCHAR(20) NOT NULL,

  # other stuff here
);
如果相应的
颜色
实体如下所示:

@Entity
class Color {
  @Id
  @Column(name = "color_id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Long id

  @Column(name = "color_name")
  String name

  @Column(name = "color_label")
  String label

  @Column(name = "color_hexcode")
  String hexcode

  // ...etc.
}
然后,仅使用一个模型,它将序列化为JSON,如下所示:

{
  "id" : 958,
  "name" : "Red",
  "label" : "RED",
  "hexcode" : "ff0000"
}
但也许我只是想让它作为一个简单的字符串值返回:

{
  "color" : "RED"
}

因此,在我看来,我要么需要两个独立的模型(以及在它们之间映射的映射器类),要么需要一种方法来注释我的实体,或者配置Spring、Jackson甚至Hibernate,以便在正确的时间对我的实体应用某些转换。这些框架是否提供了任何可以帮助我的东西,或者我必须在这里使用两个不同的域模型吗?

实际上,您只需一个模型就可以实现这一点,我认为这是最简单的方法,如果您只需查找隐藏字段、自定义格式,属性的简单转换等。有两个模型需要从一个模型转换到另一个模型,反之亦然,这是一种痛苦。Jackson提供了许多有用的注释,可用于自定义输出。下面列出了一些对您有用的注释

@JsonIgnore
-忽略字段/属性。可以使用此批注隐藏id字段

@JsonInclude
-可用于指定输出中应出现字段的时间。例如:如果字段为空,则该字段是否应出现在输出中

@JsonSerialize
-您可以为属性指定自定义序列化程序。例如:您有一个属性“password”,并且希望将密码输出为“**”

@JsonFormat
-您可以对字段应用自定义格式。如果您有日期/时间字段,这非常有用


@JsonProperty
-如果要在输出中为字段指定其他名称。例如:您的模型中有一个字段“name”,您希望在输出中将其显示为“userName”。

尽管可以使用注释和mixin控制Jackson序列化。对于DTO和实体,我总是有不同的类,对于它们的使用方式有不同的要求。如果不这样做,则必须处理诸如Hibernate惰性加载集合之类的无法序列化的问题,因为控制器方法退出后persistenceContext已关闭。如果使用Lombok,则创建DTO需要1分钟,并且可以根据需要在实体上创建任意多的DTO(视图)。