我应该在REST请求和/或响应中使用JPA实体吗?
我应该在REST请求和/或响应中使用JPA实体吗?
我有一个情况,可以将JPA实体作为rest请求发送,也可以将JPA实体作为rest响应获取。\n
@RequestMapping(value = "/products", method = RequestMethod.POST) public @ResponseBody ProductDetailsResponse createNewProduct(@RequestBody ProductDetails newProduct) throws Exception {
\nProductDetails
是一个实体。\n
@Entity @Table(name = "product") public class ProductDetails {
\n我应该使用这种方式,还是将实体转换为其他类型的对象?
不,不要这样做。这与良好实践、花哨的模式或其他任何东西无关。
以下是原因:
一个JPA实体(如果我们谈论的是Hibernate)与一个Hibernate Session相关联。因此,Hibernate可能会产生一些意想不到的后果。让我们来看看:
1)Flush模式- Flush相当于SQL的更新操作,Hibernate会根据某些规则检查对象的“脏状态”,然后执行:
entityManager.flush();
您可能并不打算调用“sqlStatement.update”,但是我们仍然会执行它。
2)
`class EntityA{ // 默认是延迟加载 private SetentityBees }
如果我们从控制器中执行以下操作,并且Hibernate Session已关闭,则会出现诸如Detached entity等异常:
for (EntityB b : entityA.getEntityBees) { // 这是一个问题 process(b); }
再次强调,这不是因为一些花哨的GoF模式,而是因为它是危险的。特别是如果你不知道你在做什么的话。
在REST请求和/或响应中使用JPA实体的原因可以从以下几个方面来解释:
1. 技术角度上,使用实体作为响应是可以的。实体只需要能够被序列化为响应的输出格式(JSON或XML)即可。
2. 编写测试是一个好主意,可以创建完整的实体对象(所有字段都设置为非空),并尝试对其进行序列化。即使只有一个字段不可序列化,也会导致异常。您最好在测试期间发现这个问题,而不是在发布之后。
3. 在简单的情况下(CRUD应用程序)中,如果需要在响应中使用实体的每个字段,这是一个很好的选择。
4. 如果你不需要实体的每个字段在响应中,可以使用DTO(数据传输对象)来表示响应。
5. 如果响应与实体有显著不同(例如新增字段或转换),最好为响应创建单独的DTO对象。这样,您就可以单独演进Web API(DTO)和数据库模式(实体)。
是否在REST请求和/或响应中使用JPA实体取决于具体情况。如果需要完整的实体字段或者实体与响应之间有一致性的映射关系,那么可以直接使用实体作为响应。如果需要更多的灵活性或者有不同的需求,可以考虑使用DTO来表示响应。这样可以更好地管理和演进Web API和数据库模式。
在REST请求和/或响应中使用JPA实体是否合适的问题是因为以下原因而出现的:
1. 非常重要的一个原因是,将JPA实体用作DTO(数据传输对象)被认为是不好的实践。DTO是实体的轻量级版本,大小方面更小,并且具有其他优点。
2. 使用DTO可以避免与实体相关的无限循环和JSON转换问题。例如,在一对多的单向关系中,子实体将引用父实体,但在DTO中可以打破这种链条,以避免大量的JSON转换和问题。
3. 在DTO级别进行JSON到对象的转换(反之亦然)比在实体级别更容易,因为实体代表数据库图表,而不是客户端业务图表。
4. 可以使用一个简单的通用工具类来进行转换(从DTO到实体,反之亦然)。可以使用模型映射器API来进行转换。
5. 不要让实体跨越服务层边界,控制器层应该只使用DTO,并在控制器层进行转换。
6. 在Stack Overflow上有很多关于这个主题的有趣问题可以浏览。
使用JPA实体作为DTO的最佳解决方法是在控制器层使用DTO,并在控制器层进行转换。这样可以避免与实体相关的问题,并使代码更加清晰和易于维护。