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

Spring Data查询String类型多表多对多关联数据的问题

解决方案

错误原因分析

你遇到的'Many To Many' attribute value type should not be 'String'错误,核心原因是JPA的@ManyToMany注解要求关联字段必须是实体类类型,而非直接存储外键字符串(如tconst、nconst)。你的实体中knownForTitles、directors、writers均为String类型,不符合多对多关联的类型约束。


方案一:重构实体,使用标准JPA多对多关联

IMDB数据集里Name与Title的多对多关系(导演、编剧、knownFor)原本通过字符串集合存储外键,但JPA标准多对多需要中间关联表。若允许调整数据库结构,可按以下方式重构:

1. 调整Name实体

将knownForTitles(String集合)改为关联Title实体的集合,通过@ManyToMany指定中间表:

@Entity
@Table(name = "names")
public class Name {
    @Id
    private String nconst;
    private String primaryName;

    // 关联该人物的代表作品
    @ManyToMany
    @JoinTable(
        name = "name_known_for",
        joinColumns = @JoinColumn(name = "nconst"),
        inverseJoinColumns = @JoinColumn(name = "tconst")
    )
    private Set<Title> knownForTitles = new HashSet<>();

    // 其他字段、getter/setter方法
}

2. 调整Title实体

将directors、writers(String集合)改为关联Name实体的集合,分别配置@ManyToMany:

@Entity
@Table(name = "titles")
public class Title {
    @Id
    private String tconst;
    private String primaryTitle;

    // 关联作品的导演
    @ManyToMany
    @JoinTable(
        name = "title_directors",
        joinColumns = @JoinColumn(name = "tconst"),
        inverseJoinColumns = @JoinColumn(name = "nconst")
    )
    private Set<Name> directors = new HashSet<>();

    // 关联作品的编剧
    @ManyToMany
    @JoinTable(
        name = "title_writers",
        joinColumns = @JoinColumn(name = "tconst"),
        inverseJoinColumns = @JoinColumn(name = "nconst")
    )
    private Set<Name> writers = new HashSet<>();

    // 其他字段、getter/setter方法
}

3. 编写Repository查询

在TitleRepository中定义查询方法,获取指定nconst担任导演或编剧的作品:

public interface TitleRepository extends JpaRepository<Title, String> {
    // 方法名推导方式
    List<Title> findByDirectors_NconstOrWriters_Nconst(String nconst);

    // 或自定义JPQL查询
    @Query("SELECT t FROM Title t JOIN t.directors d JOIN t.writers w WHERE d.nconst = :nconst OR w.nconst = :nconst")
    List<Title> findByDirectorOrWriter(@Param("nconst") String nconst);
}

注意事项

  • 中间表name_known_for、title_directors、title_writers可通过JPA的spring.jpa.hibernate.ddl-auto=update自动生成,也可手动建表。
  • 若需保留原字符串外键字段,可添加@Transient注解让JPA忽略,仅用于数据导入等逻辑。

方案二:不修改实体结构,直接用原生SQL/JPQL查询

若不想调整数据库结构(比如直接导入IMDB原始数据),可直接编写查询语句绕过JPA多对多的关联要求:

1. 保留原有实体结构

假设Title实体的directors、writers存储逗号分隔的nconst(如nm0085156,nm1234567):

@Entity
@Table(name = "titles")
public class Title {
    @Id
    private String tconst;
    private String primaryTitle;
    private String directors; // 逗号分隔的导演nconst
    private String writers;   // 逗号分隔的编剧nconst

    // getter/setter方法
}

2. 在TitleRepository中编写查询

利用H2支持的字符串匹配逻辑,查询包含指定nconst的作品:

public interface TitleRepository extends JpaRepository<Title, String> {
    // JPQL查询(避免部分匹配)
    @Query("SELECT t FROM Title t WHERE CONCAT(',', t.directors, ',') LIKE CONCAT('%,', :nconst, ',%') OR CONCAT(',', t.writers, ',') LIKE CONCAT('%,', :nconst, ',%')")
    List<Title> findByDirectorOrWriter(@Param("nconst") String nconst);

    // 原生SQL查询
    @Query(value = "SELECT * FROM titles WHERE CONCAT(',', directors, ',') LIKE CONCAT('%,', ?1, ',%') OR CONCAT(',', writers, ',') LIKE CONCAT('%,', ?1, ',%')", nativeQuery = true)
    List<Title> findByDirectorOrWriterNative(String nconst);
}

注意事项

  • 使用CONCAT(',', ..., ',')是为了防止部分匹配(比如避免nm008515误匹配nm0085156)。
  • 若directors、writers仅存储单个nconst,可直接用=代替LIKE。

选择建议

  • 若项目需要完整的JPA实体关联(比如后续要通过Title获取导演详情、通过Name获取作品列表),优先选方案一,符合JPA规范。
  • 若仅需快速查询且不想修改现有数据库结构,选方案二更高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 01:45:47