【问题标题】:Spring Data Rest - Primary Key Violation while PUT or PATCHSpring Data Rest - PUT 或 PATCH 时主键冲突
【发布时间】:2019-04-01 11:02:31
【问题描述】:

我在通过 Spring Data Rest 对我的 Profile 实体进行 PUT 或 PATCH 时遇到问题。当我为“技能”或“工作”属性 PUT 或 PATCH 相同或新数据时会发生错误。

这是 curl 返回的:

{"cause":{"cause":{"cause":null,"message":"Unique index or primary key violation: \"PRIMARY_KEY_6E ON PUBLIC.TRAINER_PROFILE_JOBS(TRAINER_PROFILE_ID, JOBS_ID) VALUES (77, 79, 2)\"; SQL statement:\ninsert into trainer_profile_jobs (trainer_profile_id, jobs_id) values (?, ?) [23505-197]"},"message":"could not execute statement"},"message":"could not execute statement; SQL [n/a]; constraint [\"PRIMARY_KEY_6E ON PUBLIC.TRAINER_PROFILE_JOBS(TRAINER_PROFILE_ID, JOBS_ID) VALUES (77, 79, 2)\"; SQL statement:\ninsert into trainer_profile_jobs (trainer_profile_id, jobs_id) values (?, ?) [23505-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"}

应用程序产生以下输出:

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PRIMARY_KEY_6E ON PUBLIC.TRAINER_PROFILE_JOBS(TRAINER_PROFILE_ID, JOBS_ID) VALUES (77, 79, 2)"; SQL statement:
insert into trainer_profile_jobs (trainer_profile_id, jobs_id) values (?, ?) ...
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_6E ON PUBLIC.TRAINER_PROFILE_JOBS(TRAINER_PROFILE_ID, JOBS_ID) VALUES (77, 79, 2)"; SQL statement:
insert into trainer_profile_jobs (trainer_profile_id, jobs_id) values (?, ?) [23505-197]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:357) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.message.DbException.get(DbException.java:179) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.message.DbException.get(DbException.java:155) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.index.BaseIndex.getDuplicateKeyException(BaseIndex.java:101) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.mvstore.db.MVSecondaryIndex.requireUnique(MVSecondaryIndex.java:236) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.mvstore.db.MVSecondaryIndex.add(MVSecondaryIndex.java:202) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.mvstore.db.MVTable.addRow(MVTable.java:732) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.command.dml.Insert.insertRows(Insert.java:182) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.command.dml.Insert.update(Insert.java:134) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.command.CommandContainer.update(CommandContainer.java:102) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.command.Command.executeUpdate(Command.java:261) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:199) ~[h2-1.4.197.jar:1.4.197]
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:153) ~[h2-1.4.197.jar:1.4.197]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) ~[HikariCP-2.7.9.jar:na]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) ~[HikariCP-2.7.9.jar:na]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    ... 149 common frames omitted

目前只有一种方法可以使其发挥作用: 1. PUT 或 PATCH 与“工作”和“技能”的空数组 2.然后用新数据再次PUT或PATCH

这是我尝试 PUT 或 PATCH 数据之前实体的样子:

curl -s -XGET -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" http://localhost:2222/trainerProfiles/66
{
  ...
  "jobs" : [ {
    "period" : {
      "start" : "2015-01-02",
      "end" : "2017-01-25"
    },
    "company" : "Lorem ipsum dolor sit amet",
    "sector" : "Lorem ipsum dolor sit amet",
    "position" : "Lorem ipsum dolor sit amet",
    "description" : "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat."
  } ],
  "skills" : [ {
    "topic" : "java",
    "level" : "EXPERT"
  }, {
    "topic" : "spring",
    "level" : "EXPERT"
  } ],
  "_links" : {
    ...
  }
}%

如您所见,技能和工作属性已嵌入到配置文件实体中,因为我没有为它们创建导出的 JPA 存储库。这是有意的。

为了让您全面了解我的实体,我需要介绍一些继承自的基类:

public interface BaseEntity {
    public Long getId();    
    public void setId(Long id); 
}

@Data
@MappedSuperclass
public class AbstractBaseEntity implements BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
}

@Data
@EqualsAndHashCode(callSuper=true)
@MappedSuperclass
public abstract class AbstractOwnedEntity extends AbstractBaseEntity implements OwnedEntity {
    @NotNull
    @ManyToOne
    private Company owner;  
}

这是通过 Spring Data Rest 暴露的实体,这让我很头疼:

@NoArgsConstructor
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
public class Profile extends AbstractOwnedEntity {
    ...

    @NotNull
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<JobEntry> jobs = new HashSet<JobEntry>();

    @NotNull
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<SkillEntry> skills = new HashSet<SkillEntry>();

    public Profile(...) {
        ...
    }

    public void addJob(JobEntry job) {
        this.jobs.add(job);
    }

    public void addSkill(SkillEntry skill) {
        this.skills.add(skill);
    }
    ...
}

JPE Repository 是一个非常基础的 CrudRepository,带有一些 SPEL,以@PreAuthorize、@PostFilter 等形式实现访问控制(此处省略)

@RepositoryRestResource
public interface TrainerProfileRepository extends CrudRepository<TrainerProfile, Long> {
    ...
}

【问题讨论】:

  • 您的存储库引用了一个名为 TrainerProfile 的实体,但您发布了名为 Profile 的实体的代码???
  • 对不起,我的错。当我把这篇文章放在一起时,我玩了这些名字。它们是相同的实体。

标签: spring spring-data-jpa patch spring-data-rest put


【解决方案1】:

如果我没记错的话,我认为您需要公开未通过 Rest 存储库公开的实体的 ID - 在您的情况下,技能和工作:否则,框架如何知道您的 PUT/PATCH 请求是否是否引用现有实体。

您可以在配置中公开这 2 个类的 Id,如下所述:

How to expose the resourceId with Spring Data Rest

【讨论】:

    猜你喜欢
    • 2018-01-08
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多