【问题标题】:How do I get Hibernate/Spring/JPA to auto update the id of a new entity如何让 Hibernate/Spring/JPA 自动更新新实体的 id
【发布时间】:2014-05-01 16:52:45
【问题描述】:

我有一个包含子实体的聚合。当我保存聚合时,聚合和子实体的 ID 不会更新。有没有办法让 JPA 实体管理器自动更新 ID,或者是唯一的解决方案来保存并获取返回聚合然后找到您刚刚添加的子实体?我正在使用 Spring CrudRepositories 来保存我的所有聚合。

我正在使用:

春季 4.0.3

休眠 4.3.5

JPA 2.1

这是我拥有的基本代码。

@MappedSuperclass
public class AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false)
    protected Long id;
}


@Entity
@Table(name = "foo")
public class Foo extends AbstractEntity{
    @OneToMany(cascade=CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "foo")
    private List<Bar> bars;

    public void addBar(Bar bar)
    {
        bar.setBar
        bars.add(bar);
    }
}

@Entity
@Table(name = "bar")
public class Bar extends AbstractEntity{
    @ManyToOne
    @JoinColumn(name = "foo_id", nullable = false)
    private Foo foo;

    protected setFoo(Foo foo){
        this.foo = foo;
    }
}

@Repository
@Transactional(propagation = Propagation.MANDATORY)
interface FooRepository extends CrudRepository<Foo, Long> {
}

@Service
public class DoStuffService()
{
    @AutoInject
    private FooRepository fooRepository;

    public Bar doStuff(long id)
    {
        Foo foo = fooRepository.findOne(id);
        Bar bar = createSomeBar();
        foo.addBar(bar);
        fooRepository.save(foo);
        return bar; // I would like this to have the id filled out from the save of the aggregate rather than having to go to the get the Foo again and go through the collection to find the right bar so I can return it with an ID.
    }
}

@Service
@Transactional
public class SomeRootServiceService()
{
    @AutoInject
    private DoStuffService doStuffService;

    public Bar doRootStuff()
    {
         Bar bar = doStuffService.doStuff(someId);
         if (bar.getId() == null){
             //Should never get here but I do.
         }
    }
}

编辑:添加一些信息来阐明我们的最终目标是什么,id 问题只是其中的一个症状。

我们真正想做的是获取一个从聚合中创建的实体,并将其传递给在同一个数据库事务中链接到新创建的实体的其他东西,因为该实体不是然而“已保存”的休眠会引发瞬态实体错误。

【问题讨论】:

  • 你在你的 id 字段上使用@GeneratedValue 吗?
  • 是的,很抱歉忘记将其包含在原始问题中。更新了描述以显示我的 id 是如何注释的。
  • 我不明白为什么你的问题被否决了。我相信这是一个合理的问题。无论如何,请包括您的实体的代码,实际保存的代码和您得到的错误/不需要的结果
  • 添加了正在发生的事情的代码。
  • 如何测试没有生成 ID?您在哪里访问 ID?你为什么要调用 fooRepository.save(foo)? foo 是一个托管实体。它将自动、透明地保存。你为什么不设置酒吧的 foo。这是必要的。

标签: java spring hibernate jpa


【解决方案1】:

您可以使用以下代码:

@Id
@Column(name = "id") //Don't need nullable = false as primary keys can never be null
@GeneratedValue(generator = "generatorName")
@SequenceGenerator(name = "generatorName", sequenceName = "SEQ_NAME")
protected Long id;

您可以自己在数据库中创建序列,或者如果 Hibernate 正在为您生成架构,它会这样做。

Hibernate 在创建新对象时将使用这些注释自动从数据库中获取序列的下一个值,而无需您做任何事情。它非常方便。如果您的级联设置正确,您应该只需要保存父对象,然后将创建子对象并自动分配 ID。

对于它的价值,拥有一个映射的抽象超类只是为了封装 ID 对我来说没有意义。一个对象的 ID 是其个体状态所固有的,每个对象都应该有一个唯一的序列来生成 ID。为什么需要两个对象使用相同的 ID 源。每个对象都应该有自己的表的唯一 ID。

如果所有子类共享该功能,并且您的对象都需要 ID,那么您仅将功能封装在超类中,而这些 ID 彼此不相关。所以你不能说 Foo_ID 和 Bar_ID 有关系,它们应该是完全分开的。

在另一个不相关的说明中,您为什么不在 Spring 中使用 @AutoInject 而不是 @Autowired?

在第三个不相关的注释中,为什么你的 DAO 被注释为 @Transactional。服务层是所有事务管理和对象操作应该发生的地方。如果你需要你的 DAO 注释 @Transactional 你有问题。你也不应该需要那个传播修饰符。 Hibernate 在防止事务相互干扰方面做得非常出色。

【讨论】:

  • 抽象超类是这样我们就不必到处复制 id 代码了。它不会对所有实体产生单一序列,每个表都有自己的 id 列和序列。
  • 此外,@Transactional(propagation = Propagation.MANDATORY) 不会启动事务,如果类不在事务中被调用,它只会引发异常。这只是一个安全检查,以确保没有人在调用事务时忘记启动事务。
【解决方案2】:

持久化实体不会立即生成生成 ID 并保存实体的 SQL 查询。这些查询实际上是延迟的,以便对它们进行批处理,并在回滚的情况下避免它们,以获得性能。

因此,至少查询将在事务提交之前实际执行。但是您试图在事务提交之前访问 ID。这就是为什么,我认为,你会得到空值。您可以通过启用 SQL 日志记录来确认这一点(在开发过程中应该始终如此,因为它非常有用),并使用您的调试器逐行执行您的代码。

因此,如果您需要访问事务中生成的 ID,请确保在获取 ID 之前刷新会话(或实体管理器,如果使用 JPA)。这将执行所有待处理的查询,并生成您的 ID。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-11
    • 2019-02-09
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多