当前位置: 首页 > 知识库问答 >
问题:

如何使用Spring Data JPA保存带有手动分配的标识符的实体?

堵凯
2023-03-14

我正在更新一个现有的代码,该代码处理从一个表到同一数据库中的多个对象的副本或原始数据。

以前,每种对象都使用每个表的序列生成PK。

大概是这样的:

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Id
@Column(name = "id")
private Integer id;

对于这个实体,我没有更改我的JPararePository,如下所示:

public interface EntityRepository extends JpaRepository<Entity, Integer> {
    <S extends Entity> S save(S entity);
}

现在,我正在努力理解具有默认传播和隔离级别的spring事务(@transactional)中的以下行为:

  • 使用实体上的@GeneratedValue,当我调用EntityRepository.Save(entity)时,我可以在Hibernate show sql activated中看到一个insert请求被激发(但似乎只在缓存中,因为数据库没有更改)
  • 如果实体上没有@GeneratedValue,则只激发select请求(不尝试插入)
ERROR: insert or update on table "t_other_entity" violates foreign key constraint "other_entity_entity"
Détail : Key (entity_id)=(110) is not present in table "t_entity"

我使用的是Spring Boot 1.5.12、Java8和PostgreSQL 9

共有1个答案

高迪
2023-03-14

您基本上是从自动分配的标识符切换到手动定义的标识符,这在JPA和Spring数据级别上都有两个结果。

在普通JPA级别上,持久性提供程序不一定需要立即执行单个insert,因为它不需要获取标识符值。这就是为什么它通常会延迟语句的执行,直到它需要刷新,刷新是对EntityManager.flush()的显式调用、要求数据库中的数据是最新的以提供正确的结果或事务提交的查询执行。

Spring Data JPA存储库在调用save(…)时自动使用默认事务。但是,如果您在用@transactional依次注释的方法中调用存储库,则数据库交互可能要等到该方法离开时才会发生。

JPA需要EntityManager客户机代码来区分持久化一个全新的实体还是将更改应用于现有实体。Spring数据存储库可以使客户机代码从处理这种区别中解脱出来,因为业务代码不应该被这种实现细节重载。这意味着Spring数据必须以某种方式将新实体与现有实体区别开来。参考文档中描述了各种策略。

在手动标识符的情况下,检查标识符属性的null值的默认值将不起作用,因为根据定义,该属性永远不会是null。标准模式是调整实体以实现persistable并保留一个瞬时的is-new-flag并使用实体回调注释来翻转该标志。

@MappedSuperclass
public abstract class AbstractEntity<ID extends SalespointIdentifier> implements Persistable<ID> {

  private @Transient boolean isNew = true;

  @Override
  public boolean isNew() {
    return isNew;
  }


  @PrePersist
  @PostLoad
  void markNotNew() {
    this.isNew = false;
  }

  // More code…
}

isnew声明为瞬态,这样它就不会持久化。该类型实现了persistable,因此存储库的save(…)方法的Spring Data JPA实现将使用它。上面的代码导致从使用new的用户代码创建的实体将标志设置为true,但任何类型的数据库交互(保存或加载)将实体转换为现有实体,因此save(…)最初将触发EntityManager.Persist(…),但所有后续操作都将触发….Merge(…)

我利用这个机会创建了DATAJPA-1600,并在参考文档中添加了这个描述的摘要。

 类似资料:
  • 我使用spring save 该表类似于varchar(256)NOT NULL, 原因:java.sql.sqlexception:字段“f_id”在com.mysql.jdbc.sqlerror.createsqlexception(sqlerror.java:946)在com.mysql.jdbc.mysqlio.checkerrorpacket(mysqlio.java:2870)在com

  • 我试图使用ikvmc从Scala代码编译的jar文件生成的DLL(是的,我的日子过得很好)。Scala编译器似乎为操作符重载生成包含美元符号的标识符,而IKVM使用生成的DLL中的标识符(我可以在Reflector中看到)。问题是,美元符号在C#代码中是非法的,所以我不能引用那些方法。 有办法解决这个问题吗?

  • 问题内容: ,仅以定界格式保存数据。如何在JAVA中保存带有标题的数据框。 问题答案: 如果您想另存为csv文件,我建议使用软件包。您可以使用以下带有标头的格式保存数据框。 您可以参考下面的链接,以获取更多信息:https : //github.com/databricks/spark-csv 具有Maven依赖性。

  • 我正在使用100个实体(使用JHipster)设置一个新的Spring Boot API,我的问题是:鉴于我有一组存储库层方法,我希望我的所有存储库都能够调用这些方法。 我已经尝试制作所有接口来扩展('RepositoryQuery'是我默认的自定义接口名称后缀),然后使用特定于实体的类。请注意,所有的类扩展了一个泛型实现类,名为。 请注意,给定正则表达式中的“.*”代表我的持久实体集中的任何实体

  • 我有两个具有@ManyToOne关系的实体类,如下所示。 我是Spring Data JPA的新手,所以这可能是一个非常基本的错误。

  • 我想使用maven执行空手道测试,并将标记动态传递给执行。我使用的是junit4,我尝试了这两种方法--并行和简单的@Runwith注释。 @Runwith 使用这种方法,我可以动态地将标记传递给执行,但是如果空手道测试失败,maven构建仍然是成功的。 平行的 使用并行方法,如果空手道测试失败,maven构建将失败。但我无法通过maven命令选项将标记传递给测试。 我正在使用命令运行测试