我们正在使用AngularJS,Rest,JPA开发Web应用程序。我已经阅读了一些关于域实体不应该通过服务公开的文章。 我知道这是紧密耦合,可能存在循环引用,关注点分离,这似乎对我有用。 但后来我看到有关将jpa和jaxb映射应用于同一模型的文章,eclipseLink moxy就是一个例子。
然后有Spring数据REST通过rest api暴露jpa实体。(可能是Spring Data REST用于解决手头的不同问题)
所以我有点困惑。回答以下两个问题和场景,其中一个更好,另一个会有所帮助。
将jaxb和JPA注释同时应用于同一个域模型有什么好处?这样做主要是为了避免介于两者之间的DTO 层
只有在开发需要公开CRUD操作的应用程序并且实际上没有涉及其他业务功能的情况下才能使用Spring数据REST吗?
答案 0 :(得分:1)
使用DTO需要一定的开销:必须创建DTO类;在序列化之前,必须将实体类映射到DTO。
DTO也“感觉不对”,因为它们似乎违反了“不要重复自己”的原则。看一个实体和一个DTO并看到它们基本相同是令人沮丧的。
因此,直接公开JPA实体可以减少代码并减少处理代码时的心理开销。您的控制器,服务和存储库都处理相同的类。
但是有一些问题:
您的内部表示与您要向API用户公开的界面不匹配。
真实示例:分层文档,其中根文档为article
,子文档为sections
。在内部,每个文档都与父文档有关,可能为空。
通过REST接口直接公开此实体将要求客户端将id
(或HATEOAS中的url
)作为其提交实体的一部分发送,以正确链接文档。
更好的方法是在创建子部分时让api用户POST到/articles/{id}/sections/
。
公开内部数据。
您的实体可能包含标识符,磁盘上的路径等,这些内容并非真正供公众使用。序列化时,您必须@JsonIgnore
这些。反序列化时,您也可以手动填充实体。
性能。
与上述类似;也许你的实体包含一个大数据对象。您希望用户能够POST或PUT这些,但不应要求他们下载对象只是为了阅读实体的其他字段。
潜在的序列化问题。
延迟加载的集合是这里经典的失败案例。
比必要的更复杂的序列化表示。
真实世界示例:word
实体,其集合为synonyms
;同义词取决于单词。直接序列化实体将导致:
{ "id":12,
"word": "cat",
"synonyms": [
{ "id": 23,
"synonym": "kitty"
},
{ "id": 34,
"synonym": "pussycat"
}
]
}
更好的模式是:
{ "word": "cat",
"synonyms": [
"kitty",
"pussycat"
]
}
同一课程上的大量注释会降低清晰度。
根据您的实体复杂程度,您可能拥有JPA注释(@Entity
,@Id
,@GeneratedValue
,@Column
,@ManyToMany
,@MappedBy
等),XML注释(@XmlRootElement
,@XmlType
,@XmlElement
等),JSON注释(@JsonInclude
,@JsonIgnore
,@JsonProperty
等)和验证注释(@NotNull
,@Null
等)都在同一个类上。
由于所有这些问题,我通常使用DTO。我手动执行DTO映射实体,但有一个项目试图让这更容易,如Dozer。
我可以想象使用spring-data-rest
或直接暴露我的实体只在我有非常简单的实体,它们之间没有太多关系,并且实体模型和模型之间存在高度对应的情况下我希望向用户公开。