(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
<mailto: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> <mailto: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>
<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>
<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>
<mailto:eclipselink-users@xxxxxxxxxxx
<mailto:eclipselink-users@xxxxxxxxxxx>>
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
<mailto:eclipselink-users@xxxxxxxxxxx>
<mailto:eclipselink-users@xxxxxxxxxxx
<mailto:eclipselink-users@xxxxxxxxxxx>>
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
------------------------------------------------------------------------
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx <mailto:eclipselink-users@xxxxxxxxxxx>
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx <mailto:eclipselink-users@xxxxxxxxxxx>
https://dev.eclipse.org/mailman/listinfo/eclipselink-users
------------------------------------------------------------------------
_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users