休眠和继承(TABLE_PER_CLASS)

时间:2010-08-24 15:05:17

标签: java hibernate inheritance orm annotations

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

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

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 个答案:

答案 0 :(得分:7)

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

  

2.2.4.1. Table per class

     

这种策略有许多缺点(尤其是   多态查询和   协会)在JPA中解释   spec,Hibernate参考   文档,Hibernate in Action,   和许多其他地方。 Hibernate的工作   他们中的大多数人实施这一点   使用SQL UNION查询的策略。它   通常用于顶级   继承层次结构:

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

此策略支持一对多   协会提供它们   双向即可。这个策略可以   不支持IDENTITY生成器   策略:必须共享id   跨几个表。所以,   在使用这种策略时,你应该这样做   不要使用AUTO也不要使用IDENTITY。

这样,Hibernate就能够在每个具体的widget表中创建适当的外键列。这是一个实际有效的映射。对于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 - 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

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

相关问题