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
相关产品推荐
相关产品推荐

