users@glassfish.java.net

Bad SQL Generation when merging an object using inheritance

From: <glassfish_at_javadesktop.org>
Date: Tue, 11 Sep 2007 08:56:06 PDT

I have a simple example of an inheritance relationship:

Table schema in Db2.

create table test.company (
id decimal (18,0) not null,
comp_typ_c smallint, not null,
name char (10)
)

Abstract Superclass:

@Entity
@Table(name="company", schema= "test")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="comp_typ_c", discriminatorType=DiscriminatorType.INTEGER)
public abstract class AbstractCompany {
        
        @Id
        @Column(name="id")
        private BigDecimal id;
        
        @Column(name="name")
        private String name;

        public AbstractCompany(){
                
        }
        
        public AbstractCompany(String name){
                this.name = name;
                this.id = new BigDecimal(new Random().nextInt());
        }
        
        public BigDecimal getId() {
                return this.id;
        }

}

Concrete Subclass:

@Entity
@DiscriminatorValue("0")
public class Company extends AbstractCompany{
        
        public Company(){
                
        }
        
        public Company(String name){
                super(name);
        }
        
}

And a very simple driver program that saves a record, disconnects from the EntityManager, reconnects the EM, then attempts to merge the reference and delete it. This runs in a J2SE environment so I have to declare my transactions.

public final class TestDriver {

        public static void main(String[] args) {
                        TestDriver gen = new TestDriver();
                        AbstractCompany company = gen.save();
                        gen.findSavedAndRemove(company);
        }
        public void findSavedAndRemove(AbstractCompany company) {
                HashMap<String, String> map = getPersistenceMap();
                EntityManagerFactory emf = Persistence.createEntityManagerFactory("TEST", map);
                EntityManager manager = emf.createEntityManager(map);
                
                EntityTransaction tran = manager.getTransaction();
                try {
                        tran.begin();
                        company = manager.merge(company);
                        manager.remove(company);
                        tran.commit();
                } catch (RuntimeException r){
                        tran.rollback(); throw r;
                }finally {
                        if (emf != null) {
                                emf.close();
                        }
                }
        }

        public AbstractCompany save() {
                HashMap<String, String> map = getPersistenceMap();
                EntityManagerFactory emf = Persistence.createEntityManagerFactory("TEST", map);
                EntityManager manager = emf.createEntityManager();
                
                EntityTransaction tran = manager.getTransaction();
                try {
                        tran.begin();
                        Company company = new Company("TheShack");
                        manager.persist(company);
                        tran.commit();
                        return company;
                } catch (RuntimeException r){
                        tran.rollback();
                        throw r;
                }finally {
                        if (emf != null) {
                                emf.close();
                        }
                }
        }
        
        private HashMap<String, String> getPersistenceMap() {
                HashMap<String, String> map = new HashMap<String, String>();
                map.put("toplink.jdbc.url",
                                "jdbc:db2://databaseserver:port/test");
                map.put("toplink.jdbc.driver", "com.ibm.db2.jcc.DB2Driver");
                map.put("toplink.jdbc.user", "user");
                map.put("toplink.jdbc.password", "pw");
                map.put("toplink.logging.level", "FINE");
                
                return map;
        }

        
}

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
        version="1.0">
        <persistence-unit name="TEST">
                <provider>
                        oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
                </provider>
                <class>org.test.Company</class>
                <class>org.test.AbstractCompany</class>
        </persistence-unit>
</persistence>


When I execute this, I get the following exception. The SQL generation adds the discriminator to the query on the table, which in itself isn't so bad, but puts quotes around it which causes the exception (401 is a datatype mismatch). The discriminator is clearly marked as Integer and the insert worked fine so the entity appears to be set up correctly. This issue exists no matter what kind of inheritance you are using ( this example uses SINGLE TABLE because it is easiest to show here, but JOINED has the same behavior). I also find it curious that it always uses a literal for the discriminator even though all other variables are bound to variables.

Anyone else seen this or have an answer? I'm thinking it might be some kind of bug. BTW: This was created using the jar I got from glassfish-persistence-installer-v2-b58g.jar (the trace says toplink version b58c).


