JPA/Spring/Hibernate/etc 中是不是有类似于 JPA 的 @PrePersist 允许更改相关实体的功能?

Posted

技术标签:

【中文标题】JPA/Spring/Hibernate/etc 中是不是有类似于 JPA 的 @PrePersist 允许更改相关实体的功能?【英文标题】:Is there functionality in JPA/Spring/Hibernate/etc similar to JPA's @PrePersist that would allow changes to related entities?JPA/Spring/Hibernate/etc 中是否有类似于 JPA 的 @PrePersist 允许更改相关实体的功能? 【发布时间】:2021-04-13 16:23:56 【问题描述】:

我需要存储某些实体的某些 String 字段的规范化(即没有特殊字符等)变体。

一个例子:

@Entity
public class Car 
    @Id
    private Long id;
    private String make;
    private String model;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "CAR_ID")
    private Set<NormalizedField> normalizedFields = new HashSet();

    private Set<NormalizedField> createNormalizedFields(Car car) 
        Set<NormalizedField> normalized = normalize(car);
        this.normalizedFields.clear();
        this.normalizedFields.addAll(normalized);
    

    // I would use this approach, but it doesn't allow
    // changes to related entities.
    // @PreCreate
    // public void onCreate() 
    //     createNormalizedFields();
    // 


@Entity
public class NormalizedField 
   @Id
   private Long id;
   private String fieldName;
   private String normalizedValue;

如果在Car 实体被持久化时自动(重新)创建标准化值会很方便。有没有办法自动触发创建方法?

使用@PrePersist, @PreUpdate... 显然不是一种选择,因为它不允许更改相关实体。

项目中没有使用Spring AOP,所以我宁愿暂时避免引入它。但无论如何,这是一个选择。

应用程序很大,“手动”管理标准化值需要大量工作,因此我将其作为最后一个选项。

【问题讨论】:

使用自定义的 UserTypeAttributeConverter 即可。 @M.Deinum,AttributeConverter 似乎不能用于关系属性。此外,属性本身不包含转换数据,而是需要父实体的状态。自定义UserType 也存在访问父状态的相同问题。 我的错。我应该更仔细地阅读。 prepersist 不应该正常工作吗?我看到的主要问题是您正在清除集合并再次添加所有内容,您应该在其中更新现有的集合。此外,您应该实现正确的 equals amd hashcode 以在 Set 中使用它们。 PrePersist 不起作用,因为它不允许更改其中的 relation 状态。仅限非关系属性。 有趣;想到几个问题:(1)normalize() 的本质是什么?如果它只依赖于当前实体的状态,也许规范化的值不需要被持久化。取而代之的是,在需要时即时进行规范化(并且可能将结果缓存到@Transient 字段)。 (2) 如果它依赖于Car 实体之外的状态,在应用程序逻辑中执行规范化不是更合适吗? (3) NormalizedField 必须是一个实体吗?您能否将其建模为持久值的集合(可能适用于 PrePersist)? 【参考方案1】:

将在此处发布这个半答案(“半”,因为它提供了一种有限制的解决方法)。

在某些情况下,org.hibernate.Interceptor 可用于在父实体更改时管理子实体。

但有一些限制:javadoc 说Session 不能在Interceptor 中使用。 JPA 存储库方法、JPQL 或 HQL 调用在循环中被相同的Interceptor 拦截。除非您设置 FlushMode.COMMITFlushMode.MANUAL(可能还有其他),否则即使是原生查询也会被拦截。

以上意味着您可能必须直接使用数据源。我不记得具体是怎么做的,但是 Spring 提供了直接在当前事务中使用数据源执行查询的方法。就我而言,这已经足够了,因为我必须管理一些不需要以 Entity 表示的技术子实体。

【讨论】:

以上是关于JPA/Spring/Hibernate/etc 中是不是有类似于 JPA 的 @PrePersist 允许更改相关实体的功能?的主要内容,如果未能解决你的问题,请参考以下文章