Java 批量插入现有数据:防止JPA在每次插入之前进行选择

Java 批量插入现有数据:防止JPA在每次插入之前进行选择,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我正在开发一个使用JPA(Hibernate)作为持久层的Spring引导应用程序 我目前正在实现迁移功能。我们基本上将系统的所有现有实体转储到一个XML文件中。此导出还包括实体的ID 我遇到的问题位于另一边,即重新导入现有数据。在这一步中,XML再次转换为Java对象并持久化到数据库 在尝试保存实体时,我使用了EntityManager类的merge方法,该方法有效:所有内容都成功保存 但是,当我打开Hibernate的查询日志时,我看到在每次插入查询之前,都会执行一个select查询,以查看

我正在开发一个使用JPA(Hibernate)作为持久层的Spring引导应用程序

我目前正在实现迁移功能。我们基本上将系统的所有现有实体转储到一个XML文件中。此导出还包括实体的ID

我遇到的问题位于另一边,即重新导入现有数据。在这一步中,XML再次转换为Java对象并持久化到数据库

在尝试保存实体时,我使用了
EntityManager
类的
merge
方法,该方法有效:所有内容都成功保存

但是,当我打开Hibernate的查询日志时,我看到在每次插入查询之前,都会执行一个select查询,以查看具有该id的实体是否已经存在。这是因为实体已经具有我提供的id

我理解这种行为,它实际上是有道理的。但是,我确信ID将不存在,因此选择对我的情况没有意义。我正在保存数千条记录,这意味着在大型表上有数千条select查询,这大大降低了导入过程的速度

我的问题:有没有办法关闭“插入前检查实体是否存在”的功能


其他信息:

当我使用
entityManager.persist()
而不是merge时,会出现以下异常:

org.hibernate.PersistentObjectException:传递给的分离实体 坚持

为了能够使用提供的id,我使用此id生成器:

@Id
@GeneratedValue(generator = "use-id-or-generate")
@GenericGenerator(name = "use-id-or-generate", strategy = "be.stackoverflowexample.core.domain.UseIdOrGenerate")
@JsonIgnore
private String id;
发电机本身:

public class UseIdOrGenerate extends UUIDGenerator {

  private String entityName;

  @Override
  public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
      entityName = params.getProperty(ENTITY_NAME);
      super.configure(type, params, serviceRegistry);
  }

  @Override
  public Serializable generate(SessionImplementor session, Object object) 
  {
        Serializable id = session
            .getEntityPersister(entityName, object)
            .getIdentifier(object, session);

      if (id == null) {
        return super.generate(session, object);
      } else {
        return id;
      }
  }
}

如果您确定永远不会更新数据库中的任何现有条目,并且所有实体都应该是新插入的,那么我将使用
持久化
操作,而不是
合并

每次更新

在这种情况下(id字段设置为自动生成),唯一的方法是从id字段中删除生成注释,并将配置保留为:

@Id
@JsonIgnore
private String id;

因此,基本上设置id,以便始终手动分配。然后,持久性提供程序会考虑您的实体即使在ID存在时也是暂时的。这意味着
持久化
将起作用,并且不会生成额外的选择。

我不确定是否得到了您是否填写的ID。如果您在应用程序端填写ID,请检查。我复制了它如下:

以下是您通过使用Spring数据存储库使用的Spring SimpleParepository的代码:

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

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
@Transactional
公共存储(S实体){
if(entityInformation.isNew(实体)){
em.persist(实体);
返回实体;
}否则{
返回em.merge(实体);
}
}
它做了以下工作:

默认情况下,SpringDataJPA检查给定实体的标识符属性。如果identifier属性为null,则该实体将假定为新实体,否则为非新实体

因此,如果您的实体中有一个ID字段不为null,Spring将让Hibernate进行更新(以及之前的选择)


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

谢谢,我忘了提到这一点。我尝试使用persist,但它引发了一个异常。我已经用详细信息更新了我的原始帖子。如果您能够进行更改,请检查我的upadteThat。这是一个好主意,但如果我理解正确,这将中断在应用程序的所有其他部分插入新项目,因为ID将为空。我必须在任何地方手动执行setId(generateId()),这现在不是一个选项。这些选择是在调用每个合并方法之后发生的吗?还是在交易结束时一次完成?或者每个插入都被编码为一个事务的一部分?每件事都发生在一个事务中,首先我可以看到为要保存的每件事调用了selects(各种类型的多个实体),然后我看到一个插入列表。