JPA Hibernate - 数据库和注释中的级联删除

时间:2014-03-09 19:00:31

标签: java mysql database hibernate jpa

我想知道我应该做些什么,因为我读过很多文章试图理解这一点,包括许多SO问题。我读过的任何东西都没有用这个钉在头上。

我想知道使用级联规则和应用程序定义数据库时会发生什么,因为这将定义我是否应该采用以下方法。

示例表

create table foo(
  id int unsigned not null auto_increment,
  primary key(id)
);

create table bar(
  id int unsigned not null auto_increment,
  foo_id int unsigned not null,
  primary key(id),
  foreign key(foo_id) references foo(id) on delete cascade on update cascade
)

示例类

@Entity
@Table(name = "foo")
public class Foo {

  private int id;
  private List<Bar> bars;

  @Id
  @GeneratedValue
  @Column(name = "id")
  public int getId() {
    return id;
  }

  @OneToMany(mappedBy = "foo", cascade = {CascadeType.ALL})
  public List<Bar> getBars() {
    return bars;
  }

  public void setId() {
    this.id = id;
  }

  public void setBars(List<Bar> bars) {
    this.bars = bars;
  }

}

@Entity
@Table(name = "bar")
public class Bar {

  private int id;
  private Foo foo;

  @Id
  @GeneratedValue
  @Column(name = "id")
  public int getId() {
    return id;
  }

  @ManyToOne
  @JoinColumn(name = "foo_id", nullable = false)
  public getFoo() {
    return foo;
  }

  public void setId(int id) {
    this.id = id;
  }

  public void setFoo(Foo foo) {
    this.foo = foo;
  }

}

问题

如果我现在在EntityManagerFactory对象上调用删除操作(通过SessionFactoryFoo),将发生以下哪种情况?

  1. hibernate操作将删除bar表中的所有记录 其外键是Foo的{​​{1}}的外键,然后删除 foo_id记录。

  2. hibernate操作将删除已加载到会话缓存中的所有相应Foo条记录(其中 可能是也可能不是实际数据库中存在的所有Bar条记录)和 然后删除bar记录(数据库级联规则将删除任何剩余的Foo记录。)

  3. hibernate操作将尝试 首先删除bar记录,如果数据库失败则执行其中一个 上述步骤。

  4. 还有其他事情我没有考虑过,如果有的话呢?

  5. 考虑到以下困境假设,最佳方法是什么?

    的dilemna

    如果1为真,则建议:

    A)仅在数据库中定义级联规则。请务必从应用程序中的对象中删除Foo,以免它们与数据库分离(因为数据库将删除其记录),然后调用删除bars

    OR

    B)仅在应用程序中定义级联规则,因为它将彻底管理数据库完整性。

    不是

    C)在两者中定义级联规则,因为每个都达到了预期的结果,使另一个成为浪费处理。

    如果2为真,则建议:

    在数据库和应用程序中定义级联规则,以便Hibernate可以负责管理其实体,并且数据库可以在应用程序无法保证删除所有foo记录之后进行清理。

    如果3为真,则建议:

    在数据库和应用程序中定义级联规则,因为Hibernate似乎支持已在数据库级别定义的级联规则。

    如果4为真,则建议:

    这个问题更为重要,因为我错过了一些基本的东西!

    编辑:添加我读过的文章......

    相关文章

    数据库,应用程序或两者的冲突视图:

    SO - should-i-let-jpa-or-the-database-cascade-deletions

    数据库或应用程序的冲突视图:

    SO - cascading-deletes-updates-using-jpa-or-inside-of-database

    本文阐明了JPA提供商实际做了些什么(尽管应该注意他们使用OpenJPA提供商进行操作证明):

    jpa-tutorial

    它声明:

      

    删除和持久操作的级联也适用于那些   尚未加载的实体。它甚至通过他们   其他实体,可能遍历整个对象图。

    接着陈述:

      

    刷新,合并和分离的级联只能通过   已加载的实体。

    这意味着提议的流程2不成立。

2 个答案:

答案 0 :(得分:0)

如果在数据库中声明级联并且hibernate,数据库将始终先删除,如果它支持它,并且hibernate调用不会真正删除任何东西,但无论如何都要运行。但是,由于您正在使用hibernate,它的主要优点是允许轻松转换到可能不支持数据库端级联功能的新数据库。因此,即使您的数据库支持级联,并且hiberate下划线jdbc语句当前没有做任何事情(他们可能在将来做某事),您也希望将它们保留在那里。

答案 1 :(得分:0)

您为什么还要考虑呢?最好坚持使用休眠级联选项。在另一边具有级联的另一端将运行级联删除两次。一次从休眠状态,一次由数据库管理。

示例189.来自休眠5.2文档。会在sql下面生成。

DELETE FROM Person_Address
WHERE  Person_id = 1

DELETE FROM Person
WHERE  id = 1
export class AddTableCategories {
  static readonly type = "[TABLECATEGORIES] Add";
  constructor(public payload: TableCategories[]) {}
}

export class TableCategoriesModel {
  tableCategories: TableCategories[];
}
@State<TableCategoriesModel>({
  name: "tableCategories",
  defaults: {
    tableCategories: []
  }
})
export class AppStates {
  @Selector()
  static getTableCategories(state: TableCategoriesModel) {
    return state.tableCategories;
  }
  @Action(AddTableCategories)
  addTCategories(
    { patchState, getState }: StateContext<TableCategoriesModel>,
    { payload }: AddTableCategories
  ) {
    patchState({
      tableCategories: payload
    });
  }
}

现在,您看到休眠在删除父实体之前先删除子实体。数据库级联将在sql person delete上运行,但是当以前删除子级时,现在没有什么可删除。