使用 Spring Boot 和 Jackson 避免两个不同的域模型
Posted
技术标签:
【中文标题】使用 Spring Boot 和 Jackson 避免两个不同的域模型【英文标题】:Avoiding two distinct domain models with Spring Boot and Jackson 【发布时间】:2017-09-02 01:07:29 【问题描述】:我正在设计一个由 mysql 支持的 Spring Boot REST API。我突然想到,我想要为我的所有域对象创建两个独立的模型:
模型 1:在外部世界(REST 客户端)和我的 Spring REST 控制器之间使用;和 模型 2:Spring Boot 应用和 MySQL 数据库之间内部使用的实体例如,我可能有一个 contacts
表用于保存个人/联系信息:
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 将 Contact
实例(可能从数据库中取回)序列化为 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
);
如果对应的Color
实体如下所示:
@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 以在我的实体上应用某些转换在正确的时间。这些框架是否提供了任何可以帮助我的东西,还是我必须在这里使用两个不同的领域模型?
【问题讨论】:
虽然可以使用注释和 Mixins 来控制 Jackson 序列化。我总是有不同的 DTO 和实体类,它们的使用方式只是不同的要求。如果您不这样做,您将不得不处理诸如 Hibernate 延迟加载集合之类的无法序列化的事情,因为在您的控制器方法退出后,persistenceContext 已关闭。如果您使用 Lombok,创建 DTO 需要 1 分钟,并且您可以在 Entity 上拥有任意数量的 DTO(视图)。 【参考方案1】:您实际上可以只使用一个模型来完成此操作,如果您只是在寻找隐藏字段、自定义格式、属性的简单转换等,我认为这是最简单的方法。拥有两个模型需要从一个模型转换到另一个模型,反之亦然- 反之亦然,这是一种痛苦。 Jackson 提供了许多有用的注释,可用于自定义输出。下面列出了一些可能对您有用的注释
@JsonIgnore
- 忽略字段/属性。您可以使用此注释隐藏您的 id 字段。
@JsonInclude
- 可用于指定字段何时应出现在输出中。例如:如果一个字段为空,是否应该出现在输出中
@JsonSerialize
- 您可以为属性指定自定义序列化程序。例如:您有一个属性“密码”,并且您想将密码输出为“****”。
@JsonFormat
- 您可以将自定义格式应用于字段。如果您有日期/时间字段,这将非常有用
@JsonProperty
- 如果您想为输出中的字段指定不同的名称。例如:您的模型中有一个字段“名称”,您希望在输出中将其显示为“用户名”。
【讨论】:
以上是关于使用 Spring Boot 和 Jackson 避免两个不同的域模型的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Boot 和 Jackson 的日期时区
使用 Spring Boot 和 Jackson 避免两个不同的域模型
使用 Spring Boot、Jackson 和 Hibernate 的多对多关系