@ManyToMany 的实体不使用 Spring-Boot & Spring-JPA 保存
Entity of @ManyToMany doesn't saved with Spring-Boot & Spring-JPA
我正在使用 Spring 引导和 MySQL。我试图在播放列表 table 中添加新实体(歌曲)。他们有多对多的关系。但是正如您在 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);
}
我在播放列表中保存歌曲的方法table
@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 数据库,那里什么也没有。这意味着我的程序确实正确地保存了多对多 tables。
https://i.stack.imgur.com/9lgW9.png
并且在日志中有任何异常。
所以我可以理解哪里出了问题。
希望有人有想法。
对于 SongEntity 的 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
,您没有任何明确包含的注释。您列出了一些要排除的内容,但根据 https://projectlombok.org/features/EqualsAndHashCode 上的文档,似乎需要您在使用此标志时将它们明确标记为已包含。
我不是 100% 确定当你把这个标志放在 class 级别但没有明确包含任何东西时 Lombok 做了什么,但它似乎是一个无效状态并且可能会弄乱你的Equals 和 Hashcode,因此搞乱了跟踪 HashSet 存储桶中代表您要添加的歌曲的项目的能力。
所以我会首先解决这个问题,这样您的 Equals/HashCode 就正确了。看起来您想要完全删除 onlyExplicitlyIncluded=true
设置 或者 您实际上添加了特定的包含。
如果您想坚持使用该路线,这里有一个讨论使用显式包含的话题:
另外,为什么你的关系上有@Transient 注释?该注释通常会告诉 EntityManager 忽略它所附加的内容。在您的情况下,如果您要向集合中添加某些内容但将集合标记为@Transient,那么直觉上它不应该影响数据库。建议您删除那些您希望 relationships/sets 中的对象更改实际反映在数据库中的注释。
我正在使用 Spring 引导和 MySQL。我试图在播放列表 table 中添加新实体(歌曲)。他们有多对多的关系。但是正如您在 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);
}
我在播放列表中保存歌曲的方法table
@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 数据库,那里什么也没有。这意味着我的程序确实正确地保存了多对多 tables。 https://i.stack.imgur.com/9lgW9.png
并且在日志中有任何异常。
所以我可以理解哪里出了问题。 希望有人有想法。
对于 SongEntity 的 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
,您没有任何明确包含的注释。您列出了一些要排除的内容,但根据 https://projectlombok.org/features/EqualsAndHashCode 上的文档,似乎需要您在使用此标志时将它们明确标记为已包含。
我不是 100% 确定当你把这个标志放在 class 级别但没有明确包含任何东西时 Lombok 做了什么,但它似乎是一个无效状态并且可能会弄乱你的Equals 和 Hashcode,因此搞乱了跟踪 HashSet 存储桶中代表您要添加的歌曲的项目的能力。
所以我会首先解决这个问题,这样您的 Equals/HashCode 就正确了。看起来您想要完全删除 onlyExplicitlyIncluded=true
设置 或者 您实际上添加了特定的包含。
如果您想坚持使用该路线,这里有一个讨论使用显式包含的话题:
另外,为什么你的关系上有@Transient 注释?该注释通常会告诉 EntityManager 忽略它所附加的内容。在您的情况下,如果您要向集合中添加某些内容但将集合标记为@Transient,那么直觉上它不应该影响数据库。建议您删除那些您希望 relationships/sets 中的对象更改实际反映在数据库中的注释。