java 休眠和继承 (TABLE_PER_CLASS)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3557879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 02:24:10  来源:igfitidea点击:

Hibernate and Inheritance (TABLE_PER_CLASS)

javahibernateinheritanceormannotations

提问by Romain

I use Hibernate to persist inherited objects but I got this message when I try to persist objects in database:

我使用 Hibernate 来持久化继承的对象,但是当我尝试在数据库中持久化对象时收到了这条消息:

org.springframework.dao.InvalidDataAccessResourceUsageException: Could not execute
JDBC batch update; SQL [update Widget set CONTAINER_ID=? where WIDGET_ID=?]; nested 
exception is org.hibernate.exception.SQLGrammarException: 
Could not execute JDBC batch update (...) Caused by: java.sql.BatchUpdateException: Table 
'schema.widget' doesn't exist

Here is the classes I used to generate the table:

这是我用来生成表的类:

@Entity
@Table(name="CONTAINER")
public class Container {
     (...)
     private Set<Widget> widgets;

     @OneToMany(targetEntity = Widget.class)
     @JoinColumn(name="CONTAINER_ID", nullable=true)
     public Set<Widget> getWidgets() {
         return widgets;
     }

     public void setWidgets(Set<Widget> widgets) {
         this.widgets = widgets;
     }
}


@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Widget {
     private long id;
     private int position;

     @Id
     @GeneratedValue(strategy = GenerationType.TABLE)
     @Column(name="WIDGET_ID")
     public long getId() {
         return id;
     }

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

     @Column(name="POSITION")
     public int getPosition() {
         return position;
     }

     public void setPosition(int position) {
         this.position = position;
     }
}


@Entity
@Table(name="TEXT_WIDGET")
public class TextWidget extends Widget {
     (...)
}


@Entity
@Table(name="IMAGE_WIDGET")
public class ImageWidget extends Widget {
     (...)
}

So it means that Hibernate is looking for the table 'widget' but it's not created and that make sense because I choose InheritanceType.TABLE_PER_CLASS option then only concrete classes have a table. In database, I can see container, text_widget and image_widget tables.

所以这意味着 Hibernate 正在寻找表 'widget' 但它没有被创建,这是有道理的,因为我选择了 InheritanceType.TABLE_PER_CLASS 选项,然后只有具体的类有一个表。在数据库中,我可以看到容器、text_widget 和 image_widget 表。

Then when I try to execute this code and save container, then I got the above error:

然后当我尝试执行此代码并保存容器时,出现上述错误:

Set<Widget> widgets = new HashSet<Widget>();
widgets.add(textw1); // instance of TextWidget
widgets.add(imgw1); // instance of ImageWidget

Container container1 = new Container();
container1.setWidgets(widgets);

Thanks for your help!

谢谢你的帮助!

回答by Pascal Thivent

Your association must be bidirectional, as mentioned in the Hibernate documentation.

您的关联必须是双向的,如 Hibernate 文档中所述。

2.2.4.1. Table per class

This strategy has many drawbacks (esp. with polymorphic queries and associations) explained in the JPA spec, the Hibernate reference documentation, Hibernate in Action, and many other places. Hibernate work around most of them implementing this strategy using SQL UNION queries. It is commonly used for the top level of an inheritance hierarchy:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }            

This strategy supports one-to-many associations provided that they are bidirectional. This strategy does not support the IDENTITY generator strategy: the id has to be shared across several tables. Consequently, when using this strategy, you should not use AUTO nor IDENTITY.

2.2.4.1. 每班表

这种策略有很多缺点(尤其是多态查询和关联),在 JPA 规范、Hibernate 参考文档、Hibernate in Action 和许多其他地方都有解释。Hibernate 解决了大多数使用 SQL UNION 查询实现此策略的问题。它通常用于继承层次结构的顶层:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }            

