Spring JPA复合主键:该类未定义IdClass

16

运行应用程序时出现错误 这个类[class com.socnetw.socnetw.model.Relationship]没有定义IdClass。当我使用EntityManager时一切都正常。但现在我转换到Spring的CrudRepository<T, T>并且出现了这个错误。我知道问题与映射主键约束有关。但我不知道具体该怎么做。有人能帮忙解决吗?

Relationship.class

@Table(name = "RELATIONSHIP")
@Getter
@Setter
@Entity
@ToString
@EqualsAndHashCode
public class Relationship implements Serializable {
    @Id
    private Long userIdFrom;
    @Id
    private Long userIdTo;
    @Enumerated(EnumType.STRING)
    private RelationshipStatus status;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "YYYY-MM-DD HH:mm:ss")
    private LocalDate friendsRequestDate;
}

RelationshipRepository.class 仅作为备选项

public interface RelationshipRepository extends CrudRepository<Relationship, Long> {

    @Query(value = "some query", nativeQuery = true)
    Long findAmountOfFriends(@Param("userId") Long userId);  
    ...other methods

}

DataInit.class

@Component
public class DataInit implements ApplicationListener<ContextRefreshedEvent> {
    private UserRepository userRepository;
    private PostRepository postRepository;
    private RelationshipRepository relationshipRepository;
    private MessageRepositorys messageRepository;

    public DataInit(UserRepository userRepository, PostRepository postRepository, RelationshipRepository relationshipRepository, MessageRepositorys messageRepository) {
        this.userRepository = userRepository;
        this.postRepository = postRepository;
        this.relationshipRepository = relationshipRepository;
        this.messageRepository = messageRepository;
    }

    @Override
    @Transactional
    public void onApplicationEvent(ContextRefreshedEvent event) {
     //here I create users and save them
    ...
    ...
    ...
    userRepository.save(someUser);


    relationshipRepository.save(relationship);
    messageRepository.save(message);

    }
}

错误

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataInit' defined in file [C:\Users\tpmylov\Desktop\learning\Projects\socnetw\target\classes\com\socnetw\socnetw\bootstrap\DataInit.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'relationshipRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.socnetw.socnetw.model.Relationship] does not define an IdClass
...
...
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'relationshipRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.socnetw.socnetw.model.Relationship] does not define an IdClass
2个回答

23

你有一个复合键:

@Id
private Long userIdFrom;
@Id
private Long userIdTo;

你需要创建一个IdClass:

public class RelationshipId implements Serializable {
    private Long userIdFrom;
    private Long userIdTo;

    // Getter and Setter
}

那么您可以将它用于该类

@IdClass(RelationshipId.class)
public class Relationship ....

在代码库中:

public interface RelationshipRepository 
                 extends CrudRepository<Relationship, RelationshipId> {

    @Query(value = "some query", nativeQuery = true)
    Long findAmountOfFriends(@Param("userId") Long userId);  
    ...other methods
}

在官方的Hibernate文档中阅读有关复合键的更多信息:

https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#identifiers-composite


https://www.baeldung.com/jpa-composite-primary-keys - Vikcen

7
你的实体关系声明假定使用复合主键。因此,你应该按照以下方式更正你的实体:
@Table(name = "RELATIONSHIP")
@Getter
@Setter
@Entity
@ToString
@EqualsAndHashCode
@IdClass(RelationshipPK.class)
public class Relationship implements Serializable {
    @Id
    private Long userIdFrom;
    @Id
    private Long userIdTo;
    @Enumerated(EnumType.STRING)
    private RelationshipStatus status;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "YYYY-MM-DD HH:mm:ss")
    private LocalDate friendsRequestDate;
}

然后是RelationshipPK

public class RelationshipPK implements Serializable {

    private Long userIdFrom;
    private Long userIdTo;

    public RelationshipPK(Long userIdFrom, Long userIdTo) {
        this.userIdFrom = userIdFrom;
        this.userIdTo = userIdTo;
    }

    public RelationshipPK() {
    }

    //Getters and setters are omitted for brevity

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }
        RelationshipPK pk = (RelationshipPK) o;
        return Objects.equals( userIdFrom, pk.userIdFrom ) &&
                Objects.equals( userIdTo, pk.userIdTo );
    }

    @Override
    public int hashCode() {
        return Objects.hash( userIdFrom, userIdTo );
    }
}

你的 CrudRepository 应该具有以下视图:

public interface RelationshipRepository extends CrudRepository<Relationship, RelationshipPK>
{

    @Query(value = "some query", nativeQuery = true)
    Long findAmountOfFriends(@Param("userId") Long userId);  
    ...other methods

}

实际上,Hibernate允许声明复合键并且不需要其他操作。这就是所谓的具有关联性的复合标识符。但是对于以下CrudRepository方法,这是必要的:
public interface CrudRepository<T,ID> extends Repository<T,ID>
{
   void deleteById(ID id);

   boolean existsById(ID id);

   Iterable<T> findAllById(Iterable<ID> ids);

   Optional<T>  findById(ID id);

   // ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接