Hi again,
I forgot to attach the patched files. Sorry
about that, attaching them now.
Regards,
Rafał
From:
eclipselink-users-bounces@xxxxxxxxxxx
[mailto:eclipselink-users-bounces@xxxxxxxxxxx] On Behalf Of Swierzynski, Rafal
Sent: Thursday, January 14, 2010
1:34 PM
To: eclipselink-users@xxxxxxxxxxx
Subject: [eclipselink-users]
Native SQL query and custom column mapping problem
Hi EclipseLink users and developers,
we are using EclipseLink 2.0.0 and encountered a problem
with native SQL support and custom table name mappings. The problem is that is
doesn't always work. Our @Column mappings contain the 'name' attribute, like this
(whole source code available as attachment):
@Id
@Column(name = "Id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "Name")
private String name;
Please notice that the column names are camel case. Our
tests show that it works with SQL Server only, whereas it fails for HSQLDB, H2
and PostgreSQL (tested only with these, not sure about others). It throws an
exception saying that the id property cannot be null:
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row
[DatabaseRecord(
=> 1
=> Jason Bourne)] during the execution of the query was detected to be
null. Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Person sql="select
pe.* from Person pe")
at
org.eclipse.persistence.exceptions.QueryException.nullPrimaryKeyInBuildingObject(QueryException.java:863)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:468)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:441)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:635)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:838)
at
org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:464)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:997)
at
org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:675)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:958)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:432)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1021)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2863)
at
org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1225)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1207)
at
org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1181)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:453)
at
org.eclipse.persistence.internal.jpa.EJBQueryImpl.getResultList(EJBQueryImpl.java:669)
at test.NativeQueryTest.test(NativeQueryTest.java:29)
... Removed 22 stack frames
with the id being 1 in this example (in the test, it the
entity is persisted first, and then re-read). It doesn't matter if the second
level cache is turned off or if I clear() the entity manager, or both (which I
think is a good thing as it is consistent ;d).
After some debugging it turns out that the driver for SQL
Server (net.sourceforge.jtds:jtds:1.2.4 in maven parlance) returns the tables
from the result metadata with mixed case, and it works fine with the method
org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.sortFields(Vector
fields, Vector columnNames). However, it doesn't work with other databases
because: PostgreSQL (driver postgresql:postgresql:8.4-701.jdbc4) returns
metadata in lowercase, whereas HQSLDB (org.hsqldb:hsqldb:1.0.8.10) and H2
(com.h2database:h2:1.1.118) return the metadata in uppercase. There is a
setting to force transforming returned metadata to uppercase for PostgreSQL (as
described here: http://wiki.eclipse.org/EclipseLink/FAQ/JPA/PostgreSQL) but
this only works if the mappings use only uppercase (as if you use the default,
for example), which is not true in our case.
When the @Id is mapped so that it works (like all
uppercase), there is no exception reported about the id being null, but the
entity may not be completely initialized - the other attribute is null if
second level cache is turned off and I clear() the entity manager (what
basically means that the data is re-read from the database). This might be a
'silent killer' in some scenarios.
After looking into the code and debugging, there is a method
org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.setShouldIgnoreCaseOnFieldComparisons(boolean)
and an appropriate getter, and it is actually used by the mentioned
sortFields() method, but I cannot find a way to configure this property in
persistence.xml, I also didn't find (via eclipse IDE's References -> Project
/ Workspace) any code that calls it. So, it looks like I must call it myself,
and when I add this call (with the parmeter set to true) to our test case, it passes.
Looks like a solution, but:
1. it ties the code of our JPA module to EclipseLink because
of this call, which we would like to avoid
2. as it is a static method with a static field as an
implementation, the setting is inherently JVM / classloader-wide, so every EL
persistence unit within the JVM / classloader would have it set (we have only
one unit at this time, but still this might be a problem for others I suppose)
To fix this, I added another property called
'eclipselink.jdbc.case-insensitive-columns' which resembles the
'eclipselink.jdbc.uppercase-columns' property in implementation, and changed 5
classes slightly to make use of it. Now, the tests work fine.
My question is: is there any other way to have support for
camel case mappings? I really did my best to find it, but couldn't. Still, I
may be missing something important.
Also, this is not just a test case, it actually is our
requirement to have mixed case attributes, support many databases, and use
native queries (we have some scenarios that prevents us from using JPQL only).
For more input, I attach a maven2 project with the sample
entity and a test for it. Also, I attach my 'patch' (the 5 modified files I
mentioned). To test it, one would have to compile the files, replace them in
the EL jar and uncomment the last property in persistence.xml. (I could attach
the modified EL jar, but as it is 5mb I think I should not. If you want me to
do it, please provide me with an appropriate link for upload). Is it possible
(if there is no other way to fix our issue) that the patch be incorporated into
EL?
Best regards,
Rafał