【问题标题】:JPA: Mapping crossed OneToOne and ManyToOne relationsJPA:映射跨越 OneToOne 和 ManyToOne 关系
【发布时间】:2020-04-11 22:14:59
【问题描述】:

我有两个实体,我们将它们称为 A 和 B。B 始终将 A 作为具有 ManyToOne 关系的父级。 但是,我需要 A 与表 B 中插入的最新记录建立 OneToOne 关系。 这是因为我需要保存多个版本的 B,但 99% 的时间只需要使用最新的版本。

这看起来像这样:

@Data
@Entity
public class A {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Long id;

    /* Properties
       ...
    */

    @OneToOne(optional = false)
    private B latest;
}
@Data
@Entity
public class B {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Long id;

    /* Properties
       ...
    */

    @Column(nullable = false)
    private Date lastModified;

    @ManyToOne(optional = false)
    private A parent;
}

现在,手头的问题是我似乎无法持久化这些实体,因为它们似乎总是短暂的:

  • A 无法持久化,因为 latest 引用了 B,但 B 没有持久化。
  • B 无法持久化,因为 parent 引用了 A,但 A 没有持久化。

尝试这样做会导致:

java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: 非空属性引用瞬态值 - 瞬态实例必须在当前操作之前保存:B.parent -> A

我尝试将负责持久化它们的代码包装在 @Transactional 方法中,但同样的情况发生了:

@Transactional
public void saveAB(A parent, B child) {
    parent.setLatest(child);
    child.setParent(parent);
    Arepository.save(parent);
    Brepository.save(child);
}

我还想过忽略从 A 到 B 的 OneToOne 关系,而是将 latest 作为临时的 @Formula 字段,它将查询 B 以获取最新记录。但是,@Formula 似乎仅限于原语,而不是完整的实体。

使用 JPA 执行此操作的正确方法是什么?我是不是走错了路?

【问题讨论】:

  • 看看这个vladmihalcea.com/tutorials/hibernate -> 关系
  • @AhmetOZKESEK 谢谢。实际上,我在“高级映射技术”中找到了我的确切场景的示例:vladmihalcea.com/… 最初的 StackOverflow 问题没有得到很多赞成,所以我想我不是自然而然地偶然发现的。

标签: java spring hibernate jpa spring-data-jpa


【解决方案1】:

由于AB 相互依赖,它们可能应该被视为单个aggregateA 是聚合根。

这意味着您在关系上只有ARepositoryCascadeType.ALL

【讨论】:

    【解决方案2】:

    解决方案是按照here 的说明应用@JoinFormula。

    @Data
    @Entity
    public class A {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Setter(AccessLevel.NONE)
        private Long id;
    
        /* Properties
           ...
        */
    
        @ManyToOne
        @JoinFormula(value = "(SELECT b.id FROM b " +
                "WHERE b.id = id ORDER BY b.lastModified DESC LIMIT 1)")
        private B latest;
    }
    

    然后在 B 上:

        @ManyToOne(optional = false)
        private A parent;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-27
      • 1970-01-01
      • 2014-05-17
      • 2019-02-24
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2016-07-05
      相关资源
      最近更新 更多