【问题标题】:Unidirectional @OneToMany does not work as expected with @EmbeddedId单向 @OneToMany 不能按预期使用 @EmbeddedId
【发布时间】:2019-06-13 09:54:48
【问题描述】:

如果我使用这段代码(图片中的CASE 1):

@Data
@Entity
public class PostOneUni {

    @EmbeddedId //if @Id, class CompositeId must be annotated @Embeddable?
    private CompositeId id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumns({
            @JoinColumn(name = "id1"),
            @JoinColumn(name = "id2")
    })
    private Set<PostCommentUniMany> comments = new HashSet<>();

    ...
}

当执行创建此对象并添加子 cmets 时,当保存到 DB 时,一切都按预期工作(子 PostCommentUniMany 上的 ID 按预期添加)但更改不会传播到 java 代码(图片中的对象 @12081应该将 id 字段更新为 2 而不是 null)。

在其他情况下(CASE 2)我使用这个代码:

@Data
@Entity
public class PostOneUni {

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

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "post_id")
    private Set<PostCommentUniMany> comments = new HashSet<>();

    ...
}

从图中可以看出,fields也被持久化到了DB中,保存后用来保存状态到DB的对象更新(对象@12052更新反映id字段为@ 987654332@ - 在 DB 中)。

如何更新 CASE 1 中的对象 @12081 以反映 DB id?

更新

经过下面的讨论 - 问题是如果实体上的 id 手动设置为某个值,Spring 认为它不是新实体并尝试进行合并而不是持久化实体。

“解决方法”之一是在@EntityPostOneUni 上设置@Version 字段,该字段将跟踪实体是否是新的。

@Version
private Integer version;

【问题讨论】:

  • 您需要发布 ID 类的代码以及用于创建和持久化这些对象的代码。
  • @Alan Hay - 我可以。 Composite 只是标准的 lombok @_Value 并使用 Spring Data JPA repos .save(PostOneUni) 方法使用 @_Transactional 完成保存。
  • 你需要添加代码不要告诉我。
  • @AlanHay 好的,我更新了代码。
  • 集合中的实例comment2和实例comment2是内存中的不同项,显然不好。我希望这些在内存中是相同的实例。这是怎么发生的?

标签: java hibernate jpa orm nhibernate-mapping


【解决方案1】:

问题是因为你手动设置了 ID 字段,Spring Data 调用了合并操作而不是持久化:

参见 org.springframework.data.jpa.repository.support.SimpleJpaRepository

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) //checks if ID null {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

JPA 规范指出,当

3.2.7.1 合并分离实体状态

• 如果 X 是一个新的实体实例,则一个新的托管实体实例 X' 是 创建并将 X 的状态复制到新的托管实体中 实例 X'。

如果您这样做,请在您的测试代码中:

post = oneToManyService.addNewPost(post);

标识符在返回的实例上正确设置,就像从数据库重新加载时一样。但是,原始实例(处于“新”状态)保持不变,即没有设置 ID。

如果调用persist而不是合并,则返回原始(相同)实例,并将在此实例上正确设置标识符。

【讨论】:

  • 感谢您的解释。我会试试这个。 JpaRepository 不暴露persist,所以我必须检查如何做到这一点。 Spring Data 是否不够聪明,无法检查手动设置的 Id 是否在 DB 中并将其标记为新的?我还看到有Version字段可以设置来做这个决定。
  • Spring Data 在发布的代码中做出决定。
  • 可能看这里。 stackoverflow.com/a/37255257/1356423 实现 Persistable 接口并定义您自己的 isNew() 实现。
  • 进度 :) 现在我有org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: me.vukas.hiperfjavapersistence.entity.relationship.onetomany.PostOneUni; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: me.vukas.hiperfjavapersistence.entity.relationship.onetomany.PostOneUni
  • 好的。我可以确认这是问题所在。将字段@Version private Integer version; 添加到PostOneUni 后,它按预期工作。也许有其他方法不使用版本控制?
猜你喜欢
  • 2016-09-06
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
相关资源
最近更新 更多