Spring Data Rest-在PUT或PATCH期间违反主键

时间:2018-10-28 08:54:51

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

通过Spring Data Rest放置或修补我的Profile实体时遇到问题。当我为“技能”或“工作”属性输入或修补相同或新的数据时,就会发生错误。

这是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

在尝试放置或修补数据之前,这是实体的外观:

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存储库是一个非常基本的CrudRepository,具有一些@@ PreAuthorize,@ PostFilter等形式的SPEL,以实现访问控制(此处省略)

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

1 个答案:

答案 0 :(得分:0)

如果我没记错的话,我认为您需要公开那些尚未通过Rest Repository公开的实体的ID-在您的情况下为Skills and Jobs:否则,框架如何知道您的PUT / PATCH请求是否为是否引用现有实体。

您可以在此处概述的配置中公开这2个类的ID:

How to expose the resourceId with Spring Data Rest