大容量插入现有数据:防止 JPA 在每次插入之前执行选择



我正在开发一个使用JPA(Hibernate(作为持久性层的Spring Boot应用程序。

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

我遇到的问题位于另一侧,重新导入现有数据。在此步骤中,XML 将再次转换为 Java 对象并持久保存到数据库中。

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

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

我理解这种行为,这实际上是有道理的。但是,我确定 id 将不存在,因此选择对我的情况没有意义。我正在保存数千条记录,这意味着在大型表上会有数千个选择查询,这会大大减慢导入过程。

我的问题:有没有办法关闭这种"插入前检查实体是否存在"?


附加信息:

当我使用entityManager.persist()而不是合并时,我得到以下异常:

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;
}
}
}

如果您确定永远不会更新数据库上的任何现有条目,并且所有实体都应始终新鲜插入,那么我会选择persist操作而不是merge

每次更新

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

@Id
@JsonIgnore
private String id;

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

我不确定我是否填写了ID。如果您在应用程序端填写,请在此处查看答案。我在下面复制了它:

以下是您使用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 检查给定实体的标识符属性。如果标识符属性为 null,则将假定该实体为新实体,否则为非新实体。

链接到 Spring 数据文档

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

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

最新更新