hibernate中的级联插入以及级联表的唯一约束

Ale*_*min 4 java hibernate

我有以下 SQL 架构布局:

-- postgresql82+ syntax

create table AudioTracks (
  id serial primary key 
, name text
, size integer
, filePath text
, additionDate timestamp default now()
);

create table Genres (
  id serial primary key
, name text unique -- here is the unique constraint which troubles me 
, description text
, additionDate timestamp default now()
);

create table AudioTrackGenre (
  genreId integer references Genres (id) unique 
, audioTrackId integer references AudioTracks (id) unique 
, additionDate timestamp default now()
);
Run Code Online (Sandbox Code Playgroud)

以及两个对应的表映射:

@Entity(name = "AudioTracks")
public class AudioTrack implements Serializable {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column
    private String name;

    @Column
    private Integer size;

    @Column
    private String filePath;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date additionDate;


    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
    @JoinTable(name = "AudioTrackGenre",
            joinColumns =  { @JoinColumn(name = "audioTrackId") },
            inverseJoinColumns = { @JoinColumn(name = "genreId") }
    )
    @OrderBy("name")
    private Set<Genre> genres = new HashSet<Genre>();

    // setter/getter methods //
    ....
}
Run Code Online (Sandbox Code Playgroud)

@Entity(name = "Genres")
public class Genre implements Serializable {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column
    private String name;

    @Column
    private String description;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date additionDate;

    @ManyToMany(mappedBy = "genres")
    private Set<AudioTrack> audioTracks = new HashSet<AudioTrack>();

    public Genre() { }
    public Genre(String name) { this.name = name; }

    // setter/getter methods //
    ....
}
Run Code Online (Sandbox Code Playgroud)

但是每当我尝试保存 AudioTrack 时,都会填充流派表中已经存在的流派,如下所示:

        Set<Genre> genres = new HashSet<Genre>();
        genres.add(new Genre("ambient"));

        AudioTrack track = new AudioTrack();
        track.setGenres(genres);

        audioTrackService.addAudioTrack(track);
Run Code Online (Sandbox Code Playgroud)

(该事情在最低 DAO 级别audioTrackService.addAudioTrack(track)执行)sessionFactory.getCurrentSession().save(track)

我正进入(状态:

ERROR: ERROR: duplicate key value violates unique constraint "genres_name_key"
  Detail: Key (name)=(ambient) already exists.
Run Code Online (Sandbox Code Playgroud)

我如何告诉 Hibernate 不要尝试将已经存在的流派重新插入Genres级联插入表中?

Pet*_*hev 5

如果该类型已存在,则必须提供其 ID。

看这一行:new Genre("ambient")

hibernate 怎么可能猜测 Genre 环境的现有 id 是什么?

Hibernate 尝试插入相应的流派,因为您尚未提供其 id。

当您插入音轨时,hibernate 必须在 AudioTrackGenre 表中插入记录。Hibernate 必须知道流派的 id。否则,hibernate 会假设它们是新流派。

编辑:

看来您正在按需添加流派(例如 StackOverflow 标签)。

您可以在代码中执行以下操作:

for (String genreName : submittedTextGenres) {
   Genre genre = genreDAO.findByName(genre);
   if (genre == null) { //a new genre
      genre = new Genre(genreName);
   }

   audioTrack.addGenre(genre);
}
Run Code Online (Sandbox Code Playgroud)

如果您担心并发用户添加相同的流派:(建议JB Nizet

for (String genreName : submittedTextGenres) {
   Genre genre = genreDAO.findByName(genre);
   if (genre == null) { //a new genre
      try {
         genre = genreDAO.insertGenre(genre); //a transaction
      } catch (GenreExistsException) {
         genre = genreDAO.findByName(genre); //a separate transaction
      }
   }

   audioTrack.addGenre(genre);
}
Run Code Online (Sandbox Code Playgroud)