You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Micronaut Data(JDBC)中获取关联实体的主键?

问题:Micronaut Data JDBC中如何在投影DTO中包含关联实体的主键?

背景

我正在使用Micronaut Data搭配micronaut-jdbc-hikari驱动(本质为JDBC),采用4.5.0版本的平台BOM。需求如下:

  • 两个实体类为ManyToOne关联关系
  • 获取实体的投影DTO,仅需包含关联实体的主键(已知Micronaut Data不支持嵌套投影,此限制可接受)

相关代码示例

Transaction实体类

@MappedEntity
public record Transaction(
    @NonNull @NotNull @NotBlank @Id String id,
    @NonNull @NotNull @NotBlank LocalDate madeOn,
    @NonNull @NotNull @NotBlank @Size(max = 3) String currencyCode,
    @NotNull @NonNull @NotBlank String category,
    @NotNull @NonNull @NotBlank Boolean duplicated,
    @NotNull @NonNull @NotBlank LocalDateTime createdAt,
    @NotNull @NonNull @NotBlank LocalDateTime updatedAt,
    @Nullable @Relation(value = Kind.MANY_TO_ONE) Account account
) {

}

Account实体类

@MappedEntity
public record Account(
    @NonNull @NotNull @NotBlank @Id String id,
    @NonNull @NotNull @NotBlank String name,
    @NonNull @NotNull @NotBlank @Size(max = 3) String currencyCode,
    @NonNull @NotNull @NotBlank LocalDateTime createdAt,
    @NonNull @NotNull @NotBlank LocalDateTime updatedAt
) {

}

目标投影DTO

@Introspected
@Serdeable
public record TransactionDTO(
    String id,
    String mode,
    LocalDate madeOn,
    String accountId  // 此字段无法正常映射,导致编译报错
) {

}

Repository接口

@JdbcRepository(dialect = Dialect.POSTGRES)
public interface TransactionRepository extends PageableRepository<Transaction, String> {

  Page<TransactionDTO> listByAccountInListAndMadeOnBetweenOrderByMadeOnDesc(
      List<Account> accounts, LocalDate from, LocalDate to, Pageable pageable
  );
}

上述代码编译失败,提示accountId是无效字段,因为Micronaut Data无法自动关联Transaction.account.id到DTO的accountId字段。


解决方案

方法1:使用@Query显式指定字段映射

在Repository方法上添加@Query注解,通过JPQL明确指定要查询的字段,并将关联主键别名设置为DTO中的accountId:

@JdbcRepository(dialect = Dialect.POSTGRES)
public interface TransactionRepository extends PageableRepository<Transaction, String> {

  @Query("SELECT t.id, t.mode, t.madeOn, t.account.id AS accountId " +
         "FROM Transaction t " +
         "WHERE t.account IN :accounts AND t.madeOn BETWEEN :from AND :to " +
         "ORDER BY t.madeOn DESC")
  Page<TransactionDTO> listByAccountInListAndMadeOnBetweenOrderByMadeOnDesc(
      List<Account> accounts, LocalDate from, LocalDate to, Pageable pageable
  );
}

注意:JPQL中的别名accountId必须与DTO的字段名完全一致,Micronaut Data会自动完成映射。

方法2:在实体类中添加派生字段

在Transaction实体中添加一个派生的accountId字段,通过@Property注解指定其映射来源为关联实体的主键,同时用@Transient标记该字段不会生成数据库列:

@MappedEntity
public record Transaction(
    @NonNull @NotNull @NotBlank @Id String id,
    @NonNull @NotNull @NotBlank LocalDate madeOn,
    @NonNull @NotNull @NotBlank @Size(max = 3) String currencyCode,
    @NotNull @NonNull @NotBlank String category,
    @NotNull @NonNull @NotBlank Boolean duplicated,
    @NotNull @NonNull @NotBlank LocalDateTime createdAt,
    @NotNull @NonNull @NotBlank LocalDateTime updatedAt,
    @Nullable @Relation(value = Kind.MANY_TO_ONE) Account account,
    @Transient @Property(name = "account.id") String accountId // 新增派生字段
) {

}

修改后,原Repository方法无需额外修改即可正常编译并映射accountId到DTO。

方法3:使用原生SQL优化查询(推荐)

直接查询数据库中的外键列(假设transaction表的外键列为account_id),同时将参数改为List<String>类型的账户ID,避免对象转换开销:

@JdbcRepository(dialect = Dialect.POSTGRES)
public interface TransactionRepository extends PageableRepository<Transaction, String> {

  @Query(value = "SELECT t.id, t.mode, t.made_on, t.account_id AS accountId " +
                 "FROM transaction t " +
                 "WHERE t.account_id IN (:accountIds) AND t.made_on BETWEEN :from AND :to " +
                 "ORDER BY t.made_on DESC",
         countQuery = "SELECT COUNT(*) FROM transaction t " +
                      "WHERE t.account_id IN (:accountIds) AND t.made_on BETWEEN :from AND :to")
  Page<TransactionDTO> listByAccountIdsAndMadeOnBetweenOrderByMadeOnDesc(
      List<String> accountIds, LocalDate from, LocalDate to, Pageable pageable
  );
}

注意:原生SQL需要确保表名、列名与数据库实际结构一致,同时需要单独指定countQuery用于分页计数。


内容的提问来源于stack exchange,提问作者Fohlen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:35:06