休眠 ElementCollection/JoinTable IntegrityConstraintViolationException
Hibernate ElementCollection/JoinTable IntegrityConstraintViolationException
我有 3 个这样的 JPA 实体以及相应的 JPA 存储库。
@Entity
public class ChairEntity {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "chair_image")
private Set<ImageEntity> images = new HashSet<>();
...
}
@Entity
public class TableEntity {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "table_image")
private Set<ImageEntity> images = new HashSet<>();
...
}
@Entity
public class ImageEntity{
...
private String description;
@Lob
private byte[] data;
...
}
使用 REST-API 创建和更新这些对象。这通常很好用,例如我可以像这样一次添加多个 imageEntities(所有代码块都在它们自己的事务中)
chairEntity.getImages().add(new ImageEntity(..));
chairEntity.getImages().add(new ImageEntity(..));
chairRepository.save(chairEntity);
...或一次更新同一个 chairEntity 的多个 ImageEntities。
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairRepository.save(chairEntity);
在这两种情况下,所有更改都成功级联并保存。
但是,如果我在更新现有 ImageEntity 的同时添加另一个实体,则会失败:
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairEntity.getImages().add(new ImageEntity(...));
chairRepository.save(chairEntity); // crashes
异常如下(使用h2db抛出等效错误):
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "chair_image_pkey"
检查 DB-Log 时,Hibernate 似乎在尝试:
- 正在插入新图像(成功)
- 正在更新现有图像(成功)
- 正在向 Join-Table/Collection-Table (chair_image) 中插入一个引用椅子和现有图像的条目。然后抛出这个 JdbcSQLIntegrityConstraintViolationException,因为这个外键组合已经存在(旧图像之前已经存在)。
为什么会这样,我该如何解决?在同一事务中单独保存和刷新更改似乎也不起作用。
解决方法,以防其他人遇到此问题:颠倒操作顺序:
chairEntity.getImages().add(new ImageEntity(...));
chairRepository.saveAndFlush(chairEntity);
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairRepository.save(chairEntity); // crashes
hibernate 执行 SQL-Statements 的顺序保持不变,但由于它们之间的刷新,错误插入到 Join-Table 不再发生。
我有 3 个这样的 JPA 实体以及相应的 JPA 存储库。
@Entity
public class ChairEntity {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "chair_image")
private Set<ImageEntity> images = new HashSet<>();
...
}
@Entity
public class TableEntity {
...
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "table_image")
private Set<ImageEntity> images = new HashSet<>();
...
}
@Entity
public class ImageEntity{
...
private String description;
@Lob
private byte[] data;
...
}
使用 REST-API 创建和更新这些对象。这通常很好用,例如我可以像这样一次添加多个 imageEntities(所有代码块都在它们自己的事务中)
chairEntity.getImages().add(new ImageEntity(..));
chairEntity.getImages().add(new ImageEntity(..));
chairRepository.save(chairEntity);
...或一次更新同一个 chairEntity 的多个 ImageEntities。
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairRepository.save(chairEntity);
在这两种情况下,所有更改都成功级联并保存。
但是,如果我在更新现有 ImageEntity 的同时添加另一个实体,则会失败:
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairEntity.getImages().add(new ImageEntity(...));
chairRepository.save(chairEntity); // crashes
异常如下(使用h2db抛出等效错误):
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "chair_image_pkey"
检查 DB-Log 时,Hibernate 似乎在尝试:
- 正在插入新图像(成功)
- 正在更新现有图像(成功)
- 正在向 Join-Table/Collection-Table (chair_image) 中插入一个引用椅子和现有图像的条目。然后抛出这个 JdbcSQLIntegrityConstraintViolationException,因为这个外键组合已经存在(旧图像之前已经存在)。
为什么会这样,我该如何解决?在同一事务中单独保存和刷新更改似乎也不起作用。
解决方法,以防其他人遇到此问题:颠倒操作顺序:
chairEntity.getImages().add(new ImageEntity(...));
chairRepository.saveAndFlush(chairEntity);
chairEntity.getImages().stream().forEach(imageEntity -> {
imageEntity.setDescription("some other description");
}
chairRepository.save(chairEntity); // crashes
hibernate 执行 SQL-Statements 的顺序保持不变,但由于它们之间的刷新,错误插入到 Join-Table 不再发生。