如何编写Jpa Specification实现两张表关联查询?附实体类示例
嘿,我来帮你搞定这个JPA Specification关联查询的问题!首先得先把两个实体的关联关系给明确下来——你现在的实体代码里没体现关联映射,这是实现关联查询的前提哦。
第一步:完善实体的关联映射
首先我们需要在两个实体之间建立关联,假设业务场景是一个Reward可以对应多个RewardUserRelation记录(也就是一对多关系),那我们需要给两个实体补充关联注解:
1. 给Reward实体添加一对多关联
@Data @Entity @Table(name = "reward") public class Reward{ @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "org.hibernate.id.UUIDGenerator") @Column(length = 36) private String id; private String sn; // 一对多关联RewardUserRelation,mappedBy指向关联实体里的Reward属性名 @OneToMany(mappedBy = "reward", fetch = FetchType.LAZY) private List<RewardUserRelation> rewardUserRelations; // 其他字段 }
2. 给RewardUserRelation实体添加多对一关联
@Data @Entity @Table(name = "strategy_rewards") public class RewardUserRelation{ @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "org.hibernate.id.UUIDGenerator") private String id; // 多对一关联Reward,JoinColumn指定数据库中外键字段名和关联的主键字段 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "reward_id", referencedColumnName = "id") private Reward reward; // 其余代码省略 }
第二步:编写JPA Specification实现关联查询
接下来我们就可以通过Specification来构建关联查询的条件了,下面举两个常见的场景例子:
场景1:根据Reward的sn查询对应的RewardUserRelation记录
我们可以写一个静态方法来生成对应的Specification:
import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.*; public class RewardUserRelationSpecs { /** * 根据Reward的sn查询关联的RewardUserRelation记录 */ public static Specification<RewardUserRelation> hasRewardSn(String targetSn) { return (root, query, criteriaBuilder) -> { // 内连接Reward表,root是RewardUserRelation的查询根对象 Join<RewardUserRelation, Reward> rewardJoin = root.join("reward", JoinType.INNER); // 构建条件:Reward的sn等于目标sn return criteriaBuilder.equal(rewardJoin.get("sn"), targetSn); }; } }
场景2:查询所有存在关联RewardUserRelation的Reward记录
如果需要从Reward出发关联查询,我们可以写另一个Specification:
public class RewardSpecs { /** * 查询所有存在关联RewardUserRelation的Reward */ public static Specification<Reward> hasRelatedUserRelation() { return (root, query, criteriaBuilder) -> { // 内连接RewardUserRelation表 Join<Reward, RewardUserRelation> relationJoin = root.join("rewardUserRelations", JoinType.INNER); // 只要关联记录存在即可,这里用判断关联ID不为空来实现 return criteriaBuilder.isNotNull(relationJoin.get("id")); }; } }
如何使用这些Specification
首先你的Repository需要继承JpaSpecificationExecutor接口,比如:
// RewardUserRelation的Repository public interface RewardUserRelationRepository extends JpaRepository<RewardUserRelation, String>, JpaSpecificationExecutor<RewardUserRelation> { } // Reward的Repository public interface RewardRepository extends JpaRepository<Reward, String>, JpaSpecificationExecutor<Reward> { }
然后在业务代码里直接调用即可:
// 示例1:查询sn为"SN202405"的Reward对应的所有关联记录 List<RewardUserRelation> relations = rewardUserRelationRepository.findAll(RewardUserRelationSpecs.hasRewardSn("SN202405")); // 示例2:查询所有存在关联关系的Reward List<Reward> rewards = rewardRepository.findAll(RewardSpecs.hasRelatedUserRelation());
一些注意事项
- 关联字段的属性名要和join方法里的字符串完全一致,比如
root.join("reward")对应RewardUserRelation里的private Reward reward;属性; - FetchType的选择:
LAZY是懒加载(默认),EAGER是立即加载,根据业务场景选择,避免不必要的性能损耗; - JoinType的选择:
INNER是内连接(只返回两边都匹配的记录),LEFT是左连接(返回左边表的所有记录,右边匹配不到则为null),根据查询需求选择; - 如果需要组合多个条件,可以用
criteriaBuilder.and()或criteriaBuilder.or()来拼接,比如:return criteriaBuilder.and( criteriaBuilder.equal(rewardJoin.get("sn"), targetSn), criteriaBuilder.isNotNull(root.get("userId")) );
内容的提问来源于stack exchange,提问作者Azarea
相关产品推荐
相关产品推荐

