Micronaut Data JDBC+JPA注解下findAll/findById无法映射实体对象
Micronaut Data JDBC + JPA 实体属性映射偶发失效问题(Micronaut 4.7.3)
在Micronaut 4.7.3环境中,使用Micronaut Data结合JDBC与JPA注解开发时,调用IJobRepository的findAll或findById方法后,返回的Job实体对象属性未正确映射。已尝试为Job实体添加@Introspected注解、将增量编译配置incremental设为false,修改Service层代码后问题仍偶发,执行gradlew clean与build操作也无法解决。
依赖配置
dependencies { annotationProcessor("org.projectlombok:lombok") annotationProcessor("io.micronaut:micronaut-http-validation") annotationProcessor("io.micronaut.data:micronaut-data-processor") annotationProcessor("io.micronaut.openapi:micronaut-openapi") annotationProcessor("io.micronaut.serde:micronaut-serde-processor") annotationProcessor("io.micronaut.validation:micronaut-validation-processor") implementation("io.micronaut:micronaut-jackson-databind") implementation("school.staff:common:0.0.1") implementation("io.micronaut.data:micronaut-data-jdbc") implementation("io.micronaut:micronaut-management") implementation("org.flywaydb:flyway-core:11.1.0") implementation("io.micronaut.flyway:micronaut-flyway") implementation("io.micronaut.validation:micronaut-validation") compileOnly("org.projectlombok:lombok") compileOnly("io.micronaut:micronaut-http-client") compileOnly("jakarta.persistence:jakarta.persistence-api") compileOnly("io.micronaut.openapi:micronaut-openapi-annotations") runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("org.postgresql:postgresql") runtimeOnly("io.micronaut.sql:micronaut-jdbc-hikari") runtimeOnly("org.flywaydb:flyway-database-postgresql") // 用于Micronaut测试资源的PostgreSQL支持,否则会抛出版本不兼容错误 testImplementation("io.micronaut:micronaut-http-client") testRuntimeOnly("org.postgresql:postgresql") }
Repository代码
@JdbcRepository(dialect = Dialect.POSTGRES) public interface IJobRepository extends CrudRepository<Job, UUID>, JpaSpecificationExecutor<Job> { Optional<Job> findById(UUID id); Optional<Job> findByTitle(String title); }
Service层代码
初始实现
@Singleton public non-sealed class JobService implements IJobService { @Override public Result<ResponseMeta<List<JobResponse>>> find(FilterSortRequest jobRequest) { var result = iJobRepository.findAll() .stream() .map(jobResponseMapper) .toList(); ResponseMeta.Metadata metadata = ResponseMeta.Metadata.builder() .supportedFilters(Job.SUPPORTED_FILTERS) .supportedSorts(Job.SUPPORTED_SORTS) .build(); return Result.of(new ResponseMeta<>(result, metadata)); } @Override public Result<JobResponse> get(@Nonnull UUID id) { return iJobRepository.findById(id) .map(x -> Result.of(jobResponseMapper.apply(x))) .orElseGet(() -> Result.of(new Exception(ApplicationMessage.TagMessage.TAG_NOTFOUND))); } }
修改后的测试实现(问题仍偶发)
@Override public Result<JobResponse> get(@Nonnull UUID id) { var job = iJobRepository.findById(id).orElse(null); if (job == null) Result.of(new Exception(ApplicationMessage.TagMessage.TAG_NOTFOUND)); return Result.of(jobResponseMapper.apply(job)); }
Job实体类
@AllArgsConstructor @Builder @Setter @Getter @Entity public final class Job extends AuditTrail { @NotNull private String title; @TypeDef(type = DataType.JSON) @Column(columnDefinition = "jsonb") @Nullable private List<UUID> tag = new ArrayList<>(); @Column(columnDefinition = "TEXT") private String description; @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") @NotNull private Instant startDate; @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") @NotNull private Instant endDate; @Enumerated(EnumType.STRING) @NotNull private JobType type; @Enumerated(EnumType.STRING) @Column(length = 100) @NotNull private TeachingLevel teachingLevel; private String externalLink; @Enumerated(EnumType.STRING) @Column(length = 100) @NotNull private ResidenceStatus residenceStatus; @Enumerated(EnumType.STRING) @Column(length = 100) @NotNull private Experience experience; private String subjectToTeach; @TypeDef(type = DataType.JSON) @Column(columnDefinition = "jsonb") @Nullable private List<UUID> perk; @Enumerated(EnumType.STRING) @Column(length = 100) @NotNull private JobStatus status; public static final List<SortOption> SUPPORTED_SORTS = List.of( SortOption.of("title", SortOperators.ASC) ); public static final List<FilterOption> SUPPORTED_FILTERS = List.of( FilterOption.of("title", FilterOperators.EQUAL, Collections.emptyMap()), FilterOption.of("startDate", FilterOperators.IN, Collections.emptyMap()) ); public Job() { super(); } }
AuditTrail父类
@MappedSuperclass @Setter @Getter @NoArgsConstructor @AllArgsConstructor @EntityListeners(AuditTrailEventListener.class) public sealed class AuditTrail extends EntityId permits Job { private UUID createdBy; @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private Instant createdDate; private UUID lastModifiedBy; @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private Instant lastModifiedDate; }
EntityId父类
@NoArgsConstructor @Getter @Setter @AllArgsConstructor @MappedSuperclass public sealed class EntityId permits AuditTrail { @Id @AutoPopulated private UUID id; }
排查与解决建议
- Lombok兼容性调整:当前Lombok仅作为编译依赖引入,尝试添加
implementation("org.projectlombok:lombok"),或暂时移除Lombok手动编写getter/setter和构造函数,验证是否是Lombok生成代码与Micronaut代理冲突导致的问题。 - 密封类兼容性测试:移除Job、AuditTrail、EntityId的
sealed修饰符,改为普通继承类,测试是否还会出现属性映射失效——Micronaut Data对密封类的代理生成可能存在偶发bug。 - JSON字段映射优化:将
@TypeDef(type = DataType.JSON)替换为JPA标准的@Type(type = "jsonb"),或检查是否缺少PostgreSQL JSON处理的依赖配置。 - Repository接口简化:暂时移除
JpaSpecificationExecutor继承,仅保留CrudRepository,避免Micronaut Data同时处理JDBC和JPA规范导致的代理生成异常;若需要JPA规范支持,改用JpaRepository替代CrudRepository。 - 版本一致性检查:确保所有Micronaut相关依赖(core、data、jdbc等)版本统一为4.7.3,使用Micronaut BOM管理版本可避免版本不一致问题。
- 实体监听器排查:暂时注释
@EntityListeners(AuditTrailEventListener.class),验证监听器是否在实体加载时干扰了属性赋值流程。
内容的提问来源于stack exchange,提问作者San Jaisy
相关产品推荐
相关产品推荐

