在Mysql上使用ddl模式生成时,未生成ON DELETE CASCADE选项

时间:2012-12-22 18:31:50

标签: mysql hibernate cascade cascading-deletes hbm2ddl

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

如您所见,“FOREIGN KEY”部分中没有任何“ON DELETE CASCADE”。

此问题也在herehere中进行了描述。

所以我尝试在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的建议,我将@OnDelete注释放在UserDetails类的“user”成员之上,这就是OneToOne关系的技巧,删除是级联的,但OnUpdate设置为restrict (仍然),这引出了我的第一个问题 - 那是什么意思?我的意思是“OnDelete”非常简单 - 当我删除父母时也删除了孩子,但“OnUpdate”选项的含义是什么?当它设置为限制/级联时,它如何影响我的应用程序?

我的第二个问题是指使用OneToMany关系进行级联。我的用户类包含许多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);

角色只是一个枚举,而不是一个实体,因此我无法从一个角色指向父实体。在这种情况下,有没有办法级联onDelete?

3 个答案:

答案 0 :(得分:4)

使用OnDelete注释,DDL应该是正确的。你能否检查一下如何配置SessionFactory,具体用于hbm2ddl.auto参数的值。

<强>更新

  • 关于UserProvider课程的问题。首先,映射似乎是双向的,但一方必须是所有者方,另一方必须是反方。意味着拥有该关系的那个是将该关系持久保存到连接表中的那个,另一个必须使用mappedBy参数进行映射而不控制该关系。因此,OneToMany mappedBy指向user的{​​{1}}成员将是反面,而UserProperty将成为所有者,并且应该是UserProperty注释。但是让我明天测试一下,确保,我不在我的开发站前面。

答案 1 :(得分:2)

在调查问题之后,我提出了以下方法来处理数据库模式生成(假设您使用Hibernate作为JPA提供程序):

  • 使用ddl架构生成,您可以生成数据库架构。使用此选项,将在启动Web服务器时创建/更新架构。如果使用此方法,为了确保将onDelete选项设置为cascade,可以使用OnDelete注释。这对我来说对OneToOne关系很好(感谢Angel Villalain),但由于某种原因它不适用于OneToMany关系。为了解决这个差距,我使用了Spring的ResourceDatabasePopulator

enter image description here

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;

请注意,在Hibernate ddl生成架构之后,ResourceDatabasePopulator触发的脚本会应用,这很好。我知道由于最终的结果,我无法确保它得到保证。

  • 第二种方法是在编译时使用maven生成模式。有几种方法可以做到这一点,例如this一个或that一个。

我希望这会对某人有所帮助..

答案 2 :(得分:0)

由于某种原因,将@OnDelete放在Mysql和PostgreSql的@ManyToOne端对我不起作用,但它在@OneToMany端起作用。 https://stackoverflow.com/a/44988100/4609353