Hibernate创建冗余的多对多表

时间:2016-03-14 13:43:34

标签: mysql spring hibernate spring-boot spring-data-jpa

在开发我的Spring Boot应用程序时,我不得不放弃我的数据库并让Hibernate再次使用hibernate.hbm2ddl.auto=update生成它。之后我想确保它完成了我想做的所有事情,所以我打电话给MySQL Workbench来反向工程我的整个数据库。当我这样做时,我注意到由于某种原因,我的架构中有两倍的表。我的表中有很多实体关系,但它们都是一对多的,但由于某种原因,几乎所有的一对多关系Hibernate都生成了多对多的连接表。这让我感到意外,因为在使用相同的Web应用程序之前并没有这样做。

我对SO的搜索只引发了this这个似乎无关紧要的问题。

现在我将提供样本,但我有很多代码,我想保持简短,所以我将只剪切一个例子。如果您需要了解更多信息,请在答案中注明。

所以,例如,我有这个实体:

@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将其解释为:

skill_skill image

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

role and user image

这两个对象在这里有单边关系:

@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. 我还将所有实体都移到另一个包中。

2 个答案:

答案 0 :(得分:4)

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

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

这样,Hibernate在维护关系时会忽略一对(并且不会创建连接表,这是没有@OneToMany的{​​{1}}关联的默认配置。

答案 1 :(得分:2)

如果你使用@OneToMany而没有@JoinColumn Hibernate创建一个连接表

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

将其更改为

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

请不要使用不必要的映射属性 - targetEntity = Skill.class

我不确定您是否需要使用此关联

private Skill parent; 

使用User关联关联Role@OneToMany是不正确的,因为User可以有多个角色,每个角色可以属于多个用户。使用@ManyToMany进行此类关联。

An example of an association between User and Role