【发布时间】: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