Home > Blockchain >  "incorrect PK" : delete operation on CrudRepository for entity annotated with @IdClass
"incorrect PK" : delete operation on CrudRepository for entity annotated with @IdClass

Time:10-13

Context

When my service calls repository.delete(myEntity), I get the following error:

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.

The repository interface extends CrudRepository<MyEntity, PrimaryKey>.

I use @IdClass(PrimaryKey.class) on my @Entity class and have labeled the relevant individual fields with @Id. There is a single field in the PrimaryKey, and it's a String. We do this to keep our codebase consistent so that every @Entity always declares its primary key as a composite key.

My own analysis (could be wrong)

For some reason, JPA internally calls em.find(MyEntity.class, primaryKeyString) instead of building a PrimaryKey instance from that String and using it, hence the error.

The same error happens when I try to call repository.deleteById(correspondingPrimaryKeyInstance) instead.

Framework code

This comes from org.springframework.data.jpa.repository.support.SimpleJpaRepository, and I added a comment where things fail:

    @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));
    }

Code

  • Repository:
public interface MyEntityCRUDRepository extends CrudRepository<MyEntity, PrimaryKey> { }
public interface MyEntityCustomRepository { /* some custom operations */ }

@Repository
public interface MyEntityRepository extends MyEntityCRUDRepository, MyEntityCustomRepository { }
  • Service:
@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:
@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
}
  • Primary key class:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PrimaryKey implements Serializable {
    private String theId;
}

CodePudding user response:

A temporary fix: don't use @IdClass when you have only a single attribute that is the ID.

@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> { }

Once that's changed, make the appropriate adjustments in the rest of the code.

CodePudding user response:

Turns out it was a bug in Spring Data. See the related (now solved) issue: https://github.com/spring-projects/spring-data-jpa/issues/2330.

One of the maintainers said:

Another workaround is to let your entities implement Persistable.

  • Related