如何在PrePersistenceHook保存更新前获取ProductModel旧值?
在SAP Commerce(Hybris)PrePersistenceHook中正确获取ProductModel旧值的方案
问题根源
Hybris的会话缓存(一级缓存)会复用同一PK的模型实例,直接通过productService获取或调用modelService.refresh()会导致新旧模型共享引用,无法正确对比值。以下是两种可靠的解决方案:
方法一:通过FlexibleSearch绕过缓存获取数据库原始值
直接用FlexibleSearch从数据库查询,再分离实例避免上下文干扰,确保拿到的是未被修改的旧值。
public Optional<ItemModel> execute(ItemModel item, PersistenceContext context) { if (item instanceof ProductModel productToInsert) { // 编写查询语句,指定需要对比的属性 final String query = "SELECT {p.pk}, {p.name}, {p.description} FROM {Product AS p} WHERE {p.code} = ?code"; final FlexibleSearchQuery flexibleSearchQuery = new FlexibleSearchQuery(query); flexibleSearchQuery.addQueryParameter("code", productToInsert.getCode()); // 强制绕过缓存,直接从数据库读取 flexibleSearchQuery.setCacheable(false); final SearchResult<ProductModel> result = flexibleSearchService.search(flexibleSearchQuery); if (!result.getResult().isEmpty()) { ProductModel oldProduct = result.getResult().get(0); // 分离实例,避免与当前待保存模型共享引用 modelService.detach(oldProduct); // 对比新旧值 String newName = cleanNameOrDesc(productToInsert.getName()); String oldName = oldProduct.getName(); String newDesc = cleanNameOrDesc(productToInsert.getDescription()); String oldDesc = oldProduct.getDescription(); if ((StringUtils.isNotBlank(newName) && newName.equals(oldName)) || (StringUtils.isNotBlank(newDesc) && newDesc.equals(oldDesc))) { productToInsert.setApprovalStatus(ArticleApprovalStatus.CHECK); } } } return Optional.of(item); }
方法二:利用PersistenceContext获取原始值(性能最优)
Hybris的PersistenceContext内置了当前模型的变更属性原始值,无需额外查询数据库,仅返回被修改过的属性旧值。
public Optional<ItemModel> execute(ItemModel item, PersistenceContext context) { if (item instanceof ProductModel productToInsert) { // 获取属性旧值映射,key为属性名,value为数据库原始值 Map<String, Object> originalValues = context.getOriginalValues(); if (originalValues != null) { String oldName = (String) originalValues.get("name"); String oldDesc = (String) originalValues.get("description"); String newName = cleanNameOrDesc(productToInsert.getName()); String newDesc = cleanNameOrDesc(productToInsert.getDescription()); if ((StringUtils.isNotBlank(newName) && newName.equals(oldName)) || (StringUtils.isNotBlank(newDesc) && newDesc.equals(oldDesc))) { productToInsert.setApprovalStatus(ArticleApprovalStatus.CHECK); } } } return Optional.of(item); }
方案对比
- 方法一:适合需要完整旧模型或对比大量属性的场景,缺点是需要额外数据库查询。
- 方法二:性能最优,仅获取变更属性的旧值,适合只关注修改字段的场景。
之前尝试方案的问题说明
productService.getProductForCode返回新值:会话缓存已保存修改后的模型实例,直接获取会复用同一对象。modelService.refresh(oldProduct)导致实例同步:新旧模型是同一引用,refresh会重置实例为数据库值,覆盖当前待保存的修改。- 分离后赋值报错:不能将分离实例赋值给当前待保存的item,分离实例仅用于读取旧值,不可替换上下文对象。
内容的提问来源于stack exchange,提问作者aanssaien
相关产品推荐
相关产品推荐

