如何对多对多关联表编写查询:筛选拥有指定技能的工匠
查询拥有指定技能的Craftsman实体方案
嘿,我来帮你搞定这个多对多关联的查询需求!先理清楚你的场景:Craftsman和Skill是多对多关联,关联表是craftman_skill,现在要找出所有拥有指定技能的工匠对吧?我先补全你没写完的实体关联代码,再给你几种实用的查询方案:
完整实体关联代码
首先得确保你的多对多关联配置正确,我把你没写完的部分补上:
Craftsman实体
@Entity @Table(name = "craftsman") public class Craftsman { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 可以添加其他业务字段,比如name、age等 private String name; @ManyToMany @JoinTable( name = "craftman_skill", // 关联表名称 joinColumns = @JoinColumn(name = "craftman_id", referencedColumnName = "id"), // 关联Craftsman的外键 inverseJoinColumns = @JoinColumn(name = "skill_id", referencedColumnName = "id") // 关联Skill的外键 ) private Set<Skill> skills; // 构造器、getter、setter方法 }
Skill实体(双向关联必备)
@Entity @Table(name = "skill") public class Skill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // 技能名称,比如"木工"、"铁匠" // 双向关联,mappedBy指向Craftsman类中的skills字段 @ManyToMany(mappedBy = "skills") private Set<Craftsman> craftsmen; // 构造器、getter、setter方法 }
实用查询方案
方案1:JPQL查询(日常开发最常用)
JPQL是JPA的查询语言,贴近实体类,不用关心底层表结构,非常方便。
按技能ID查询
// 编写JPQL语句,通过关联的skills筛选 String jpql = "SELECT c FROM Craftsman c JOIN c.skills s WHERE s.id = :skillId"; // 执行查询 List<Craftsman> craftsmen = entityManager.createQuery(jpql, Craftsman.class) .setParameter("skillId", 1L) // 替换成你要查询的目标技能ID .getResultList();
按技能名称查询(精确/模糊匹配)
// 精确匹配技能名称 String jpqlExact = "SELECT c FROM Craftsman c JOIN c.skills s WHERE s.name = :skillName"; // 模糊匹配(比如找所有包含"木"字的技能) String jpqlLike = "SELECT c FROM Craftsman c JOIN c.skills s WHERE s.name LIKE :skillPattern"; // 执行精确匹配示例 List<Craftsman> craftsmen = entityManager.createQuery(jpqlExact, Craftsman.class) .setParameter("skillName", "木工") .getResultList();
方案2:Criteria API(动态查询首选)
如果你的查询条件是动态变化的(比如可能同时按技能ID、名称甚至其他字段筛选),用Criteria API会更灵活,不用拼接字符串:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Craftsman> query = cb.createQuery(Craftsman.class); // 从Craftsman实体出发 Root<Craftsman> craftsmanRoot = query.from(Craftsman.class); // 关联到skills集合 Join<Craftsman, Skill> skillJoin = craftsmanRoot.join("skills"); // 设置筛选条件:技能ID等于指定值 Predicate condition = cb.equal(skillJoin.get("id"), 1L); query.select(craftsmanRoot).where(condition); // 去重:避免一个工匠拥有多个符合条件的技能导致重复返回 query.distinct(true); // 执行查询 List<Craftsman> craftsmen = entityManager.createQuery(query).getResultList();
方案3:原生SQL查询(复杂场景/性能优化)
如果需要直接操作数据库表,或者有复杂的SQL逻辑,用原生SQL更直接:
SELECT c.* FROM craftsman c JOIN craftman_skill cs ON c.id = cs.craftman_id JOIN skill s ON cs.skill_id = s.id WHERE s.id = ?; -- 替换成目标技能ID
Java代码中调用:
String sql = "SELECT c.* FROM craftsman c JOIN craftman_skill cs ON c.id = cs.craftman_id JOIN skill s ON cs.skill_id = s.id WHERE s.id = ?"; List<Craftsman> craftsmen = entityManager.createNativeQuery(sql, Craftsman.class) .setParameter(1, 1L) .getResultList();
注意事项
- 记得添加
distinct(true)(JPQL或Criteria API中),避免因为一个工匠拥有多个符合条件的技能而返回重复的实体。 - 检查关联表和字段名的拼写:你的关联表是
craftman_skill(注意和Craftsman实体的表名craftsman少了一个s,如果是笔误的话要统一,避免查询出错)。 - 如果是单向关联(只有Craftsman里配置了@ManyToMany,Skill里没有),查询逻辑完全不受影响,照样能用上面的方案。
内容的提问来源于stack exchange,提问作者Nemeth Attila
相关产品推荐
相关产品推荐

