在 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 实体的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate注解实体类

Hibernate注解方式开发实体类

HIbernate实体类注解配置

Hibernate:每月自动拆分表

Hibernate多表关系配置——多对多对关系映射

Hibernate根据实体类自动创建表