使用MySQL的DDL模式生成时,没有生成ON DELETE CASCADE选项。

7
在一个运行在Tomcat上的Maven-Spring-Hibernate-MySQL Web应用程序中,我使用Hibernate DDL和MySQL5InnoDBDialect生成我的DB模式。模式已经生成,除了外键的级联选项。例如,我有这个结构:一个保存用户详细信息对象的用户对象,它们共享相同的键:
@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;

...

}

当生成模式时,从用户详情表到用户表的外键缺少级联。

以下是用户详情的模式创建:

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$$

正如您所看到的,在“FOREIGN KEY”部分中没有写入任何“ON DELETE CASCADE”。

这个问题也在这里这里有描述。

因此,我尝试在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”,并没有发生变化:

enter image description here

有没有一种方法可以解决这个问题(当然不需要手动操作)?

更新

按照Angel Villalain的建议,我在UserDetails类中的"user"成员上方放置了@OnDelete注释,并且这对于OneToOne关系起到了作用,删除级联,但是OnUpdate仍然设置为Restrict,这让我想到了第一个问题 - 这是什么意思呢?我的意思是,“OnDelete”非常简单明了-当我删除父对象时,也删除子对象,但是“OnUpdate”选项的含义是什么?如果将其设置为restrict/cascade,它会如何影响我的应用程序?

我的第二个问题涉及OneToMany关系的级联。我的User类包含许多UserProviders。

@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);

角色只是一个枚举,不是实体,因此我无法从角色指向父实体。在这种情况下,是否有一种方法可以级联删除?


Role 是可嵌入对象吗? - Angel Villalain
角色只是一个枚举。如果我没记错,ElementCollection是一个新的JPA2功能,旨在处理这种情况 - 当您需要保存字符串列表时。由于这是一个新功能,如果级联生成选项受到限制,我不会感到惊讶。 - forhas
3个回答

4
使用 OnDelete 注解应该可以正确生成 DDL。您能否检查一下您是如何配置 SessionFactory 的,具体来说是使用了哪个值作为 hbm2ddl.auto 参数。 更新 关于您遇到的 UserProvider 类问题。首先,映射似乎是双向的,但一个方向必须是所有者方,另一个方向必须是反向方。这意味着拥有关系的一方将关系持久化到联接表中,而另一方必须用 mappedBy 参数进行映射,并且不能控制关系。所以,OneToManymappedBy 指向 UserPropertyuser 成员将成为反向方,而 UserProperty 将成为所有者方,并且应该加上 OnDelete 注解。但让我明天测试一下,以确保情况正确,因为我现在不在我的开发工作站前。

实际上,我正在使用entity-manager(因为我遵循JPA规范),并且使用具有generateDdl属性值为“true”的HibernateJpaVendorAdapter。 - forhas
您能否将 hibernate.hbm2ddl.auto 属性设置为用于实例化 jpa 适配器的 jpaProperties 的一部分? - Angel Villalain
1
应该放在UserDetails类中。此外,UserDetails应该定义与User类的关系,并将其注释为Id,这样就可以消除您正在使用的GenericGenerator的需要。 - Angel Villalain
谢谢!在UserDetails类的用户成员上使用OnDelete解决了问题,但现在我想问一些其他情况——更新问题。 - forhas
关于您上次的回复 - 我同意,并且在这种情况下,OnDelete似乎放置得很合适。关于“mappedby”选项 - 我不能使用它,因为我使用了一个连接表(我猜使用连接列也应该是这样)。所以我真的不知道我错过了什么。 - forhas
显示剩余3条评论

2
调查问题后,我提出了以下处理DB模式生成的方法(假设您使用Hibernate作为JPA提供程序):
  • 使用ddl模式生成,您可以生成您的DB模式。使用此选项,模式将在启动Web服务器时创建/更新。如果您使用此方法,请确保您的onDelete选项设置为级联,您可以使用OnDelete注释。这对于一对一关系(感谢Angel Villalain)对我有效,但由于某种原因,它对于一对多关系不起作用。为了解决这个差距,我使用了Spring的ResourceDatabasePopulator

enter image description here

db-additions.sql文件包含适应我的数据库的查询,例如创建Ondelete级联。
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 生成模式。有几种方法可以做到这一点,例如 thisthat

希望这能帮助某些人..


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接