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ł