Hibernate 创建冗余的多对多表

Posted

技术标签:

【中文标题】Hibernate 创建冗余的多对多表【英文标题】:Hibernate creating redundant many-to-many tables 【发布时间】:2016-06-29 14:07:31 【问题描述】:

在开发我的 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;

我能想到的一些原因:

    抽象AbstractDomainObject 超类。它仅具有受保护的样板功能。不过之前没有造成任何问题。 @Cascade(CascadeType.DETACH) 我添加的注释。虽然看起来不太可能。 我最后的更改是在我的项目中创建了第二个数据源,这就是我将@EnableAutoConfiguration 更改为@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class) 的原因。会不会是 Hibernate 现在的行为有所不同? 我还将所有引起问题的实体移到了不同​​的包中。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

【参考方案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; 

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

An example of an association between User and Role

【讨论】:

以上是关于Hibernate 创建冗余的多对多表的主要内容,如果未能解决你的问题,请参考以下文章

hibernate关联关系(多对多)

十一 .Django 多对多表ManyToManyField (ORM)

ORM表多对多的操作

Hibernate的多对多关联关系

填充具有访问权限的多对多表

Hibernate学习笔记 --- 创建基于中间关联表的多对多映射关系