Hibernate与继承(TABLE_PER_CLASS)

6

我使用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

这是我用来生成表格的类:

@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 {
     (...)
}

所以这意味着Hibernate正在寻找名为'widget'的表,但它没有被创建,这很合理,因为我选择了InheritanceType.TABLE_PER_CLASS选项,那么只有具体类才有一个表。在数据库中,我可以看到container、text_widget和image_widget表。然后当我尝试执行此代码并保存container时,就会出现上述错误:
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);

感谢您的帮助!
1个回答

7

如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.

这样,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);
    }
}

抽象的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; }
}

而下面的测试方法(在事务内运行)则只是通过了:

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

并生成以下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 - 将'98304'绑定到参数: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 - 将'98304'绑定到参数:1
21:59:58.165 [main] TRACE org.hibernate.type.IntegerType - 将'0'绑定到参数:2
21:59:58.166 [main] TRACE org.hibernate.type.LongType - 将'32768'绑定到参数: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 - 将'98304'绑定到参数:1
21:59:58.188 [main] TRACE org.hibernate.type.IntegerType - 将'0'绑定到参数:2
21:59:58.189 [main] TRACE org.hibernate.type.LongType - 将'32769'绑定到参数:3
但请记住,每个类一个表的策略对于多态关系提供了较差的支持,如果您有许多Widget子项,则可能完全不适用(这将导致巨大的SQL UNION)。

相关问题


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