Hi there,
Here is a short background of our problem.
We are using the JPA implementation based on Hibernate Core 3.2 GA, Hibernate Annotations 3.3.0 GA and Hibernate EntityManager 3.3.0 GA.
We have an abstract class (a layer supertype) which encapsulates an embeddable primary key. There are two subclasses which inherit the primary key from the layer supertype. Each of the subclasses is mapped to a particular table. The names of the columns to which the primary keys of these classes are mapped are different in each table.
We then try to retrieve an instance of an entity bean by its identity, but keep getting an exception message which seems to relate to the fact that the primary key mapping cannot be overridden in the mapping of a subclass.
Our example is based on:
Hibernate Core 3.2 GA,
Hibernate Annotations 3.3.0 GA ,
Hibernate EntityManager 3.3.0 GA,
MS SQL 2000
Here is the primary key class we are using in our example:
Code:
package testing;
import java.io.*;
public final class IntegerIdentity implements Serializable, Comparable
{
/**
* The unique identifier.
*/
private Integer id;
/**
* Initialises a new instance of the class.
*
* <p> This constructor is used exclusively by persistence provider.
*/
private IntegerIdentity()
{
super();
}
/**
* Initialises a new instance of the class with the specified parameters.
*
* @param id the unique identifier.
* @throws NullPointerException if the value of {@code id} is {@code null}.
*/
public IntegerIdentity(Integer id)
{
super();
if(id == null)
throw new NullPointerException("A non-null value is expected.");
this.id = id;
}
/**
* Returns the unique identifier.
*
* @return the unique identifier.
*/
public Integer getId()
{
return id;
}
/**
* Compares the current object with the specified object for equality.
*
* <p> This implementation is based on comparison between the {@code id} attribute of the current object
* and the {@code id} attribute of the specified object according to the general equality requirements set
* out in the {@code equals} method of the {@code Object} class.
*
* @param anObject the object to be compared with the current object.
* @return {@code true} if the objects are equal, otherwise return {@code false}.
*/
public boolean equals(Object anObject)
{
if(this == anObject)
return true;
if(anObject == null || !(anObject instanceof IntegerIdentity))
return false;
IntegerIdentity other = (IntegerIdentity)anObject;
return (id == other.id || (id != null && id.equals(other.id)));
}
/**
* Compares the current object with the specified object according to their natural ordering.
*
* <p> This implementation is based on comparison between the {@code id} attribute of the current object
* and the {@code id} attribute of the specified object according to their numerical order and in
* consistency with the {@code equals} method of the {@code Object} class.
*
* @param anObject the object to be compared with the current object.
* @return the value of {@code -1} if the {@code id} attribute of the current object is less than the
* {@code id} attribute of the specified object; the value of {@code 0} if the {@code id} attribute of the
* current object is equal to the {@code id} attribute of the specified object; the value of {@code 1} if
* the {@code id} attribute of the current object is greater than the {@code id} attribute of the specified
* object.
*/
public int compareTo(Object anObject)
{
IntegerIdentity other = (IntegerIdentity)anObject;
return id.compareTo(other.id);
}
/**
* Returns the hash code of the object.
*
* @return the hash code of the object.
*/
public int hashCode()
{
return id != null ? id.hashCode() : 0;
}
/**
* Returns the string representation of the object.
*
* <p> The string representation of the object contains the unique identifier.
*
* @return the string representation of the object.
*/
public String toString()
{
return getClass().getName() + "(id=" + id + ")";
}
}
Here is the abstract class (a layer supertype) we are using in our example:
Code:
package testing;
import java.io.*;
public abstract class LayerSuperType implements Serializable
{
protected IntegerIdentity identifer;
protected LayerSuperType()
{
super();
}
}
Here is the first subclass of a layer supertype we are using in our example:
Code:
package testing;
public final class C extends LayerSuperType
{
public C()
{
super();
}
public String toString()
{
return "identifer = " + identifer.getId();
}
}
Here is the second subclass of a layer supertype we are using in our example:
Code:
public class D extends LayerSuperType
{
public D()
{
super();
}
public String toString()
{
return "identifer = " + identifer.getId();
}
}
Here is the testing class we are using in our example:
Code:
package testing;
import com.dkib.cpds.zion.common.entity.processed.fitch.*;
import javax.persistence.*;
public final class Main
{
public static void main(String[] args)
{
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
EntityManager em = emf.createEntityManager();
try
{
EntityTransaction tr = em.getTransaction();
tr.begin();
C c = em.find(C.class, new IntegerIdentity(1));
System.out.println(c);
D d = em.find(D.class, new IntegerIdentity(1));
System.out.println(d);
tr.commit();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
em.close();
}
}
}
Here is the first table we are using in our example:
Code:
CREATE TABLE [c] (
[c_id] [int] NOT NULL ,
[col1] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
CONSTRAINT [PK_c] PRIMARY KEY CLUSTERED
(
[c_id]
) ON [PRIMARY]
) ON [PRIMARY]
GO
Here is the second table we are using in our example:
Code:
CREATE TABLE [d] (
[d_id] [int] NOT NULL ,
[col1] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
CONSTRAINT [PK_d] PRIMARY KEY CLUSTERED
(
[d_id]
) ON [PRIMARY]
) ON [PRIMARY]
GO
Here is the mapping file we are using in our example:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
version="1.0">
<mapped-superclass class="testing.LayerSuperType"
access="FIELD"
metadata-complete="true">
<attributes>
<embedded-id name="identifer"/>
</attributes>
</mapped-superclass>
<entity class="testing.C"
access="FIELD"
metadata-complete="true">
<attribute-override name="identifer">
<column name="c_id"
insertable="true"
updatable="true"/>
</attribute-override>
</entity>
<entity class="testing.D"
access="FIELD"
metadata-complete="true">
<attribute-override name="identifer">
<column name="d_id"
insertable="true"
updatable="true"/>
</attribute-override>
</entity>
<embeddable class="testing.IntegerIdentity" access="FIELD"/>
</entity-mappings>
Here is the exception stack of our example:
Code:
23-Apr-2007 14:09:56 org.hibernate.util.JDBCExceptionReporter logExceptions
WARNING: SQL Error: 207, SQLState: S0003
23-Apr-2007 14:09:56 org.hibernate.util.JDBCExceptionReporter logExceptions
SEVERE: Invalid column name 'id'.
23-Apr-2007 14:09:56 org.hibernate.event.def.DefaultLoadEventListener onLoad
INFO: Error performing load command
org.hibernate.exception.SQLGrammarException: could not load an entity: [testing.C#component[id]{id=1}]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1865)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3038)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:179)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:175)
at testing.Main.main(Main.java:53)
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'id'.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(Unknown Source)
at com.microsoft.sqlserver.jdbc.IOBuffer.processPackets(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.sendExecute(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.doExecuteQuery(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeQuery(Unknown Source)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1778)
at org.hibernate.loader.Loader.doQuery(Loader.java:662)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1851)
... 13 more
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not load an entity: [testing.C#component[id]{id=1}]
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:630)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:195)
at testing.Main.main(Main.java:53)
Caused by: org.hibernate.exception.SQLGrammarException: could not load an entity: [testing.C#component[id]{id=1}]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1865)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3038)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:179)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:175)
... 1 more
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'id'.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(Unknown Source)
at com.microsoft.sqlserver.jdbc.IOBuffer.processPackets(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.sendExecute(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.doExecuteQuery(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeQuery(Unknown Source)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1778)
at org.hibernate.loader.Loader.doQuery(Loader.java:662)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1851)
... 13 more
We understand that the <attribute-override> feature used to map primary keys of our subclasses should allow the primary keys to be mapped to different columns, but this doesnt seem to be the case as proven by the exception above. This makes us wonder whether this is an error in the implementation or whether this type of overriding is not allowed at all.
If anyone could give us a hand with this problem we would really appreciate it.
Cheers