【问题标题】: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)?

标签: spring hibernate jpa orm


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2021-08-22
    • 2013-01-21
    • 2021-05-22
    • 2016-03-30
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    相关资源
    最近更新 更多