Spring Data JPA复合键关联查询问题:实体映射与SQL等效实现
实体映射问题修正
你的实体映射存在几处关键问题,需要调整:
Account实体的@OneToMany关联缺失配置
当前@OneToMany未指定关联字段,JPA无法识别Account与AccountBalance的关联关系,需补充mappedBy属性指向AccountBalance中关联Account的字段:@Entity @IdClass(AccountCompositeKey.class) @Data public class Account { @Id @Column(name = "customerId") private int customerId; @Id @Column(name = "account_id") private UUID accountId; private String country; // 补充mappedBy,指向AccountBalance中的account关联字段 @OneToMany(mappedBy = "account") private List<AccountBalance> balances; }AccountBalance实体的关联配置错误
- 同时使用
@JoinColumn和@Column会造成配置冲突,应移除@Column,@JoinColumn已指定数据库列名; - 需要添加
@ManyToOne注解建立与Account的双向关联,有两种实现方式:
方式一:直接关联Account实体作为主键字段
方式二:保留UUID类型的accountId作为主键,通过@Entity @Table(name= "account_balance") @IdClass(AccountBalanceCompositeKey.class) @Data public class AccountBalance { @Id @ManyToOne @JoinColumn(name="account_id") private Account account; @Id @Enumerated(EnumType.STRING) private CurrencyEnum currency; private BigDecimal amount; }@MapsId映射关联@Entity @Table(name= "account_balance") @IdClass(AccountBalanceCompositeKey.class) @Data public class AccountBalance { @Id @Column(name = "account_id") private UUID accountId; @Id @Enumerated(EnumType.STRING) private CurrencyEnum currency; private BigDecimal amount; @ManyToOne @MapsId("accountId") @JoinColumn(name = "account_id") private Account account; }
- 同时使用
复合主键类的强制要求
AccountCompositeKey和AccountBalanceCompositeKey必须满足:- 实现
Serializable接口; - 重写
equals()和hashCode()方法; - 字段名、类型与实体类中的主键字段完全匹配。
- 实现
实现等效SQL的JPA查询
针对你需要的SQL查询,提供三种实现方式:
方式1:原生SQL查询
先定义DTO接收结果:
@Data public class AccountBalanceDto { private UUID accountId; private BigDecimal amount; private int customerId; }
在AccountBalanceRepository中添加查询方法:
@Repository public interface AccountBalanceRepository extends JpaRepository<AccountBalance, AccountBalanceCompositeKey> { @Query(value = "SELECT A.ACCOUNT_ID, A.AMOUNT, B.CUSTOMER_ID FROM ACCOUNT_BALANCE A JOIN ACCOUNT B ON A.ACCOUNT_ID=B.ACCOUNT_ID WHERE A.ACCOUNT_ID=?", nativeQuery = true) List<AccountBalanceDto> findByAccountIdNative(UUID accountId); }
方式2:JPQL查询
使用JPQL替代原生SQL,支持DTO构造:
@Repository public interface AccountBalanceRepository extends JpaRepository<AccountBalance, AccountBalanceCompositeKey> { @Query("SELECT new com.yourpackage.AccountBalanceDto(ab.accountId, ab.amount, a.customerId) FROM AccountBalance ab JOIN ab.account a WHERE ab.accountId = :accountId") List<AccountBalanceDto> findByAccountIdJpql(@Param("accountId") UUID accountId); }
注意替换com.yourpackage为DTO实际包路径。
方式3:Spring Data投影
定义投影接口,Spring Data自动映射结果:
public interface AccountBalanceProjection { UUID getAccountId(); BigDecimal getAmount(); int getCustomerId(); }
在Repository中定义方法:
@Query("SELECT ab.accountId as accountId, ab.amount as amount, a.customerId as customerId FROM AccountBalance ab JOIN ab.account a WHERE ab.accountId = :accountId") List<AccountBalanceProjection> findByAccountIdProjection(@Param("accountId") UUID accountId);
原Repository方法问题说明
你之前定义的Account findByAccountId(UUID accountId)存在逻辑歧义:Account的复合主键是customerId+accountId,同一个accountId可能对应多条Account记录,JPA无法确定返回单条结果,应改为返回List<Account>或调整查询逻辑。
内容的提问来源于stack exchange,提问作者Abhilash
相关产品推荐
相关产品推荐

