Hibernate 创建冗余的多对多表
Hibernate creating redundant many-to-many tables
在开发我的 Spring 引导应用程序时,我不得不删除我的数据库并让 Hibernate 使用 hibernate.hbm2ddl.auto=update
再次生成它。在那之后,我想确保它按照我想要的方式执行所有操作,所以我调用了 MySQL Workbench 来对我的整个数据库进行逆向工程。当我这样做时,我注意到由于某种原因,我的模式中 table 的数量是原来的两倍。我的 table 中有很多实体关系,但它们都是一对多的,但出于某种原因,几乎所有的一对多关系,Hibernate 都生成了多对多连接table秒。这让我感到惊讶,因为以前在同一个 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
table 甚至没有被使用。你可以看到 skill
没有这个 table 仍然引用它自己。当我在此 table 中插入新数据时,skill_skill
中没有出现任何新数据。虽然这两个实体出于某种原因没有得到额外的 table:
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 现在的行为有所不同?
- 我也将所有造成问题的实体移到了另一个包中。
如果您在没有 @JoinColumn
的情况下使用 @OneToMany
,Hibernate 会创建一个连接 table
@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
关联来关联User
和Role
是不正确的,因为User
可以有多个角色,每个角色可以属于多个用户。使用 @ManyToMany
这样的关联。
这里是一个双方都是所有者的双向关联,基本上变成了两个独立的关联。在一对多关联中,所有者通常是多对端(注意 mappedBy
属性):
OneToMany(fetch = FetchType.EAGER, targetEntity = Skill.class, mappedBy = "parent")
@Cascade({CascadeType.DETACH})
private Set<Skill> children;
这样 Hibernate 将在维护关系时忽略一对一(并且不会创建连接 table,这是 @OneToMany
关联的默认配置,没有 @JoinColumn
)。
在开发我的 Spring 引导应用程序时,我不得不删除我的数据库并让 Hibernate 使用 hibernate.hbm2ddl.auto=update
再次生成它。在那之后,我想确保它按照我想要的方式执行所有操作,所以我调用了 MySQL Workbench 来对我的整个数据库进行逆向工程。当我这样做时,我注意到由于某种原因,我的模式中 table 的数量是原来的两倍。我的 table 中有很多实体关系,但它们都是一对多的,但出于某种原因,几乎所有的一对多关系,Hibernate 都生成了多对多连接table秒。这让我感到惊讶,因为以前在同一个 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
table 甚至没有被使用。你可以看到 skill
没有这个 table 仍然引用它自己。当我在此 table 中插入新数据时,skill_skill
中没有出现任何新数据。虽然这两个实体出于某种原因没有得到额外的 table:
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 现在的行为有所不同? - 我也将所有造成问题的实体移到了另一个包中。
如果您在没有 @JoinColumn
的情况下使用 @OneToMany
,Hibernate 会创建一个连接 table
@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
关联来关联User
和Role
是不正确的,因为User
可以有多个角色,每个角色可以属于多个用户。使用 @ManyToMany
这样的关联。
这里是一个双方都是所有者的双向关联,基本上变成了两个独立的关联。在一对多关联中,所有者通常是多对端(注意 mappedBy
属性):
OneToMany(fetch = FetchType.EAGER, targetEntity = Skill.class, mappedBy = "parent")
@Cascade({CascadeType.DETACH})
private Set<Skill> children;
这样 Hibernate 将在维护关系时忽略一对一(并且不会创建连接 table,这是 @OneToMany
关联的默认配置,没有 @JoinColumn
)。