为什么仅在保存之前进行刷新时才为局部变量生成ID?

时间:2018-12-20 20:49:05

标签: java spring hibernate

让我们说我有三堂课。

public class Parent {
    @Id
    @GeneratedValue
    @Column(name = "parent_id", updatable = false, insertable = false)
    private Long id;

    private String name;

    @Builder.Default
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    Set<Child> childs = new HashSet<>();
}

public class Child {
    @Id
    @GeneratedValue
    @Column(name = "child_id", updatable = false, insertable = false)
    private Long id;

    private String name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name ="parent_id")
    Parent parent;

    @Builder.Default
    @OneToMany(mappedBy = "child", cascade = CascadeType.ALL)
    Set<GrandChild> grandChildren = new HashSet<>();
}

public class GrandChild {
    @Id
    @GeneratedValue
    @Column(name = "grandchildid", updatable = false, insertable = false)
    private Long id;

    private String name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "child_id")
    Child child;
}

如果我随后通过GrandChild grandChild创建新的parent.getChilds().get(0).getGranChilds().add(grandChild)并通过parentRepo.save(parent)保存实体。局部变量grandChild将没有id。但是,如果我先刷新然后保存,则本地变量将具有ID?这是您通常会做的事情吗?

我想这会在您保存然后刷新时发生。但是似乎只有在我先冲洗然后保存时才能工作。

添加grandChild:

   GrandChild grandChild = GrandChild.builder().name(name).build();
   Parent parent = parentRepo.findAll().get(0);

   Optional<Child> optional = parent.getChilds().stream().findAny();

   optional.ifPresent(child -> child.getGrandChildren().add(grandChild));

(这只是示例代码,所以不要介意丑陋)。

保存后刷新(未为本地变量生成ID,仍然保存实体):

parentRepo.saveAndFlush(parent);
System.out.println(grindChild.getId()); // null

先冲洗(局部变量获取ID)

parentRepo.flush();
parentRepo.save(parent);
System.out.println(grandChild.getId()); //not null, value exists

在两种情况下,该ID都是在数据库中生成的。

2 个答案:

答案 0 :(得分:0)

我假设您可以保存实例而无需刷新并将结果分配给局部变量。请从CrudRepository界面中查看以下javadoc注释:

/**
 * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
 * entity instance completely.
 *
 * @param entity must not be {@literal null}.
 * @return the saved entity will never be {@literal null}.
 */
<S extends T> S save(S entity);

我认为您可以尝试以下方法:

Parent persistentParent = parentRepo.saveAndFlush(parent);

persistentParent应具有可用的生成的ID值。

UPD

在与数据库同步时生成ID。因此,只有在刷新会话后才能使用。

答案 1 :(得分:0)

这里的问题是,根据保存之前或之后是否进行刷新来执行不同的级联操作。

如果在保存之前刷新,则实体将通过ACTION_PERSIST_ON_FLUSH级联保存,该级联使用与本地变量相同的对象引用。

如果我在保存后刷新,则实体将通过ACTION_MERGE级联保存,该级联会在生成ID之前在实体上进行复制,因此引用不同,这意味着局部变量将没有ID 。

在这两种情况下,saveWithGeneratedId(...)类都被调用org.hibernate.event.internal.AbstractSaveEventListener。只是具有不同的对象引用。