在 2 个表之间拆分 Hibernate 实体
Posted
技术标签:
【中文标题】在 2 个表之间拆分 Hibernate 实体【英文标题】:Splitting Hibernate Entity among 2 Tables 【发布时间】:2009-04-28 17:44:36 【问题描述】:我有一个简单的类层次结构,我正在尝试使用 Hibernate/JPA。
基本上我想要的是将 MovementData 放在它自己的表中,并带有一个 FK 到数据库中主车辆表的整数 id。
我做错了吗?我还能如何完成类似的事情? 我很确定我正在遵循 JPA 规范。 (EJB3 In Action 说这应该可以工作:EJB3 In Action: @SecondaryTable
这是我得到的异常的一部分:
SEVERE: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: Table MOVEMENT_DATA not found
at org.hibernate.persister.entity.JoinedSubclassEntityPersister.getTableId(JoinedSubclassEntityPersister.java:480)
at org.hibernate.persister.entity.JoinedSubclassEntityPersister.<init>(JoinedSubclassEntityPersister.java:259)
at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:87)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:261)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1327)
这是来自 Hibernate 的一些与 Vehicle 相关的日志信息……看起来它可以识别一切正常……然后它出于某种原因抛出异常。
INFO: Binding entity from annotated class: com.dataobject.Vehicle
FINE: Import with entity name Vehicle
INFO: Bind entity com.dataobject.Vehicle on table VEHICLE
INFO: Adding secondary table to entity com.dataobject.Vehicle -> MOVEMENT_DATA
FINE: Processing com.dataobject.Vehicle property annotation
FINE: Processing annotations of com.dataobject.Vehicle.id
FINE: Binding column id. Unique false. Nullable false.
FINE: id is an id
FINE: building SimpleValue for id
FINE: Building property id
FINEST: Cascading id with null
FINE: Bind @Id on id
FINE: Processing annotations of com.dataobject.Vehicle.color
FINE: Binding column COLOR. Unique false. Nullable true.
FINE: binding property color with lazy=false
FINE: building SimpleValue for color
FINE: Building property color
FINEST: Cascading color with null
FINE: Processing annotations of com.dataobject.Vehicle.movementData
FINE: Binding column movementData. Unique false. Nullable true.
FINE: Binding component with path: com.dataobject.Vehicle.movementData
FINE: Processing com.dataobject.MovementData property annotation
FINE: Processing annotations of com.dataobject.MovementData.latitude
FINE: Column(s) overridden for property latitude
FINE: Binding column LATITUDE. Unique false. Nullable true.
FINE: binding property latitude with lazy=false
FINE: building SimpleValue for latitude
FINE: Building property latitude
FINEST: Cascading latitude with null
FINE: Processing annotations of com.dataobject.MovementData.longitude
FINE: Column(s) overridden for property longitude
FINE: Binding column LONGITUDE. Unique false. Nullable true.
FINE: binding property longitude with lazy=false
FINE: building SimpleValue for longitude
FINE: Building property longitude
FINEST: Cascading longitude with null
FINE: Processing annotations of com.dataobject.MovementData.speed
FINE: Column(s) overridden for property speed
FINE: Binding column SPEED. Unique false. Nullable true.
FINE: binding property speed with lazy=false
FINE: building SimpleValue for speed
FINE: Building property speed
FINEST: Cascading speed with null
FINE: Processing annotations of com.dataobject.MovementData.timeOfPosition
FINE: Column(s) overridden for property timeOfPosition
FINE: Binding column TIME_OF_POSITION. Unique false. Nullable true.
FINE: binding property timeOfPosition with lazy=false
FINE: building SimpleValue for timeOfPosition
FINE: Building property timeOfPosition
FINEST: Cascading timeOfPosition with null
FINE: Building property movementData
FINEST: Cascading movementData with null
FINE: Processing annotations of com.dataobject.Vehicle.numWheels
FINE: Binding column NUM_WHEELS. Unique false. Nullable true.
FINE: binding property numWheels with lazy=false
FINE: building SimpleValue for numWheels
FINE: Building property numWheels
FINEST: Cascading numWheels with null
INFO: Binding entity from annotated class: com.dataobject.Car
FINE: Binding column id. Unique false. Nullable false.
FINE: Subclass joined column(s) created
FINE: Import with entity name Car
INFO: Bind entity com.dataobject.Car on table CAR
FINE: Processing com.dataobject.Car property annotation
FINE: Processing annotations of com.dataobject.Car.make
FINE: Binding column MAKE. Unique false. Nullable true.
FINE: binding property make with lazy=false
FINE: building SimpleValue for make
FINE: Building property make
Vehicle 是父类
/**
* Entity implementation class for Entity: Vehicle
*
*/
@Entity
@Table(name="VEHICLE")
@Inheritance(strategy=InheritanceType.JOINED)
@SecondaryTable(name="MOVEMENT_DATA",
pkJoinColumns =
@PrimaryKeyJoinColumn(name = "ID")
)
public class Vehicle implements Serializable
private int numWheels;
private String color;
private int id;
private MovementData movementData;
private static final long serialVersionUID = 1L;
public Vehicle()
super();
@Embedded
@AttributeOverrides(
@AttributeOverride(
name = "speed",
column = @Column(name = "SPEED",
table = "MOVEMENT_DATA")
),
@AttributeOverride(
name = "timeOfPosition",
column = @Column(name = "TIME_OF_POSITION",
table = "MOVEMENT_DATA")
),
@AttributeOverride(
name = "longitude",
column = @Column(name = "LONGITUDE",
table = "MOVEMENT_DATA")
),
@AttributeOverride(
name = "latitude",
column = @Column(name = "LATITUDE",
table = "MOVEMENT_DATA")
)
)
public MovementData getMovementData()
return movementData;
public void setMovementData(MovementData movementData)
this.movementData = movementData;
@Column(name="NUM_WHEELS")
public int getNumWheels()
return this.numWheels;
public void setNumWheels(int numWheels)
this.numWheels = numWheels;
@Column(name="COLOR")
public String getColor()
return this.color;
public void setColor(String color)
this.color = color;
@Id
public int getId()
return this.id;
public void setId(int id)
this.id = id;
汽车延伸车辆
/**
* Entity implementation class for Entity: Car
*/
@Entity
@Table(name="CAR")
public class Car extends Vehicle implements Serializable
private String make;
private static final long serialVersionUID = 1L;
public Car()
super();
/**
* @return
*/
@Column(name="MAKE")
public String getMake()
return this.make;
/**
* @param make
*/
public void setMake(String make)
this.make = make;
MovementData 嵌入到车辆中
@Embeddable
public class MovementData implements Serializable
private double speed;
private Date timeOfPosition;
private double latitude;
private double longitude;
private static final long serialVersionUID = 1L;
public MovementData()
super();
/**
* @return
*/
@Column(name="SPEED")
public double getSpeed()
return this.speed;
/**
* @param speed
*/
public void setSpeed(double speed)
this.speed = speed;
/**
* @return
*/
@Column(name="TIME_OF_POSITION")
public Date getTimeOfPosition()
return this.timeOfPosition;
/**
* @param timeOfPosition
*/
public void setTimeOfPosition(Date timeOfPosition)
this.timeOfPosition = timeOfPosition;
/**
* @return
*/
@Column(name="LONGITUDE")
public double getLongitude()
return this.longitude;
/**
* @param longitude
*/
public void setLongitude(double longitude)
this.longitude = longitude;
/**
* @return
*/
@Column(name="LATITUDE")
public double getLatitude()
return this.latitude;
/**
* @param latitude
*/
public void setLatitude(double latitude)
this.latitude = latitude;
持久化单元:
<properties>
<!-- The database dialect to use -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<!-- drop and create tables at deployment -->
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<!-- Hibernate Query Language (HQL) parser. -->
<!-- property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider" /-->
<!-- property name="hibernate.cache.provider_class" value="org.hibernate.cache.JbossCacheProvider" /-->
<property name ="hibernate.show_sql" value="false" />
<property name ="hibernate.format_sql" value="false" />
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
</properties>
【问题讨论】:
【参考方案1】:在开发过程中,启用此属性通常很方便:
cfg.setProperty(Environment.HBM2DDL_AUTO, "update");
cfg 是我的 AnnotationConfiguration。
启用此属性很可能会解决您的问题。
【讨论】:
我有 create-drop 集(见上面我的 persistence.xml)。我只是用更新来代替它,但它仍然无法解决同样的错误。 对于其他有此问题的人的注意事项:答案是自动选择的...这不起作用。【参考方案2】:我会尝试分解它以找到问题。尝试将运动数据制作成实体而不是可嵌入类。确保它可以独立存在,然后将其改回可嵌入的。
虽然它不是您真正想要的关系,但您可以尝试将 MovementData 设为根父级并让 Vehicle 继承 MovementData。这样你就只能使用
@Inheritance(strategy=InheritanceType.JOINED)
而不是继承加secondaryTable。它将简化关系,但仍包括所有表。
【讨论】:
啊.. 我认为你是对的.. 将 @SecondaryTable 与加入的继承策略混合可能是一个问题,因为从数据库的角度来看,它们看起来是一样的。这并不是一个真正理想的解决方案,因为我希望我的 Java 实体看起来如上。 因此您也可以使它不是辅助表,而只是 Vehicle 和 MovementData 之间的 OneToOne 关系。使用正确的级联,它的行为会相同。【参考方案3】:我想这已经太晚了,但我在寻找其他东西时偶然发现了这个问题。
问题在于 Embeddable 类没有映射到表 - Embeddable 对象在数据库中没有标识。
这就是为什么没有生成 MovementData 表的原因。
我认为您混淆了 Embedded 的含义。正如之前的评论者所提到的,Vehicle 和 MovementData 应该是 OneToOne 关系,并且应该删除 Embedded 注释。您可能需要 Cascade Remove 规则来确保如果车辆被移除,MovementData 也会被移除。
【讨论】:
【参考方案4】:从您发布的异常来看,该表似乎尚未在您的数据库中创建(“找不到表 MOVEMENT_DATA”)。如果您没有指示 Hibernate/JPA 更改您的架构,则必须在运行代码之前手动添加表(我相信它是 CREATE-UPDATE 来指示 hibernate 执行更改)。
【讨论】:
【参考方案5】:可嵌入类的定义不应引用数据库表:
@Embeddable
public class MovementData implements Serializable
private double speed;
private Date timeOfPosition;
private double latitude;
private double longitude;
private static final long serialVersionUID = 1L;
public MovementData()
super();
@Column(name="SPEED")
public double getSpeed()
return this.speed;
...
@Column(name="TIME_OF_POSITION")
public Date getTimeOfPosition()
return this.timeOfPosition;
...
@Column(name="LONGITUDE")
public double getLongitude()
return this.longitude;
...
@Column(name="LATITUDE")
public double getLatitude()
return this.latitude;
...
那么车辆实体应该为嵌入结构定义辅助表包括连接列:
@Entity
@Table(name="VEHICLE")
@Inheritance(strategy=InheritanceType.JOINED)
@SecondaryTable(name="MOVEMENT_DATA",
pkJoinColumns =
@PrimaryKeyJoinColumn(name = "ID")
)
public class Vehicle implements Serializable
private int numWheels;
private String color;
private int id;
private MovementData movementData;
private static final long serialVersionUID = 1L;
public Vehicle()
super();
@Embedded
@AttributeOverrides(
@AttributeOverride(
name = "speed",
column = @Column(name = "SPEED",
table = "MOVEMENT_DATA")
),
@AttributeOverride(
name = "timeOfPosition",
column = @Column(name = "TIME_OF_POSITION",
table = "MOVEMENT_DATA")
),
... // override the rest of the attributes
)
public MovementData getMovementData()
return movementData;
...
@Id
public int getId()
return this.id;
...
我希望这对你有用。
【讨论】:
感谢您的帮助...但仍然出现相同的错误。我已经束手无策了! 当我遇到这样的问题时,我会尝试简化游戏环境,使其尽可能透明。在这种情况下,我会从 Vehicle 实体中取出继承设置——这样你当时就可以处理一个问题(希望如此)。在解决了辅助表中的可嵌入对象问题之后,然后回滚继承,看看它们是如何协同工作的。【参考方案6】:使用适当的数据库客户端(查询浏览器、MS SQL 管理工作室等)连接到您的数据库,以验证表是否确实存在。
您可能还想在代码中插入断点,以便您可以准确地看到数据库在引发此错误之前的样子。
尝试将 hibernate.hbm2ddl.auto 的值设置为“create”,如下所示,并更改设置,以便它打印出 SQL,以便您可以看到它正在尝试什么:
<props>
<!-- some possible values: create, create-drop, update -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
....
<props>
让我知道你在数据库中看到了什么。
【讨论】:
当我将其设置为创建时,它最终不会在实际数据库中创建任何内容。它在该点之前抛出该异常。我在上面的帖子中添加了更多的日志信息。 为什么不尝试将 Id 的列名添加到 Vehicle 的 Id 字段中,如下所示:@Id @Column(name="ID") public int getId() return this.id;以上是关于在 2 个表之间拆分 Hibernate 实体的主要内容,如果未能解决你的问题,请参考以下文章