在 Mysql 上使用 ddl 模式生成时未生成 ON DELETE CASCADE 选项
Posted
技术标签:
【中文标题】在 Mysql 上使用 ddl 模式生成时未生成 ON DELETE CASCADE 选项【英文标题】:ON DELETE CASCADE option not in generated when using ddl schema generation on Mysql 【发布时间】:2012-12-09 22:29:22 【问题描述】:在 Tomcat Web 应用程序上运行的 Maven-Spring-Hibernate-mysql 中,我使用 hibernate ddl 通过 MySQL5InnoDBDialect 生成我的数据库架构。
除了外键的级联选项外,模式生成得很好。例如我有这样的结构:
一个包含用户详细信息对象的用户对象,两者共享相同的密钥:
@Entity
@Table(name = "Users")
public class User implements Serializable
private static final long serialVersionUID = -359364426541408141L;
/*--- Members ---*/
/**
* The unique generated ID of the entity.
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "User_Id")
protected long id;
@Getter
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user", optional = true)
protected UserDetails userDetails;
...
以及用户详细信息:
@Entity
@Table(name = "UserDetails")
public class UserDetails implements Serializable
private static final long serialVersionUID = 957231221603878419L;
/*--- Members ---*/
/**
* Shared Key
*/
@Id
@GeneratedValue(generator = "User-Primary-Key")
@GenericGenerator(name = "User-Primary-Key", strategy = "foreign", parameters = @Parameter(name = "property", value = "user") )
@Column(name = "User_Id")
protected long id;
@Getter
@Setter
@OneToOne(optional = false, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private User user;
...
在生成模式时,从 users-details 表到 users 表的外键缺少级联。
这是用户详细信息的架构创建:
CREATE TABLE `userdetails` (
`User_Id` bigint(20) NOT NULL,
`Creation_Time` bigint(20) NOT NULL,
`EMail` varchar(128) DEFAULT NULL,
`Enabled` bit(1) NOT NULL,
`First_Name` varchar(15) DEFAULT NULL,
`Last_Name` varchar(25) DEFAULT NULL,
`Password` varchar(64) NOT NULL,
`User_Name` varchar(15) NOT NULL,
PRIMARY KEY (`User_Id`),
UNIQUE KEY `User_Name` (`User_Name`),
UNIQUE KEY `EMail` (`EMail`),
KEY `FKAE447BD7BF9006F5` (`User_Id`),
CONSTRAINT `FKAE447BD7BF9006F5` FOREIGN KEY (`User_Id`) REFERENCES `users` (`User_Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$
如您所见,“外键”部分中没有任何“ON DELETE CASCADE”。
here 和 here 也描述了此问题。
所以我尝试在 userDetails 成员上方添加 @OnDelete 注释,但没有成功。
然后我创建了自己的方言,覆盖了supportsCascadeDelete:
public class MySql5Dialect extends MySQL5InnoDBDialect
public MySql5Dialect()
super();
@Override
public String getTableTypeString()
return " ENGINE=InnoDB DEFAULT CHARSET=utf8";
@Override
public boolean supportsCascadeDelete()
return true;
但仍然没有变化。生成架构后,我的外键级联选项仍设置为“RESTRICT”:
有没有办法解决这个问题(当然非手动)?
更新
按照Angel Villalain 的建议,我将@OnDelete 注释放在UserDetails 类的“用户”成员之上,这对OneToOne 关系起到了作用,删除是级联的,但OnUpdate 设置为限制(仍然),这引出了我的第一个问题——那是什么意思?我的意思是“OnDelete”非常简单 - 当我删除父级时也会删除子级,但是“OnUpdate”选项的含义是什么?设置为限制/级联时,它对我的应用有何影响?
我的第二个问题是指级联与 OneToMany 关系。我的 User 类拥有许多 UserProviders。 以下代码来自 User 类:
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "Users_Providers", joinColumns = @JoinColumn(name = "User_Id"), inverseJoinColumns = @JoinColumn(name = "Provider_Id"))
protected Set<UserProvider> userProviders = new HashSet<>(0);
这是反比关系,来自 UserProvider 类:
@ManyToOne(fetch = FetchType.LAZY)
@JoinTable(name = "Users_Providers", joinColumns = @JoinColumn(name = "Provider_Id", insertable = false, updatable = false), inverseJoinColumns = @JoinColumn(name = "User_Id"))
@OnDelete(action = OnDeleteAction.CASCADE)
protected User user;
所以在使用 @OnDelete 注释后,我希望在连接表中看到带有级联的 onDelete 选项,但它不是:(我使用正确了吗?
最后一个问题——像@ElementCollection 这样的单向关系呢? 我的 UserDetails 类拥有一个 ElementCollection 角色(可以为每个用户分配一个或多个角色):
@ElementCollection(fetch = FetchType.EAGER, targetClass = Role.class)
@CollectionTable(name = "Users_Roles", joinColumns = @JoinColumn(name = "User_Id", referencedColumnName = "User_Id"))
@Column(name = "Role")
protected Set<Role> roles = new HashSet<Enums.Role>(0);
角色只是一个枚举,而不是实体,因此我不能从角色指向父实体。在这种情况下,有没有办法级联onDelete?
【问题讨论】:
Role
是可嵌入对象吗?
角色只是一个枚举。 ElementCollection 是一个新的 JPA2 功能,如果我记得它是针对这种情况的 - 你需要保存一个字符串列表。由于这是一项新功能,如果它受到级联生成选项的限制,我不会感到惊讶。
【参考方案1】:
使用OnDelete
注释,DDL 应该是正确的。您能否检查一下您是如何配置 SessionFactory
的,具体而言,您将哪个值用于 hbm2ddl.auto 参数。
更新
关于UserProvider
类的问题。首先映射似乎是双向的,但一侧必须是所有者侧,另一侧必须是反向侧。这意味着拥有关系的那个是将关系持久化到连接表中的那个,另一个必须用mappedBy
参数映射并且不控制关系。所以OneToMany
和mappedBy
指向UserProperty
的user
成员的OneToMany
是反面,UserProperty
是所有者,应该有OnDelete
注解。但是让我明天测试一下,以确保我不在我的开发站前。
【讨论】:
实际上我正在使用实体管理器(因为我遵循 JPA 规范)和 HibernateJpaVendorAdapter,它的 generateDdl 属性值为“true”。 您能否将hibernate.hbm2ddl.auto
属性设置为用于实例化jpa 适配器的jpaProperties
的一部分?
UserDetails
的 id
成员上的 OnDelete
注释在哪里设置?
当我尝试使用 OnDelete 注释时,我将它放在 User 类的 userDetails 成员之上。我确信它会完成这项工作,但是.. 不 :(
它应该继续 UserDetails
类。 UserDetails
还应该定义与 User
类的关系,并将其注释为 Id
,这样就不需要您正在使用的 GenericGenerator
。【参考方案2】:
在调查了这个问题后,我想出了以下处理 DB 模式生成的方法(假设您使用 Hibernate 作为您的 JPA 提供程序):
使用 ddl 模式生成,您可以生成数据库模式。使用此选项,架构将在您启动 Web 服务器时创建/更新。如果您使用此方法,为了确保您的 onDelete 选项设置为级联,您可以使用 OnDelete 注释。这对我来说适用于 OneToOne 关系(感谢Angel Villalain),但由于某种原因,它不适用于 OneToMany 关系。为了解决这个问题,我使用了 Spring 的 ResourceDatabasePopulator:db-additions.sql 文件包含适应我的数据库的查询,在我的例子中创建 Ondelete Cascade。例如:
ALTER TABLE `buysmartdb`.`users_providers` DROP FOREIGN KEY `FKB4152EEBBF9006F5` ;
ALTER TABLE `buysmartdb`.`users_providers`
ADD CONSTRAINT `FKB4152EEBBF9006F5`
FOREIGN KEY (`User_Id` )
REFERENCES `buysmartdb`.`users` (`User_Id` )
ON DELETE CASCADE
ON UPDATE CASCADE;
请注意,ResourceDatabasePopulator 触发的脚本在 Hibernate ddl 生成架构后应用,这很好。我知道,由于最终结果,我无法真正确定它是否得到保证。
第二种方法是在编译时使用 maven 生成模式。有几种方法可以做到这一点,例如this 一或that 一。我希望这会对某人有所帮助..
【讨论】:
【参考方案3】:出于某种原因,将 @OnDelete 放在 Mysql 和 PostgreSql 中的 @ManyToOne 端对我不起作用,但它在 @OneToMany 端起作用。 https://***.com/a/44988100/4609353
【讨论】:
以上是关于在 Mysql 上使用 ddl 模式生成时未生成 ON DELETE CASCADE 选项的主要内容,如果未能解决你的问题,请参考以下文章
是否有为 SQL Server 生成完整数据库 DDL 的工具? Postgres 和 MySQL 呢?