首页 > 解决方案 > @ManyToMany 的实体不使用 Spring-Boot 和 Spring-JPA 保存

问题描述

我正在使用 Spring Boot 和 MySQL。我试图在播放列表表中添加新实体(歌曲)。他们有多对多的关系。但正如您在 mysql 查询后的回答中看到的那样,它没有保存。其他关系正常工作

播放列表实体

@Data
@Entity
@Component
@Getter
@EqualsAndHashCode(exclude = "songs")
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "playlists")
public class PlaylistEntity {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    private String playlistTitle;

    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE})
    @JoinColumn(name = "user_id", nullable = false)
    private UserEntity user;

    private LocalDateTime createdAt;

    public PlaylistEntity(String playlistTitle, UserEntity user, LocalDateTime createdAt) {
        this.playlistTitle = playlistTitle;
        this.user = user;
        this.createdAt = createdAt;
    }

    @Transient
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "playlist_song",
            joinColumns = @JoinColumn(name = "playlist_id", nullable=false),
            inverseJoinColumns = @JoinColumn(name = "song_id", nullable=false))
    private Set<SongEntity> songs = new HashSet<>();

}

播放列表存储库

@Repository
public interface PlaylistRepository extends PagingAndSortingRepository<PlaylistEntity, String> {
    @Query(value = "select * from playlists where user_id = :id", nativeQuery = true)
    List<PlaylistEntity> showAllUserPlaylists(@Param("id") String id);

    @Query(value = "select * from playlists where playlist_title = :playlist_title", nativeQuery = true)
    PlaylistEntity findByName(@Param("playlist_title") String playlist_title);
}

歌曲实体

@Data
@Entity
@Builder
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "songs")
public class SongEntity {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    private String title;

    private String artist;

    private String album;

    private String genre;

    @CreationTimestamp
    private LocalDateTime releaseDate;

    private int likes;

    @Transient
    @EqualsAndHashCode.Exclude
    @ManyToMany(mappedBy = "songs")
    private Set<PlaylistEntity> playlistEntities = new HashSet<>();

    @Transient
    @ManyToMany(mappedBy = "songs")
    @EqualsAndHashCode.Exclude
    private Set<SubscriptionEntity> subscriptionEntities = new HashSet<>();

    public SongEntity(String name) {
        this.title = name;
    }
}

歌曲库

@Repository
public interface SongRepository extends JpaRepository<SongEntity, String> {

    @Query(value="SELECT * FROM songs WHERE (:genre is null or genre = :genre) " +
            "AND (:artist IS NULL or artist = :artist)", nativeQuery=true)
    List<SongEntity> findByParams(@Param("genre") String genre, @Param("artist") String artist);

    @Query(value="SELECT * FROM songs WHERE artist = :artist", nativeQuery=true)
    List<SongEntity> findByArtist(@Param("artist") String artist);

    @Query(value="SELECT * FROM songs WHERE genre = :genre", nativeQuery=true)
    List<SongEntity> findByGenre(@Param("genre") String genre);

    @Query(value = "SELECT s.title, s.likes FROM SongEntity s WHERE s.artist = :artist")
    List<SongEntity> showSongsStatistics(@Param("artist") String artist);
}

我在播放列表中保存歌曲的方法

@Transactional
public Playlist addSongToPlaylist(String playlistId, String songId) throws Exception {

    SongEntity addedSong = findSongById(songId)
    PlaylistEntity requiredPlaylist = findPlaylistById(playlistId);

    requiredPlaylist.getSongs().add(addedSong);

    PlaylistEntity updatedPlaylist = playlistRepository.save(requiredPlaylist);

    return playlistConverter.fromEntity(updatedPlaylist);
}

和控制器

@Slf4j
@Configuration
@RestController
@AllArgsConstructor
@RequestMapping("/user/playlists")
public class PlaylistController {

    private final PlaylistService playlistService;

    @PostMapping(value = ADD_SONG_TO_PLAYLIST_URL)
    Playlist addSongToThePlaylist(@RequestParam String playlistId, @RequestParam String songId) throws Exception {
        return playlistService.addSongToPlaylist(playlistId, songId);
    }

    @UtilityClass
    public static class Links {
        public static final String ADD_SONG_TO_PLAYLIST_URL = "/addSong";
    }
}

我使用 Postman 提出请求。在请求后,我接受了这个答案,这表明该歌曲已添加到播放列表中。 https://i.stack.imgur.com/VdfbC.png

但正如我所说,如果检查 playlist_song db,它什么都没有。这意味着我的程序没有正确保存多对多表。 https://i.stack.imgur.com/9lgW9.png

在日志中有任何例外。

所以我可以理解什么是错的。希望有人有想法。

标签: javamysqlpostmanmany-to-many

解决方案


对于 SongEntity 的@EqualsAndHashCode(onlyExplicitlyIncluded = true),您没有任何明确包含的注释。您列出了一些要排除的内容,但根据https://projectlombok.org/features/EqualsAndHashCode上的文档,似乎需要您在使用此标志时将它们明确标记为已包含。

我不是 100% 确定 Lombok 在你将这个标志放在类级别时会做什么,但随后没有明确包含任何内容,但它似乎是一个无效状态,可能会弄乱你的 Equals 和 Hashcode 并因此搞乱能力跟踪 HashSet 存储桶中代表您要添加到的歌曲的项目。

所以我会先解决这个问题,这样你的 Equals/HashCode 是正确的。看起来您想要onlyExplicitlyIncluded=true完全删除设置,或者您实际上添加了特定的包含。

如果您想坚持使用该路线,这里有一个讨论使用显式包含的线程: 如何使用 @EqualsAndHashCode With Include - Lombok

另外,为什么你的关系上有@Transient 注释?该注释通常告诉 EntityManager 忽略它所附加的内容。在您的情况下,如果您要向 Set 添加一些内容但将 Set 标记为 @Transient,那么直观上它不应该影响数据库。建议您删除那些您希望对关系/集合中的对象的更改实际反映在数据库中的注释。


推荐阅读