Hibernate 创建冗余多对多表

在开发我的 Spring Boot 应用程序时,我不得不删除我的数据库并让 Hibernate 使用 hibernate.hbm2ddl.auto=update 再次生成它.在那之后,我想确保它像我想要的那样做所有事情,所以我调用 MySQL Workbench 对我的整个数据库进行逆向工程.当我这样做时,我注意到由于某种原因,我的架构中的表数量是原来的两倍.我的表中有很多实体关系,但它们都是一对多的,但出于某种原因,几乎所有的一对多关系 Hibernate 都生成了多对多连接表.这让我感到惊讶,因为以前在同一个 Web 应用程序中没有发生过这种情况.

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.

我在 SO 中的搜索只让我找到了这个似乎无关紧要的问题.

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.

例如,我有这个实体:

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

您可以看到该实体引用了自身.技能在这里是一棵树.因此,Hibernate 将其解释为:

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

skill_skill 图片

事实上,甚至没有使用skill_skill 表.你可以看到 skill 在没有这个表的情况下仍然引用自己.当我在此表中插入新数据时,skill_skill 中不会出现任何新数据.虽然这两个实体由于某种原因没有得到额外的表:

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:

角色和用户形象

这两个对象在这里是单方面的关系:

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;
}

我能想到的一些原因:

  1. 抽象 AbstractDomainObject 超类.它只有受保护的样板功能.不过之前没有造成任何问题.
  2. 我添加的
  3. @Cascade({CascadeType.DETACH}) 注释.虽然看起来不太可能.
  4. 我最后一次更改是在我的项目中创建了第二个数据源,这就是为什么我将 @EnableAutoConfiguration 更改为 @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class}).难道 Hibernate 现在的行为有什么不同吗?
  5. 我还将所有导致问题的实体移到了不同​​的包中.
  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.

推荐答案

这里是一个双向关联,双方都是所有者,基本上变成了两个独立的关联.在一对多关联中,所有者通常是多对方(注意 mappedBy 属性):

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;

这种方式Hibernate在维护关系时将忽略一对边(并且不会创建连接表,这是@OneToMany关联的默认配置,没有@JoinColumn).

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).

相关文章