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

Spring Boot多对多关联时artist_song中间表无数据填充问题

问题分析与解决方案

问题现象

运行Spring Boot应用后,artist表和song表数据正常填充,但多对多关联的中间表artist_song无数据。

核心原因

JPA中多对多关系分为维护端和被维护端:

  • Artist类是维护端:通过@JoinTable注解定义了中间表结构,负责管理关联关系的持久化。
  • Song类是被维护端:通过mappedBy="songs"声明自己不负责关联关系的维护。

你的代码中,通过Song.addArtist()方法建立关联,虽然双向集合都做了更新,但由于Song是被维护端,JPA不会主动将关联同步到中间表;同时,Artist的songs集合被修改时,Song属于未持久化的临时实体,未触发维护端的关联持久化逻辑,最终导致中间表无数据。

解决方案

方案1:从维护端(Artist)发起关联操作

修改CommandLineRunner中的代码,通过Artist.addSong()方法建立关联,利用维护端特性触发中间表更新:

@Bean
@Transactional
public CommandLineRunner commandLineRunner() {
    return args -> {
        // 创建艺术家
        Artist artist1 = new Artist("Sơn Tùng M-TP", "Vietnamese singer-songwriter and actor.");
        Artist artist2 = new Artist("tlinh", "tlinh is a Vietnamese independent singer and rapper.");
        Artist artist3 = new Artist("RPT MCK", "RPT MCK is a Vietnamese singer and rapper based in Ha Noi.");

        // 创建歌曲并从维护端添加关联
        Song song1 = new Song("nếu lúc đó", 3794587);
        artist2.addSong(song1);

        Song song2 = new Song("Chạy Ngay Đi", 234879825);
        artist1.addSong(song2);

        Song song3 = new Song("Hãy Trao Cho Anh", 2387532);
        artist1.addSong(song3);

        Song song4 = new Song("Chỉ Một Đêm Nữa Thôi (feat. tlinh)", 782643);
        artist2.addSong(song4);
        artist3.addSong(song4);

        Song song5 = new Song("Em Là Châu Báu", 298374);
        artist2.addSong(song5);
        artist3.addSong(song5);

        // 保存艺术家,由于Artist的cascade=CascadeType.ALL,关联的歌曲会被自动保存
        artistRepository.saveAll(List.of(artist1, artist2, artist3));
    };
}

方案2:从被维护端操作后,重新保存维护端实体

如果坚持使用Song.addArtist(),需要在建立关联后重新保存Artist(维护端),触发中间表更新:

// 创建歌曲并添加艺术家
Song song1 = new Song("nếu lúc đó", 3794587);
song1.addArtist(artist2);
songRepository.save(song1);
artistRepository.save(artist2); // 重新保存维护端实体

// 其他歌曲同理...

额外优化

去掉Song构造函数中重复初始化artists集合的代码(字段定义时已初始化new HashSet<>()):

public Song(String songName, long songStream) {
    this.songName = songName;
    this.songStream = songStream;
    // 移除 this.artists = new HashSet<>();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 18:32:32