Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] NullPointerException in PersistenceObjectAttributeAccessor

(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>

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


Back to the top