我使用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);
感谢您的帮助!
答案 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)。