mainstringargs
mainstringargs

Reputation: 14371

Splitting Hibernate Entity among 2 Tables

I have a simple Class Hierarchy that I am trying to get to work with Hibernate/JPA.

Basically what I want is for the MovementData to be in its own Table with a FK to the integer id of the Main Vehicle Table in the Database.

Am I doing something wrong? How else can I accomplish something similar? I am pretty sure I am following the JPA spec. (EJB3 In Action says this should work: EJB3 In Action: @SecondaryTable

Here is a portion of the exception I am getting:

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)

Here is some of the logging info from Hibernate that pertains to the Vehicle... It looks like it is recognizing everything fine.. Then it throws the exception for some reason.

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 is the Parent Class

/**
 * 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;
    }

}

Car extends Vehicle

/**
 * 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 is Embedded in Vehicle

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

}

Persistence Unit:

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

Upvotes: 2

Views: 6986

Answers (7)

KaySee
KaySee

Reputation: 423

The issues with this problem is that hibernate is evaluating that field before the basic types.

Try adding a x or z before the field so that it goes to the end of the line.

This worked for us.

Upvotes: 0

Dick Chesterwood
Dick Chesterwood

Reputation: 2659

I guess its way too late for this, but I stumbled on the question when looking for something else.

The problem is that an Embeddable class does not map to a table - an Embeddable object has no identity in the database.

This is why there is no MovementData table being generated.

I think you've confused the meaning of Embedded. As mentioned by a previous commenter, Vehicle and MovementData should be in a OneToOne relationship, and the Embedded annotations should be removed. You might need a Cascade Remove rule to make sure that if a vehicle is removed, so is the MovementData.

Upvotes: 1

Cuga
Cuga

Reputation: 17904

Connect to your database using your appropriate db client (Query Browser, MS SQL Managment Studio, etc) to verify whether or not the tables do, in fact, exist.

You may also want to insert a breakpoint in your code so that you can see exactly what the database looks like at the moment before it's throwing this error.

Try setting the hibernate.hbm2ddl.auto value to "create" as below, and also change the setting so that it prints out the SQL so you can see what it's attempting:

<props>
  <!-- some possible values: create, create-drop, update -->
  <prop key="hibernate.hbm2ddl.auto">update</prop>
  <prop key="hibernate.show_sql">true</prop>
  ....
<props>

Let me know what you see in your db.

Upvotes: -1

ccclark
ccclark

Reputation: 209

I would try breaking this down to find the issue. Try making movement data into an entity instead of an embeddable class. Make sure that it can stand on its own and then change it back to embeddable.

Although its not really the relationship that you want, you might try making MovementData the root parent and have Vehicle inherit off of MovementData. That way you would only be working with

@Inheritance(strategy=InheritanceType.JOINED)

instead of inheritance plus secondaryTable. It would simplify the relationship but still include all of the tables.

Upvotes: 1

topchef
topchef

Reputation: 19783

Definition of embeddable class should not reference database table:

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

    ...
}

Then Vehicle entity should define secondary table for embedded structure including join column:

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

    ...
}

I hope this will work for you.

Upvotes: 0

Schildmeijer
Schildmeijer

Reputation: 20946

During development its often convenient to have this property enabled:

cfg.setProperty(Environment.HBM2DDL_AUTO, "update");

where cfg is my AnnotationConfiguration.

Your problem will most likely be solved by enabling this property.

Upvotes: 1

Rich Kroll
Rich Kroll

Reputation: 4005

From the exception you posted, it appears as if the table has not yet been created in your database ("Table MOVEMENT_DATA not found"). If you have not instructed Hibernate/JPA to alter your schema you have to manually add the table prior to running your code (I believe it is CREATE-UPDATE to instruct hibernate to perform the alter's).

Upvotes: 0

Related Questions