首页 > 解决方案 > 单向 @OneToMany 不能按预期使用 @EmbeddedId

问题描述

如果我使用这段代码(图片中的案例 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<>();

    ...
}

并且当执行创建此对象并添加子注释时,当保存到 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<>();

    ...
}

从图片中可以看出,字段也被持久化到 DB,并且用于将状态保存到 DB 的对象在保存后被更新(对象@12052被更新以反映id字段是2- 就像在 DB 中一样)。

在此处输入图像描述

如何更新@12081案例 1 中的对象以反映数据库 ID?

更新

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

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

@Version
private Integer version;

标签: javahibernatejpaormnhibernate-mapping

解决方案


问题是因为您手动设置了 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而不是合并的地方,则返回原始(相同)实例,并且将在此实例上正确设置标识符。


推荐阅读