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

如何对多对多关联表编写查询:筛选拥有指定技能的工匠

查询拥有指定技能的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:03:16