zzheads
zzheads

Reputation: 1482

ManyToMany relation Hibernate

Modelling my SpringBoot application, using Hibernate and PostgreSQL as database. Need help to properly establish relation ManyToMany. Class News:

@Entity(name = "news")
public class News implements Serializable {
    private Long id;
    private Date date;
    private String text;
    private String author;
    private Set<HashTag> hashTags = new HashSet<HashTag>(0);
    private byte[] image;

    public News() {
    }

    public News(Date date, String text, String author, Set<HashTag> hashTags, byte[] image) {
        this.date = date;
        this.text = text;
        this.author = author;
        this.hashTags = hashTags;
        this.image = image;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "news_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Temporal(TemporalType.DATE)
    @DateTimeFormat(pattern = "DD/MM/YYYY")
    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Lob
    @Type(type = "org.hibernate.type.TextType")
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "news_hashTag",
            joinColumns = @JoinColumn(name = "news_id"),
            inverseJoinColumns = @JoinColumn(name = "hashtag_id"))
    public Set<HashTag> getHashTags() {
        return hashTags;
    }

    public void setHashTags(Set<HashTag> hashTags) {
        this.hashTags = hashTags;
    }

    @Lob
    public byte[] getImage() {
        return image;
    }

    public void setImage(byte[] image) {
        this.image = image;
    }

And class HashTag:

@Entity(name = "hashTag")
public class HashTag implements Serializable {
    private Long id;

    private String name;
    private String description;
    private Set<News> news = new HashSet<News>(0);

    public HashTag() {
    }

    public HashTag(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    @Column(name = "hashtag_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "hashTags")
    public Set<News> getNews() {
        return news;
    }

    public void setNews(Set<News> news) {
        this.news = news;
    }

When I try save news like this:

Set<HashTag> hashTags = new HashSet<>();
hashTags.add(new HashTag("HashTag 1");
hashTags.add(new HashTag("HashTag 2");

News news = new News();
news.text = "Some text";
news.author = "Some author";
news.date = new Date();
news.hashTags = hashTags;

newsService.save(news);

I'm getting error:

ERROR: null value in column "news_id" violates not-null constraint

Upvotes: 0

Views: 554

Answers (1)

Grim
Grim

Reputation: 2040

Lets see what we have:

On the owning side of the n-m relation:

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "news_hashTag",
        joinColumns = @JoinColumn(name = "news_id"),
        inverseJoinColumns = @JoinColumn(name = "hashtag_id"))
public Set<HashTag> getHashTags() {
    return hashTags;
}

You do not provide the exclusion of null values because documentation specifies:

boolean nullable() default true;

This is wrong for the relationtable as well as the id. You must:

  1. Set the ID to nullable=false.
  2. Set all JoinColumns of the JoinTable to nullable=false.

There is another problem: You make a insert and an update by newsService.save(news);, you specify a Generator for the ID but have no Generator, this is ok in other databases who provide something like a nextval in Oracle. In PostGreSQL you really should use a sequence-field like this:

@Id
@Column(name = "hashtag_id", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "HashTagIDGenerator")
@SequenceGenerator(name = "HashTagIDGenerator", sequenceName = "SEQ_HASHTAG_ID")

Have a good time.

Upvotes: 1

Related Questions