【问题标题】:Spring Boot JPA - Entity failing due to calling getter methodSpring Boot JPA - 由于调用 getter 方法而导致实体失败
【发布时间】:2022-01-25 05:52:10
【问题描述】:

自从从 Spring Boot 2.2.x 升级到 2.6.x(以及 Java 11 -> 17)后,我们的实体类在某些存储库方法方面存在问题。我们使用 UUID 作为我们的数据库 id,但在域层中也有强类型的 id 以保证类型安全。 DB 是 Postgres。

示例(带有一些额外的注释,并不能解决问题):

@Entity
@Access(AccessType.FIELD)
public class User {
  @Id private UUID id;
  
  @Transient
  public UserId id() {
    return new UserId(id);
  }
}

我们的存储库类:

public interface UserJPARepository extends CrudRepository<User, UUID> { }

以下失败:

repository.deleteById(userId.id());

有错误:

org.springframework.dao.InvalidDataAccessApiUsageException: Provided id of the wrong type for class com.example.User. Expected: class java.util.UUID, got class com.example.CycleId; nested exception is java.lang.IllegalArgumentException: Provided id of the wrong type for class com.example..Cycle. Expected: class java.util.UUID, got class com.example.CycleId

问题似乎是 JPA 正在使用反射来确定 public UserId id() 是 Id 的正确 getter,但是由于此方法将 UUID 转换为 UserId,因此它失败了。重命名 id() 方法可以解决问题,但这并不理想。

在我的id() 方法中添加断点,我可以看到调用来自JpaMetamodalEntityInformation.getId

if (idMetadata.hasSimpleId()) {
            return (ID) entityWrapper.getPropertyValue(idMetadata.getSimpleIdAttribute().getName());
}

TransientAccess 注释无助于解决问题。

在 Spring Boot 2.2 中一切正常。升级引入了这个问题。

【问题讨论】:

    标签: java spring-boot hibernate spring-data-jpa java-17


    【解决方案1】:

    deleteById 的存储库接口需要 UUID 类型的输入参数,这是在 extends CrudRepository... 语句中指定的 ID 类型。您正在尝试调用该方法并改为传递 UserId 对象。此外,正如您已经指出的那样,您的 id() 函数名称确实与属性 id 的 getter 命名约定冲突。

    这里有几种不同的方法可以解决这个问题:

    • 在存储库接口上声明一个 deleteByUserId 方法

    • 更改您的存储库接口声明以使用 UserId 而不是 UUID 的密钥类型

    • 重构您的模型,为 ID 属性提供适当命名的 getter 和 setter 以公开它

    【讨论】:

    • 抱歉,应该包含我的 UserId 类。 userId.id() 返回一个 UUID(否则代码甚至无法编译)。
    • 明白了。如果向返回 UUID 的 User 实体添加 getId 函数,反射会更喜欢吗?
    猜你喜欢
    • 2017-07-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    • 2016-12-03
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    相关资源
    最近更新 更多