像SpringBoot这样的框架可以为你做很多事情,这真是太好了。
您只需要一个 JPA 实体类加上一个简单的存储库接口,SpringData 将为您提供典型 CRUD 数据库操作所需的一切。
您编写了一个简单的 REST 控制器类,并且运行了一个 REST API,对吧?
嘿,但是你忘记写 DTO 了!但是,当您的应用程序无需它也可以运行时,为什么您实际上需要它?
当然有一些一般原因:
但是其他奇怪的事情也可能发生。我将根据我的经验向您展示一个奇怪的例子。
此 GitHub 存储库包含一个无需 DTO 即可运行的简单应用程序。有一个User实体,每个User可以有多个Transaction。我们甚至在存储库和 RestController 之间有一个 Service bean,用于捕获可能的数据库访问异常。
由于我们想要制作一个可用于生产的应用程序,因此我们不希望 Hibernate 生成 DDL。相反,我们有一个 schema.sql 来创建表(稍后我们可能会切换到 Flyway 或 Liquibase)。对于我们的简单示例,我们还有一个 data.sql,以便我们的表不为空。
当我们运行应用程序并调用 http://localhost:8080/users 处的 API 端点时,我们会得到包含用户及其交易的预期 JSON。
现在我们关注Transaction类中的两行代码,标记为//!!
@JsonIgnore //!!
第一个味道是,在 Transaction 类中,我们必须将 @JsonIgnore 注释添加到 User 引用中。如果没有该注释,JSON 序列化会因无限递归而崩溃。
现在让我们想象一下,有人在向 Transaction 实体添加另一个字段(描述)时犯了一个错误,但忘记调整 SQL 语句(或者在尚未应用架构更改的环境中运行应用程序)。
私有字符串描述;//!!
当然,现在API调用失败了。但看看错误处理! UserService 内的 catch 子句未按预期工作。相反,我们可以在日志中看到奇怪的堆栈跟踪:
GlobalExceptionHandler:意外错误org.springframework.http.converter.HttpMessageNotWritableException:无法写入JSON:
我曾经见过这种情况(显然,应用程序比这个示例大得多),我花了很长时间才理解为什么 SQL 异常逃逸了服务以及为什么我收到 HttpMessageNotWritableException。你能看见它吗?
发生的情况是,UserService 类(通过 UserRepository)仅查询 USERS 数据库表。由于默认的 Hibernate 延迟加载,事务实体不是结果的一部分。仅当 Jackson 反序列化器尝试从 User 实例创建 JSON 时,它才会调用其 getTransactions 方法,使 Hibernate 获取 Transaction 实体。
这就是为什么我们得到一个奇怪的堆栈跟踪,结合了 JSON 和 SQL 的内容。该异常被 GlobalExceptionHandler 捕获,但不知道如何处理它,这就是日志消息为“意外错误”的原因。
我希望这个小练习能让您更深入地了解允许应用程序的不同层混合是多么危险。在应用程序还很小的时候只看到应用程序的“晴天”场景可能会导致一些开发人员继续做错误的事情,直到为时已晚。
您不必编写在 DTO 和应用程序的其他层之间映射字段的样板代码。 MapStruct 可以为您做到。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3