Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cascade insert in hibernate and unique constraint on cascading table

Tags:

java

hibernate

I have a following SQL schema layout:

-- 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()
);

And two corresponding mappings to tables:

@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 //
    ....
}

and

@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 //
    ....
}

But whenever i am trying to save AudioTrack, populated with Genres which are already exists in Genres table, like here:

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

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

        audioTrackService.addAudioTrack(track);

(the audioTrackService.addAudioTrack(track) thing does sessionFactory.getCurrentSession().save(track) at lowest DAO level)

i am getting:

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

How do i tell Hibernate not to try to re-insert already existing genres to Genres table on cascade inserts?

like image 523
Alexander Tumin Avatar asked Oct 14 '25 03:10

Alexander Tumin


1 Answers

If the genre already exists, you must provide its id.

Look at this line: new Genre("ambient").

How could hibernate possibly guess what is the existing id of the Genre ambient?

Hibernate tries to insert the corresponding genre, because you haven't provided its id.

When you insert the audio track, hibernate must inserts records in the AudioTrackGenre table. Hibernate must know the ids of the genres. Otherwise hibernate assumes they are new genres.

Edit:

It seems you are adding genres on demand(like StackOverflow tags).

You can do the following in your code:

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

   audioTrack.addGenre(genre);
}

If you are afraid of a concurrent user adding the same genres: (suggestion by 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);
}
like image 108
Petar Minchev Avatar answered Oct 16 '25 20:10

Petar Minchev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!