java - 反序列化后如何处理瞬态实体
问题描述
假设我有一个带有控制器、服务和数据层的简单 REST 应用程序。在我的控制器层中,我执行以下操作:
@PostMapping("/items")
void save(ItemDTO dto){
Item item = map(dto, Item.class);
service.validate(item);
service.save(item);
}
但后来我收到错误,因为我的服务层如下所示:
public void validate(Item item) {
if(item.getCategory().getCode().equals(5)){
throw new IllegalArgumentException("Items with category 5 are not currently permitted");
}
}
我在 处得到 NullPointerException .equals(5)
,因为该Item
实体是从仅包含 的 DTO 反序列化的category_id
,没有其他内容(null
除 之外的所有内容id
)。
我们已经找到并尝试过的解决方案是:
制作一个特殊的反序列化器,它接受
id
s 并自动获取所需的实体。当然,这会导致大量性能问题,类似于将所有关系都标记为FetchType.EAGER
.让控制器层获取服务层需要的所有实体。问题是,控制器需要知道底层服务是如何工作的,以及它需要什么。
让服务层在运行任何验证之前验证对象是否需要获取。问题是,我们找不到确定对象是否需要获取的可靠方法。我们最终到处都是这样丑陋的代码:
(样本)
if(item.getCategory().getCode() == null)
item.setCategory(categoryRepo.findById(item.getCategory().getId()));
您还会采取哪些其他方式来保持服务易于使用?每次我们想要使用相关实体时都必须检查,这真的是违反直觉的。
请注意,这个问题不是要找到解决这个问题的任何方法。更多的是寻找更好的方法来解决它。
解决方案
你为什么不只是使用code
as 主键Category
呢?这样您就不必为这种检查获取任何东西。但根本问题是对象映射器无法处理 JPA 对象的托管性质,即它不知道它应该通过例如 PK 实际检索对象EntityManager#getReference
。如果它这样做,那么你不会有问题,因为该方法返回的代理将在第一次调用getCode
.
我建议你看看像Blaze-Persistence Entity Views这样的东西,它对类似的东西有一流的支持。
我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:
@EntityView(Item.class)
// You can omit the strategy to default to QUERY when using the code as PK of Category
@UpdatableEntityView(strategy = FlushStrategy.ENTITY)
public interface ItemDTO {
@IdMapping
Long getId();
String getName();
void setName(String name);
CategoryDTO getCategory();
void setCategory(CategoryDTO category);
@EntityView(Category.class)
interface CategoryDTO {
@IdMapping
Long getId();
}
}
查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。
ItemDTO a = entityViewManager.find(entityManager, ItemDTO.class, id);
Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它:https ://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ItemDTO> findAll(Pageable pageable);
最好的部分是,它只会获取实际需要的状态!
在您保存数据的情况下,您可以使用如下所示的Spring WebMvc 集成 :
@PostMapping("/items")
void save(ItemDTO dto){
service.save(dto);
}
class ItemService {
@Autowired
ItemRepository repository;
@Transactional
public void save(ItemDTO dto) {
repository.save(dto);
Item item = repository.getOne(dto);
validate(item);
}
// other code...
}
推荐阅读
- arrays - vba从数组数组中提取数组
- linux-kernel - 有什么方法可以让 uboot 知道内核启动状态
- python - 缺少 1 个必需的位置参数 - Selenium python
- css - 选择没有id的css标签
- javascript - 通过 Ajax 脚本添加多个产品以吸引购物车
- matlab - 如何读取自定义文件格式 .fid
- azure - Azure VM 自定义脚本未下载
- java - Spring Data REST - 尽管我提供了自定义实体查找,但 Spring 仍然调用 fndById
- android - 来自 Google SafetyNet 的 JWS 包含的 nonce 值与我在 attest() 中提交的值不同
- jdbc - 为什么数据库的编程接口称为驱动程序?