【问题标题】:How do I stop spring data JPA from doing a SELECT before a save()?如何在 save() 之前阻止 spring data JPA 执行 SELECT?
【发布时间】:2017-08-11 12:57:50
【问题描述】:

我们正在针对现有数据库编写一个新应用程序。我正在使用 Spring Data JPA,并且只是在做一个

MyRepository.save() 

在我的新实体上,使用

MyRepository extends CrudRepository<MyThing, String>

我在日志中注意到,hibernate 在插入之前执行了一个选择,并且它们需要很长时间,即使在使用索引时也是如此。

我在这里搜索过这个,answers I've found 通常与 Hibernate 相关。我对 JPA 很陌生,看起来 JPA 和 Hibernate 非常紧密地交织在一起,至少在 Spring Data 的上下文中使用它时是如此。链接的答案建议使用 Hibernate persist(),或者以某种方式使用会话,可能来自 entityManager?我不必直接对会话或实体管理器或任何 Hibernate API 做任何事情。到目前为止,我已经在我的存储库中使用 save() 和几个 @Query 完成了简单的插入操作。

【问题讨论】:

  • 您是否在为您的实体使用自动生成的 ID?
  • 没有。我们看到这发生在 2 张桌子上。其中一个主键只有一个字段,我们实际上是从另一个预先生成的数字表中获得的。我正在为该表使用一个简单的 ID。另一个是复合 Id,我使用的是 EmbeddedId
  • 能够看到你引用的sql日志会很有用。
  • @Gimby 你具体在找什么,我怎样才能得到它?是否有一个日志级别和 jpa/hibernate 包我可以设置得更详细?我们使用 application.yml 进行配置;它当前正在记录它正在使用的生成的 sql;我实际上只是拿了那个 sql 并在 dbvisualizer 中运行它;它立即在 dbvisualizer 中运行,但在应用程序中运行时需要几秒钟
  • 是的,生成的SQL。一般来说,当你在谈论某件事时,你实际上是在展示它,这是一个好主意。您的实体代码也是如此。

标签: java spring hibernate spring-data-jpa


【解决方案1】:

这是您使用 Spring Data 存储库使用的 Spring SimpleJpaRepository 的代码:

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

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

它执行以下操作:

默认情况下,Spring Data JPA 检查给定实体的标识符属性。如果标识符属性为空,则实体将被假定为新的,否则为非新的。

Link to Spring Data documentation

因此,如果您的一个实体的 ID 字段不为空,Spring 将让 Hibernate 进行更新(之前也是 SELECT)。

您可以通过同一文档中列出的 2 种方法来覆盖此行为。一个简单的方法是让您的实体实现 Persistable(而不是 Serializable),这将使您实现方法“isNew”。

【讨论】:

  • Persistable 实体解决方案太棒了!!
  • 这种方法似乎不适用于@OneToMany 关系。我试图在“One”实体上调用save,使我的“Many”实体覆盖“Persistable”。仍然有很多选择。
  • 不幸的是,Persistable 对我不起作用...如果有影响的话,我正在使用 Kotlin
【解决方案2】:

如果您提供自己的 id 值,那么 Spring Data 将假定您需要检查数据库中是否存在重复键(因此选择+插入)。

更好的做法是使用 id 生成器,如下所示:

@Entity
public class MyThing {
    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    private UUID id;
}

如果你真的必须插入你自己的 id 并且想要阻止 select+insert 然后实现 Persistable,例如

@Entity
public class MyThing implements Persistable<UUID> {

    @Id
    private UUID id;

    @Override
    public UUID getId() {
        return id;
    }

    //prevent Spring Data doing a select-before-insert - this particular entity is never updated
    @Override
    public boolean isNew() {
        return true;
    }
}

【讨论】:

  • 请注意,第二个示例将破坏默认的 delete 和 deleteAll 方法,因为它们使用此方法来确定实体是否存在并因此应该被删除。
【解决方案3】:

我在@Repository中创建了一个自定义方法:

    public void persistAll(Iterable<MyThing> toPersist) {
        toPersist.forEach(thing -> entityManager.persist(thing));
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 2019-05-28
    • 1970-01-01
    • 2014-07-11
    • 2018-08-15
    • 2020-03-26
    相关资源
    最近更新 更多