Hi Laird,
Can you catch the exception and try calling
JPAHelper.getEntityManager(em).getServerSession().getIdentityMapAccessor().printIdentityMap(AnswerEntity.class)
?
This will show what is in the shared cache for this class when the
exception occurs. Also, can you turn logging on to finest? This will
show the exact revision of EclipseLink being used (I'm not sure which
was
picked up by Glassfish 3.1b36), and the log might help track down how a
null is getting into the cache though hopefully it is not needed.
I believe it might be being nulled out due to garbage collection. If
that is the case, a workaround might be to increase the size of the
cache used for that object (assuming you are using a
softCacheWeakIdentityMap), switch to a fullidentitymap, or set it to
be isolated.
Best Regards,
Chris
On 10/01/2011 10:31 AM, Laird Nelson wrote:
(Oh, not a simple bug; my mistake in my earlier
response. Sorry; long day already and it's just begun.)
I've included a snippet of the code that builds and runs the query.
This use case is actually not necessarily all that strange. Imagine if
you will a survey application where multiple choice answers are stored
in tables, each of which has a structure like all the others. Let's
say there's a table named HomelessShelters, and it has rows that name
homeless shelters in the greater metro area. Each row has an ID ("7")
and a text ("Pine Street Inn").
From these identically structured tables we can build up a set of
Answer entities. An Answer entity is mapped like this:
@Entity(name = "Answer")
@IdClass(AnswerEntity.AnswerID.class)
@Table(name =
"Answer")
public class
AnswerEntity implements Answer {
/**
* The {@link
NamedAnswerSet} to which this {@link AnswerEntity}
* belongs. This
field may be {@code null} only at construction
* time.
*/
@JoinColumn(insertable = false, name = "ANSWERSETNAME", nullable =
false, updatable = false) // name must be uppercase for portability
@ManyToOne(fetch
= FetchType.EAGER, optional = false, targetEntity =
NamedAnswerSetEntity.class)
@NotNull
NamedAnswerSet
answerSet;
/**
* The opaque and
unique identifier for this {@link AnswerEntity}.
*/
@Column(name =
"ID") // name must be uppercase for portability
@Id
private int id;
@Column(name =
"ANSWERSETNAME") // name must be uppercase for portability
@Id
@NotNull
private String
answerSetName;
/**
* The text
describing this {@link AnswerEntity}. This field may be
* {@code null}
only at construction time.
*/
@Basic(optional =
false)
@Column(length =
100, name = "TEXT", nullable = false) // name must be uppercase for
portability
@NotNull
private String
text;
/**
* A comma and/or
whitespace-delimited {@link String} of identifiers
* usually
supplied by the {@link TableAnswerSetEntity} that built
* this {@link
AnswerEntity}. An {@link AnswerEntity} will
* typically sift
through this {@link String} to see if its {@link
* #getID() ID}
is contained in it.
*
* @see
#isFreeResponsePermitted()
*/
@Column(name =
"FREERESPONSEIDS", length = 255, updatable = false)
private String
freeResponseIds;
/**
* A {@link
Boolean} representing the result of combing through the
* {@link
#freeResponseIds} field for our own {@link #getID() id}.
*
* @see
#isFreeResponsePermitted()
*/
@Transient
private Boolean
freeResponsePermitted;
}
Logically all AnswerEntities belong to AnswerSets. One kind of
AnswerSet is one drawn from a table in the manner that I've described.
Others include constrained numeric AnswerSets; still others are defined
more or less out of thin air and backed by Drools, the JBoss rules
engine. Short answer: build an AnswerEntity one way or another, and
that's what you pick when you choose an answer to a multiple choice
question.
So this query is concerned with constructing AnswerEntities out of
particular kinds of tables.
Here is a snippet of code that builds and runs the query. I have not
yet sanitized the inputs; obviously I need to guard against SQL
injection here in a way that normally, using garden variety
PreparedStatements, I wouldn't.
final String
tableName = "HomelessShelters"; // real code obviously gets this from
somewhere else
final String answerSetName = this.getName(); // e.g.
"HomelessSheltersAnswerSet"
assert
answerSetName != null;
final String sql;
final StringBuilder
sb = new StringBuilder("SELECT t.%s AS ID, '%s' AS ANSWERSETNAME, t.%s
AS TEXT, %s AS FREERESPONSEIDS FROM %s t");
if
(orderByColumnName != null) {
sb.append(" ORDER
BY %s");
sql =
String.format(sb.toString(), this.getIDColumnName(), answerSetName,
this.getTextColumnName(), this.freeResponseIds == null ? "NULL" :
String.format("\"%s\"", this.freeResponseIds), tableName,
orderByColumnName);
} else {
sql =
String.format(sb.toString(), this.getIDColumnName(), answerSetName,
this.getTextColumnName(), this.freeResponseIds == null ? "NULL" :
String.format("\"%s\"", this.freeResponseIds), tableName);
}
final Query q =
em.createNativeQuery(sql, AnswerEntity.class);
assert q != null;
@SuppressWarnings("unchecked")
final
List<AnswerEntity> results = q.getResultList();
assert results !=
null;
for (final
AnswerEntity a : results) {
if (a != null) {
a.setAnswerSet(this);
}
}
returnValue = new
LinkedHashSet<AnswerEntity>(results);
So nothing too odd going on here, I don't think, at least where the
query is concerned.
Best,
Laird
On Mon, Jan 10, 2011 at 9:53 AM, Tom Ware <tom.ware@xxxxxxxxxx>
wrote:
You might want to look at how FreeResponse is mapped. What is the @Id,
is there a way a component of the @Id can be unexpectedly made null by
the user.
How is the query executed? Is there a result set mapping?
-Tom
Laird Nelson wrote:
I noticed that too; simple bug; should have been
t.freeResponseIds.
The query itself is actually dynamic and the table name is supplied at
query build time. The intent is to turn this native query into a set
of entities, which works fine.
(There is a set of tables in my application that are under the control
of the user; they all share these fields in common. Through various
gyrations of the app, the user can pick which table to build normalized
entities out of; this native query gets assembled from the table name.
So a simple entity mapping doesn't work, because the table name is not
known at compile/startup time.)
I'll keep an eye on this and see if I can narrow down what I was doing
when it happens again. I cannot reproduce this in a unit test (I've
tried, believe me).
Best,
Laird
On Mon, Jan 10, 2011 at 9:39 AM, Tom Ware < tom.ware@xxxxxxxxxx <mailto: tom.ware@xxxxxxxxxx>> wrote:
This exception seems to occur when trying to set a value in a null
object. I am not sure what would cause EclipseLink to try to do
that. Can you provide any other information about what is occuring?
I notice that your example query selects "freeResponseIds" but does
not link it to anything else in your query. (i.e. you select t.id
<http://t.id>,
t.text with "t." but freeResponseIds without the
"t.") What is the goal here?
-Tom
Laird Nelson wrote:
I have an application that runs a simple JPA native query. 99
out of 100 times the query works fine. Then every now and again
I get this:
Caused by: java.lang.NullPointerException
at
org.eclipse.persistence.internal.descriptors.PersistenceObjectAttributeAccessor.setAttributeValueInObject(PersistenceObjectAttributeAccessor.java:46)
at
org.eclipse.persistence.mappings.DatabaseMapping.setAttributeValueInObject(DatabaseMapping.java:1426)
at
org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1317)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:343)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.refreshObjectIfRequired(ObjectBuilder.java:3333)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneFromRow(ObjectBuilder.java:1495)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:559)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:496)
at
org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:455)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:723)
at
org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:743)
at
org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:424)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1080)
at
org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1040)
at
org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:384)
at
org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1126)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2904)
at
org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1508)
at
org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1490)
at
org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1464)
at
org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:484)
at
org.eclipse.persistence.internal.jpa.EJBQueryImpl.getResultList(EJBQueryImpl.java:741)
Once the query has failed in this manner, it will continue to
fail until application restart.
I am using the EclipseLink version that is bundled with
Glassfish 3.1b36.
My query is SELECT t.id <http://t.id> <http://t.id>
AS ID,
'FreeResponse' AS ANSWERSETNAME, t.text AS TEXT, freeResponseIds
AS FREERESPONSEIDS FROM FreeResponse t
What is EclipseLink trying to tell me here?
Thanks,
Laird
------------------------------------------------------------------------
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
<mailto:eclipselink-users@xxxxxxxxxxx>
eclipselink-users@xxxxxxxxxxx <mailto:eclipselink-users@xxxxxxxxxxx>
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
|