该策略支持一对多关联,前提是它们是双向的。此策略不支持 IDENTITY 生成器策略:id 必须在多个表之间共享。因此,在使用此策略时,不应使用 AUTO 或 IDENTITY。

This way, Hibernate will be able to create the appropriate foreign key column in each concrete widget table. Here is a mapping that actually works. For the Container:

这样,Hibernate 将能够在每个具体的小部件表中创建适当的外键列。这是一个实际有效的映射。对于Container

@Entity
public class Container {
    @Id @GeneratedValue
    private long id;

    @OneToMany(targetEntity = Widget.class, mappedBy = "container", cascade = CascadeType.ALL)
    private Set<Widget> widgets = new HashSet<Widget>();

    public long getId() { return id; }

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

    public Set<Widget> getWidgets() { return widgets; }

    public void setWidgets(Set<Widget> widgets) { this.widgets = widgets; }

    public void addToWidgets(Widget widget) {
        this.getWidgets().add(widget);
        widget.setContainer(this);
    }

    public void removeFromWidgets(Widget widget) {
        this.getWidgets().remove(widget);
        widget.setContainer(null);
    }
}

And the abstract Widget class:

和抽象的 Widget 类:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Widget {
    @Id @GeneratedValue(strategy = GenerationType.TABLE)
    private long id;

    private int position;

    @ManyToOne
    private Container container;

    public long getId() { return id; }

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

    public int getPosition() { return position; }

    public void setPosition(int position) { this.position = position; }

    public Container getContainer() { return container; }

    public void setContainer(Container container) { this.container = container; }
}

And the following test method (running inside a transaction) just passes:

并且以下测试方法(在事务中运行)刚刚通过:

@Test
public void testInsertContainer() {
    TextWidget textw1 = new TextWidget();
    ImageWidget imgw1 = new ImageWidget();

    Container container1 = new Container();
    container1.addToWidgets(textw1); // instance of TextWidget
    container1.addToWidgets(imgw1); // instance of ImageWidget

    session.persist(container1);
    session.flush();

    assertNotNull(textw1.getId());
    assertNotNull(imgw1.getId());
    assertNotNull(container1.getId());
}

And generates the following SQL:

并生成以下 SQL:

21:59:57.964 [main] DEBUG org.hibernate.SQL - select next_hi from hibernate_unique_key for read only with rs
21:59:57.978 [main] DEBUG org.hibernate.SQL - update hibernate_unique_key set next_hi = ? where next_hi = ?
21:59:58.063 [main] DEBUG org.hibernate.SQL - null
21:59:58.125 [main] DEBUG org.hibernate.SQL - insert into Container (id) values (?)
Hibernate: insert into Container (id) values (?)
21:59:58.140 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.145 [main] DEBUG org.hibernate.SQL - insert into ImageWidget (container_id, position, id) values (?, ?, ?)
Hibernate: insert into ImageWidget (container_id, position, id) values (?, ?, ?)
21:59:58.164 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.165 [main] TRACE org.hibernate.type.IntegerType - binding '0' to parameter: 2
21:59:58.166 [main] TRACE org.hibernate.type.LongType - binding '32768' to parameter: 3
21:59:58.172 [main] DEBUG org.hibernate.SQL - insert into TextWidget (container_id, position, id) values (?, ?, ?)
Hibernate: insert into TextWidget (container_id, position, id) values (?, ?, ?)
21:59:58.187 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.188 [main] TRACE org.hibernate.type.IntegerType - binding '0' to parameter: 2
21:59:58.189 [main] TRACE org.hibernate.type.LongType - binding '32769' to parameter: 3

But keep in mind that the table per classstrategy provides poor support for polymorphic relationshipsand might not be appropriate at all if you have many Widgetchild (this will result in huge SQL UNION).

但是请记住,每个类策略对多态关系的支持很差,如果您有很多Widget孩子,则可能根本不合适(这将导致巨大的 SQL UNION)。

Related question

相关问题