Hibernate creating redundant many-to-many tables

2019-02-11 07:12发布

While developing my Spring Boot application I had to drop my database and have Hibernate generate it again with hibernate.hbm2ddl.auto=update. After that I wanted to make sure it did everything like I wanted to so I called MySQL Workbench to reverse engineer my entire database. When I did that, I noticed that for some reason there were twice as many tables in my schema. I have a lot of entity relationships in my table, but all of them are one-to-many, yet for some reason for almost all of my one-to-many relationships Hibernate has generated many-to-many join tables. It came as a surprise to me because this didn't happen before with the same web application.

My search through SO has only led me to this question which seems irrelevant.

Now I will provide samples, but I have a lots of code and I want to keep it short, so I will cut to only one example. If you need to see more, please specify this in your answer.

So, for instance, I have this entity:

@SuppressWarnings("serial")
@Entity
@Indexed
@Table(name = "SKILL")
public class Skill extends AbstractDomainObject {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", nullable = false)
private long id;

//Some properties

@ManyToOne(fetch = FetchType.EAGER, targetEntity = Skill.class)
@JoinColumn(name = "PARENT", nullable = true)
@Cascade({CascadeType.DETACH}) 
//Cascade annotations are from Hibernate, all else except for 
//"Indexed" are from javax.persistence
private Skill parent;

@OneToMany(fetch = FetchType.EAGER, targetEntity = Skill.class)
@Cascade({CascadeType.DETACH})
private Set<Skill> children;

//Getters, setters, etc
}

You can see that this entity references itself. Skills are a tree here. So, Hibernate interprets this as:

skill_skill image

In fact, skill_skill table isn't even being used. You can see that skill still references itself without this table. When I insert new data in this table, nothing new appears in skill_skill. Although these two entities for some reason don't get extra table:

role and user image

These two object have one-side relationship here:

@SuppressWarnings("serial")
@Entity
@Table(name = "ROLE")
public class Role implements DomainObject {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", nullable = false)
private long id;

@Column(name = "ROLENAME", nullable = false, length = 50)
private String rolename;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "OWNER", nullable = false)
private User owner;
}

Some causes I can think on:

  1. Abstract AbstractDomainObject superclass. It only has protected boilerplate functions. It didn't cause any problem before though.
  2. @Cascade({CascadeType.DETACH}) annotation that I added. Although seems unlikely.
  3. My last change was that I created a second data source in my project, this is why I changed my @EnableAutoConfiguration to @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class}). Could it be that Hibernate behaves somehow differently now?
  4. I also moved all my entities causing trouble to a different package.

2条回答
smile是对你的礼貌
2楼-- · 2019-02-11 07:49

If you use @OneToMany without @JoinColumn Hibernate creates a join table

@OneToMany(fetch = FetchType.EAGER, targetEntity = Skill.class)
@Cascade({CascadeType.DETACH})
private Set<Skill> children;

change it to

@OneToMany(fetch = FetchType.EAGER)
@Cascade({CascadeType.DETACH})
@JoinColumn
private Set<Skill> children;

Please, don't use unnecessary mapping properties — targetEntity = Skill.class

And I am not sure you need to use this association

private Skill parent; 

It is incorrect to associate User and Role using the @OneToMany association, because of User can have multiple roles and each role can belong to multiple users. Use @ManyToMany for such an association.

An example of an association between User and Role

查看更多
Animai°情兽
3楼-- · 2019-02-11 08:09

What you have here is a bidirectional association in which both sides are owners, turning it basically to two independent associations. In a one-to-many association the owner is usually the many-to side (note the mappedBy attribute):

OneToMany(fetch = FetchType.EAGER, targetEntity = Skill.class, mappedBy = "parent")
@Cascade({CascadeType.DETACH})
private Set<Skill> children;

This way Hibernate will ignore one-to side when maintaining the relationship (and will not create the join table which is the default configuration for @OneToMany association without @JoinColumn).

查看更多
登录 后发表回答