"incorrect PK" : 对带有 @IdClass 注释的实体的 CrudRepository 删除操作
"incorrect PK" : delete operation on CrudRepository for entity annotated with @IdClass
上下文
当我的服务调用 repository.delete(myEntity)
时,出现以下错误:
105 | You have provided an instance of an incorrect PK class for this find operation. Class expected : class com.foo.PrimaryKey, Class received : class java.lang.String.
存储库界面extends CrudRepository<MyEntity, PrimaryKey>
。
我在 @Entity
class 上使用 @IdClass(PrimaryKey.class)
并用 @Id
标记了相关的各个字段。 PrimaryKey
中只有一个字段,它是 String
。我们这样做是为了保持我们的代码库一致,以便每个 @Entity
始终将其主键声明为复合键。
我自己的分析(可能有误)
出于某种原因,JPA 内部调用 em.find(MyEntity.class, primaryKeyString)
而不是从该字符串构建 PrimaryKey
实例并使用它,因此出现错误。
当我尝试调用 repository.deleteById(correspondingPrimaryKeyInstance)
时发生同样的错误。
框架代码
这来自 org.springframework.data.jpa.repository.support.SimpleJpaRepository
,我在失败的地方添加了评论:
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = (T) em.find(type, entityInformation.getId(entity)); // IT FAILS HERE... `entityInformation.getId(entity)` returns a String instead of the expected `PrimaryKey`
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
代码
- 存储库:
public interface MyEntityCRUDRepository extends CrudRepository<MyEntity, PrimaryKey> { }
public interface MyEntityCustomRepository { /* some custom operations */ }
@Repository
public interface MyEntityRepository extends MyEntityCRUDRepository, MyEntityCustomRepository { }
- 服务:
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class MyEntityService {
private final MyEntityRepository repository;
public void destroy(final PrimaryKey pk) {
// repository.deleteById(pk); // also returns the error
MyEntity myEntity = repository.findById(pk)
.orElseThrow(() -> new IllegalArgumentException(DOES_NOT_EXIST));
repository.delete(myEntity); // returns the error
}
}
- JPA 实体:
@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@IdClass(PrimaryKey.class)
public class MyEntity {
@Id
@Column(name = "the_id", updatable = false)
private String theId;
// other fields which aren't part of the primary key
}
- 主键class:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PrimaryKey implements Serializable {
private String theId;
}
临时修复:当您只有一个属性即 ID 时,不要使用 @IdClass
。
@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
//@IdClass(PrimaryKey.class)
public class TemporaryReservation {
@Id
@Column(name = "the_id", updatable = false)
private String theId;
// other fields which aren't part of the primary key
}
public interface MyEntityCRUDRepository extends CrudRepository<MyEntity, String> { }
更改后,对其余代码进行适当的调整。
原来这是 Spring Data
中的错误。查看相关(现已解决)问题:https://github.com/spring-projects/spring-data-jpa/issues/2330.
其中一位维护者说:
Another workaround is to let your entities implement Persistable
.
上下文
当我的服务调用 repository.delete(myEntity)
时,出现以下错误:
105 | You have provided an instance of an incorrect PK class for this find operation. Class expected : class com.foo.PrimaryKey, Class received : class java.lang.String.
存储库界面extends CrudRepository<MyEntity, PrimaryKey>
。
我在 @Entity
class 上使用 @IdClass(PrimaryKey.class)
并用 @Id
标记了相关的各个字段。 PrimaryKey
中只有一个字段,它是 String
。我们这样做是为了保持我们的代码库一致,以便每个 @Entity
始终将其主键声明为复合键。
我自己的分析(可能有误)
出于某种原因,JPA 内部调用 em.find(MyEntity.class, primaryKeyString)
而不是从该字符串构建 PrimaryKey
实例并使用它,因此出现错误。
当我尝试调用 repository.deleteById(correspondingPrimaryKeyInstance)
时发生同样的错误。
框架代码
这来自 org.springframework.data.jpa.repository.support.SimpleJpaRepository
,我在失败的地方添加了评论:
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = (T) em.find(type, entityInformation.getId(entity)); // IT FAILS HERE... `entityInformation.getId(entity)` returns a String instead of the expected `PrimaryKey`
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
代码
- 存储库:
public interface MyEntityCRUDRepository extends CrudRepository<MyEntity, PrimaryKey> { }
public interface MyEntityCustomRepository { /* some custom operations */ }
@Repository
public interface MyEntityRepository extends MyEntityCRUDRepository, MyEntityCustomRepository { }
- 服务:
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class MyEntityService {
private final MyEntityRepository repository;
public void destroy(final PrimaryKey pk) {
// repository.deleteById(pk); // also returns the error
MyEntity myEntity = repository.findById(pk)
.orElseThrow(() -> new IllegalArgumentException(DOES_NOT_EXIST));
repository.delete(myEntity); // returns the error
}
}
- JPA 实体:
@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@IdClass(PrimaryKey.class)
public class MyEntity {
@Id
@Column(name = "the_id", updatable = false)
private String theId;
// other fields which aren't part of the primary key
}
- 主键class:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PrimaryKey implements Serializable {
private String theId;
}
临时修复:当您只有一个属性即 ID 时,不要使用 @IdClass
。
@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
//@IdClass(PrimaryKey.class)
public class TemporaryReservation {
@Id
@Column(name = "the_id", updatable = false)
private String theId;
// other fields which aren't part of the primary key
}
public interface MyEntityCRUDRepository extends CrudRepository<MyEntity, String> { }
更改后,对其余代码进行适当的调整。
原来这是 Spring Data
中的错误。查看相关(现已解决)问题:https://github.com/spring-projects/spring-data-jpa/issues/2330.
其中一位维护者说:
Another workaround is to let your entities implement
Persistable
.