[TopLink Config]: 2007.09.11 10:05:07.770--ServerSession(13756574)--Thread(Thread[main,5,main])--The alias name for the entity class [class org.test.Company] is being defaulted to: Company.
[TopLink Config]: 2007.09.11 10:05:07.801--ServerSession(13756574)--Thread(Thread[main,5,main])--The alias name for the entity class [class org.test.AbstractCompany] is being defaulted to: AbstractCompany.
[TopLink Config]: 2007.09.11 10:05:07.864--ServerSession(15142448)--Thread(Thread[main,5,main])--The alias name for the entity class [class org.test.Company] is being defaulted to: Company.
[TopLink Config]: 2007.09.11 10:05:07.864--ServerSession(15142448)--Thread(Thread[main,5,main])--The alias name for the entity class [class org.test.AbstractCompany] is being defaulted to: AbstractCompany.
[TopLink Info]: 2007.09.11 10:05:08.473--ServerSession(13756574)--Thread(Thread[main,5,main])--TopLink, version: Oracle TopLink Essentials - 2.0 (Build b58c-fcs (08/22/2007))
[TopLink Fine]: 2007.09.11 10:05:08.723--Thread(Thread[main,5,main])--Detected Vendor platform: oracle.toplink.essentials.platform.database.DB2Platform
...
[TopLink Info]: 2007.09.11 10:05:09.176--ServerSession(19940306)--Thread(Thread[main,5,main])--file:/C:/projects/Test/bin/-TEST login successful
[TopLink Fine]: 2007.09.11 10:05:09.208--ServerSession(19940306)--Connection(14965598)--Thread(Thread[main,5,main])--SELECT id, comp_typ_c, name FROM test.company WHERE ((id = ?) [b]AND (comp_typ_c = '0'))[/b] bind => [-54069932]
[TopLink Warning]: 2007.09.11 10:05:09.208--UnitOfWork(27318374)--Thread(Thread[main,5,main])--Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2.0 (Build b58c-fcs (08/22/2007))): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.c.SqlException: DB2 SQL error: SQLCODE: -401, SQLSTATE: 42818, SQLERRMC: =
Error Code: -401
Call: SELECT id, comp_typ_c, name FROM test.company WHERE ((id = ?) AND (comp_typ_c = '0'))
        bind => [-54069932]
Query: ReadObjectQuery(org.test.Company)
Local Exception Stack:
Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2.0 (Build b58c-fcs (08/22/2007))): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.c.SqlException: DB2 SQL error: SQLCODE: -401, SQLSTATE: 42818, SQLERRMC: =
Error Code: -401
Call: SELECT id, comp_typ_c, name FROM test.company WHERE ((id = ?) AND (comp_typ_c = '0'))
        bind => [-54069932]
Query: ReadObjectQuery(org.test.Company)
        at oracle.toplink.essentials.exceptions.DatabaseException.sqlException(DatabaseException.java:319)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:566)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:452)
        at oracle.toplink.essentials.threetier.ServerSession.executeCall(ServerSession.java:473)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:214)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.selectOneRow(DatasourceCallQueryMechanism.java:635)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectOneRowFromTable(ExpressionQueryMechanism.java:2453)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectOneRow(ExpressionQueryMechanism.java:2428)
        at oracle.toplink.essentials.queryframework.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:365)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:724)
        at oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:628)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:692)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:746)
        at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2233)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:952)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:909)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.readObject(AbstractSession.java:2191)
        at oracle.toplink.essentials.internal.sessions.MergeManager.registerObjectForMergeCloneIntoWorkingCopy(MergeManager.java:695)
        at oracle.toplink.essentials.internal.sessions.MergeManager.mergeChangesOfCloneIntoWorkingCopy(MergeManager.java:411)
        at oracle.toplink.essentials.internal.sessions.MergeManager.mergeChanges(MergeManager.java:264)
        at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.mergeCloneWithReferences(UnitOfWorkImpl.java:2723)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork.mergeCloneWithReferences(RepeatableWriteUnitOfWork.java:219)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.mergeInternal(EntityManagerImpl.java:235)
        at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.merge(EntityManagerImpl.java:128)
        at org.test.TestDriver.findSavedAndRemove(TestDriver.java:34)
        at org.test.TestDriver.main(TestDriver.java:20)
Caused by: com.ibm.db2.jcc.c.SqlException: DB2 SQL error: SQLCODE: -401, SQLSTATE: 42818, SQLERRMC: =
        at com.ibm.db2.jcc.c.cw.e(cw.java:1490)
        at com.ibm.db2.jcc.c.cw.a(cw.java:1100)
        at com.ibm.db2.jcc.b.bd.h(bd.java:138)
        at com.ibm.db2.jcc.b.bd.a(bd.java:42)
        at com.ibm.db2.jcc.b.r.a(r.java:31)
        at com.ibm.db2.jcc.b.bs.g(bs.java:149)
        at com.ibm.db2.jcc.c.cw.l(cw.java:1080)
        at com.ibm.db2.jcc.c.cx.bc(cx.java:1554)
        at com.ibm.db2.jcc.c.cx.bh(cx.java:1670)
        at com.ibm.db2.jcc.c.cx.setObject(cx.java:954)
        at oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform.setPrimitiveParameterValue(DatabasePlatform.java:1432)
        at oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:1422)
        at oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:1483)
        at oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:1471)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseCall.prepareStatement(DatabaseCall.java:623)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:485)
        ... 25 more
[Message sent by forum member 'murgaster' (murgaster)]

http://forums.java.net/jive/thread.jspa?messageID=234882