Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Inherited OneToOne relationship not hydrated properly(Inherited OneToOne relationship in EclipseLink 1.0.1 not hydrated properly)
Inherited OneToOne relationship not hydrated properly [message #541625] Mon, 21 June 2010 20:10 Go to next message
Doug Gschwind is currently offline Doug GschwindFriend
Messages: 18
Registered: July 2009
Junior Member
Hello,

We are using EclipseLink version 1.0.1 and have a One to One relationship that is being written corrrectly to the database, but when instances of the JPA mapped class are later hydrated into Java objects from the database, the One to One relationship is not hydrated correctly and the reference typed instance variable unexpectedly has a value of null. This is the problem.

We have an abstract superclass Sup that has several concrete subclasses, one of which is called Sub (names changed to protect the innocent). Sup, and thus Sub, has two JPA mapped
instance variables lineString and lineStringClob of type String and ClobHolder respectively. lineString is mapped to a column of type VARCHAR2(4000), whereas lineStringClob is mapped to
a column of type RAW(16) as we use that data type to persist relationships. If the line string data is 4000 characters or less, the data is housed in the LINESTRING VARCHAR2(4000) column, otherwise the data of 4001 characters or more is housed in the CLOBHOLDER table and we store the foreign key to that row in the LINESTRINGCLOBID RAW(16) column. Thus, for each row in the source table, LINESTRING or LINESTRINGCLOBID will be null, but not both. One of the two
pieces of information is required to be present in each row of SUP or SUB.

Sup is JPA mapped to the SUP table and Sub is JPA mapped to the SUB table. Those table definitions can be summarized as follows :

SUP SUB
--- ---
SUPID PK RAW(16) NOT NULL SUBID PK RAW(16) NOT NULL
LINESTRING VARCHAR2(4000) LINESTRING VARCHAR2(4000)
LINESTRINGCLOBID RAW(16) LINSTRINGCLOBID RAW(16)
... SUPFK FK RAW(16) NOT NULL
...

The relevant JPA mapped portions of the Sup class is shown below :

@MappedSuperclass
@Customizer(SupDescriptorCustomizer.class)
public abstract class Sup extends AbstractSup {

/**
* Long display -- used if string length < 4001
*/
@Column(name="LINESTRING")
private String lineString;

/**
* Long display -- if string length > 4000
*/
@OneToOne(fetch=LAZY, cascade={ALL})
@JoinColumn(name="lineStringClobId")
@PrivateOwned
private ClobHolder lineStringClob;
...
}

The relevant JPA mapped portions of the Sub class is shown below :

@Entity
@Table(name="SUB")
@Customizer(GeneralDescriptorCustomizer.class)
@AttributeOverride(name="id", column=@Column(name="SUBID"))
public class Sub extends Sup {
...
}

When an instance of the Sup class is read, its line string data should come from the LINESTRING or LINESTRINGCLOBID columns in the SUP table. When an instance of the Sub
class is read however, its line string data should come from the LINESTRING or LINESTRINGCLOBID columns in the SUB table. What we are seeing though is that upon a Sub instance being hydrated where the value in SUB.LINESTRINGCLOBID column is non-null, the ClobHolder in the Sub instance is unexpectedly null.

We have also tried changing the attribute overrides in Sub to look like :

@AttributeOverrides({
@AttributeOverride(name="id", column=@Column(name="SUBID")),
@AttributeOverride(name="lineStringClob", column=@Column(name="LINESTRINGCLOBID"))
})

or

@AttributeOverrides({
@AttributeOverride(name="id", column=@Column(name="SUBID")),
@AttributeOverride(name="lineStringClob", column=@Column(name="SUB.LINESTRINGCLOBID"))
})

to no avail. We are seeing that instances of other non-Sub Sup subclasses are being hydrated correctly in this regard. This thus feels like an EclipseLink 1.0.1 bug, but I cannot find any similar complaints in the EclipseLink forum.

Any ideas on what to provide differently in terms of JPA annotations, or other suggestions that might yield properly hydrated instances of Sub, other than simply making another trip
to the database when both the String and ClobHolder instance variables are null in a given Sub instance?

Thank you,

Doug Gschwind
Re: Inherited OneToOne relationship not hydrated properly [message #541779 is a reply to message #541625] Tue, 22 June 2010 13:57 Go to previous messageGo to next message
James Sutherland is currently offline James SutherlandFriend
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

Please include the SQL for the insert and the select of the Sub.
Also include the code for your customizers.

If you clear or disable the cache, and create a new EntityManager (or restart your app), is the Sub's reference still null?

How are you verifying that the reference value is null? Check that your get/set methods are correctly defined.


James : Wiki : Book : Blog : Twitter
Re: Inherited OneToOne relationship not hydrated properly [message #541997 is a reply to message #541779] Wed, 23 June 2010 13:46 Go to previous messageGo to next message
Doug Gschwind is currently offline Doug GschwindFriend
Messages: 18
Registered: July 2009
Junior Member
Hi James,

Thanks for the quick response. I am in the process of getting the information and answers to your questions, and will hopefully respond with that later today.

Regards,

Doug
Re: Inherited OneToOne relationship not hydrated properly [message #542116 is a reply to message #541779] Wed, 23 June 2010 22:17 Go to previous messageGo to next message
Doug Gschwind is currently offline Doug GschwindFriend
Messages: 18
Registered: July 2009
Junior Member
Hi James,

Hopefully this gives you more of the information you need to be able to help diagnose the issue. Feel free to ask for other information if you need it.

Q: SQL statement for the insert of the Sub instance
A: INSERT IGNORE INTO CLOBHOLDER (CLOBID, CLOBVALUE, OWNERTYPE) VALUES (?, ?, ?)
bind => [[B@cdb963, String in excess of 4000 characters goes here, SVC]
INSERT IGNORE INTO SUB (SUBID, LINESTRING, CLINICALDTM, SUPID, ENTRYDTM, lineStringClobId) VALUES (?, ?, ?, ?, ?)
bind => [[B@1400d67, null, oracle.sql.DATE@2feaf6, [B@16dbf20, oracle.sql.DATE@104ee97, [B@cc15ac]

Please keep in mind that there are more like 20 columns specified in the SUB table, but I have culled
those that are not relevant to the issue.

Q: SQL statement for the select of the Sub instances
A: SELECT s.* FROM Sub s WHERE s.supId=? OR s.supId=? OR s.supId=? OR ... order by s.entryDtm desc
bind => [[B@1922b38, [B@153956b, ..., [B@e0719]

which is followed by many statements of the form:
SELECT CLOBID, CLOBVALUE, OWNERTYPE FROM CLOBHOLDER WHERE (CLOBID = ?)

I should note that not only is Sub a Sup subclass, but a Sup instance can have many
Sub instances. So the select statement you see above is attempting to hydrate many
one to many relationships with one query. The number of supIds provided is much more than the
three I depict here. More like on the order of 100 or so.

Q: Include code for customizers
A: Can you share an email address with me where I can send you these files privately?

Q: If the cache is cleared or disabled, and create a new EntityManager, is the Sub's reference still null?
A: I believe the problem only occurs when the cache is cold and we have to make a trip to the database to
hydrate a Sub instance. What have seen is that if we use getDelegate() against an EntityManager
instance to obtain a reference to an EclipseLink Session, and then call refreshObject() with our Sub
instance as an argument to refreshObject(), the problem remains. However, if we use an EclipseLink classic
ReadObjectQuery, and instruct the query to not use cache, the Sub instance returned from that call is in
fact properly populated, where it was not prior. This is not a direct answer to your question I believe,
but very close.

Q: How are you verifying that the reference value is null?
A: By stepping through the code in the debugger (Eclipse IDE 3.5.2), and seeing that after the
ClobHolder reference is requested, its returned value is unexpectedly null.

Thanks,

Doug
Re: Inherited OneToOne relationship not hydrated properly [message #542362 is a reply to message #541625] Thu, 24 June 2010 15:42 Go to previous messageGo to next message
Doug Gschwind is currently offline Doug GschwindFriend
Messages: 18
Registered: July 2009
Junior Member
I have an additional data point to share on this Thread. It turns out that if we rewrite our query (no ORM/JPA mapping metadata changes made anywhere in our domain model), the results from the newly written query have Sub instances that are properly populated. So, here is our original query that yields incorrectly populated Sub instances :

public List<Sub> findSubInstances(List<UUID> ids) {
if (ids.isEmpty()) {
return Collections.EMPTY_LIST;
}
EntityManager em = getEntityManager();
try {
StringBuilder sql = new StringBuilder("SELECT s.* FROM Sub s WHERE ");
int i = 0;
for (UUID id : ids) {
if (i++ > 0) {
sql.append("OR ");
}
sql.append("s.supId=?").append(" ");
}
sql.append("order by s.entryDtm desc");
Query q = em.createNativeQuery(sql.toString(), Sub.class);
i = 0;
for (UUID id : ids) {
q.setParameter(++i, id.getRaw());
}
return q.getResultList();
}
finally {
close(em);
}
}

And here is the rewritten form of the method that results in properly populated Sub instances :

public List<Sub> findSubInstances(List<UUID> ids) {
if (ids.isEmpty()) {
return Collections.EMPTY_LIST;
}
EntityManager em = getEntityManager();
Transaction tx = getTransaction(em);
try {
tx.begin();
Session session = getTopLinkSession(em);
ReadAllQuery raq = new ReadAllQuery(Sub.class);
ExpressionBuilder bldr = raq.getExpressionBuilder();
Expression exp = null;
int numIds = ids.size();
if (numIds > 0){
exp = bldr.get("supId").equal(ids.get(0));
for (int i = 1; i < numIds; ++i){
exp = exp.or(bldr.get("supId").equal(ids.get(i)));
}
}
if (exp != null){
raq.setSelectionCriteria(exp);
}
raq.addOrdering(bldr.get("audit").get("entryDtm"));
return session.executeQuery(raq);
}
finally {
tx.commit();
close(em);
}
}

Hope that helps narrow in on the issue.

Thanks,

Doug
Re: Inherited OneToOne relationship not hydrated properly [message #542433 is a reply to message #542362] Thu, 24 June 2010 19:43 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
Hello,

The problem is due to case sensitivity. I'm not sure which database you are using, but because you are using Native SQL for your query, EclipseLink must look up the columnames in the results to find the correct values. It uses the strings defined in the mappings, so if the database you are using returns columnames in uppercase (Oracle and others do), it will not match the "lineStringClobId" defined in the @JoinColumn(name="lineStringClobId").

This is common issue with native SQL queries, and is somewhat solved in bug
https://bugs.eclipse.org/bugs/show_bug.cgi?id=299926

Solutions are to change the columnames to match the case used in the database (defaults are always uppercase, so you will probably want to be consistent anyway), or use a 2.1 EclipseLink version with the fix and set the eclipselink.jpa.uppercase-column-names property to true.

Best Regards,
Chris

Re: Inherited OneToOne relationship not hydrated properly [message #542451 is a reply to message #542433] Thu, 24 June 2010 21:41 Go to previous message
Doug Gschwind is currently offline Doug GschwindFriend
Messages: 18
Registered: July 2009
Junior Member
Hi Chris,

Right you are, we are using an Oracle database, and your suggested fix did in fact resolve the issue for us. That is obviously a very easy change, so easily done. We will also pay attention to our mapping metadata in that regard going forward, just in case.

Thank you,

Doug
Previous Topic:managing in-memory references held on the owning side
Next Topic:Enum literals in JPQL IN expressions
Goto Forum:
  


Current Time: Thu Nov 27 08:49:08 GMT 2014

Powered by FUDForum. Page generated in 0.02776 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software