persistence@glassfish.java.net

Is it legal to have a JPQL query that has multiple LEFT JOIN FETCHes?

From: Jon Miller <jemiller_at_uchicago.edu>
Date: Mon, 19 Feb 2007 14:54:03 -0600

Hi all,

I'm not sure if what I'm trying to do is legal or advisable. I'm attempting
to execute a query that has two LEFT JOINs. I'm receiving a
NullPointerException. I'm thinking that what I'm trying to do may not be
legal, but, it doesn't seem like I should be receiving a NPE. I'm using GP
V2 B33. I have an Item class which has reservations, super items, and sub
items. What I'm trying to figure out is the optimal way to do a query so
that these three collections are all eagerly loaded. I want to try to limit
the number of subqueries that are being executed. However, I'm wondering of
eagerly loading them will cause performance issues as well.

The other thing that I'm wondering about is the fact that the sub and super
items collections also point to Item objects and whether that will cause a
problem (for example, if I marked the collections as being eagerly loaded
rather than trying to do it using a query).

Feb 19, 2007 2:43:53 PM edu.uchicago.at.reservations.ReservationsService
findAvailableItems
FINEST: SELECT i FROM Item i LEFT JOIN FETCH i.reservations LEFT JOIN FETCH
i.subItems ORDER BY i.name
Feb 19, 2007 2:43:53 PM
oracle.toplink.essentials.session.file:/J:/Reservations/ReservationsLibrary/build/classes/-Reservations
WARNING:
java.lang.NullPointerException
 at
oracle.toplink.essentials.internal.expressions.SQLSelectStatement.appendFromClauseForOuterJoin(SQLSelectStatement.java:347)
 at
oracle.toplink.essentials.internal.expressions.SQLSelectStatement.appendFromClauseToWriter(SQLSelectStatement.java:452)
 at
oracle.toplink.essentials.internal.expressions.SQLSelectStatement.printSQL(SQLSelectStatement.java:1310)
 at
oracle.toplink.essentials.internal.expressions.SQLSelectStatement.buildCall(SQLSelectStatement.java:683)
 at
oracle.toplink.essentials.descriptors.ClassDescriptor.buildCallFromStatement(ClassDescriptor.java:548)
 at
oracle.toplink.essentials.internal.queryframework.StatementQueryMechanism.setCallFromStatement(StatementQueryMechanism.java:393)
 at
oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.prepareReportQuerySelectAllRows(ExpressionQueryMechanism.java:1379)
 at
oracle.toplink.essentials.queryframework.ReportQuery.prepareSelectAllRows(ReportQuery.java:988)
 at
oracle.toplink.essentials.queryframework.ReadAllQuery.prepare(ReadAllQuery.java:398)
 at
oracle.toplink.essentials.queryframework.ReportQuery.prepare(ReportQuery.java:904)
 at
oracle.toplink.essentials.queryframework.DatabaseQuery.checkPrepare(DatabaseQuery.java:387)
 at
oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:469)
 at
oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:587)
 at
oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:677)
 at
oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:731)
 at
oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2219)
 at
oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:937)
 at
oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:909)
 at
oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:335)
 at
oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getResultList(EJBQueryImpl.java:442)
 at
edu.uchicago.at.reservations.ReservationsService.findAvailableItems(ReservationsService.java:61)
 at
edu.uchicago.at.reservations.ReservationsServiceTest.testFindAvailableItems(ReservationsServiceTest.java:91)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at junit.framework.TestCase.runTest(TestCase.java:154)
 at junit.framework.TestCase.runBare(TestCase.java:127)
 at junit.framework.TestResult$1.protect(TestResult.java:106)

package edu.uchicago.at.reservations.persistence.entity;

import edu.uchicago.at.common.persistence.entity.AuditObject;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;

@Entity
@Table(name="Item")
public class Item extends AuditObject implements Comparable<Item> {
    private String description;
    private Integer id;
    private String name;
    private List<Reservation> reservations = new ArrayList<Reservation>();
    private List<Item> subItems = new ArrayList<Item>();
    private List<Item> superItems = new ArrayList<Item>();

    public Item() {
    }

    public Item(String name) {
        setName(name);
    }

    public int compareTo(Item item) {
        if(name == null && item.getName() == null) {
            return 0;
        }
        if(name == null) {
            return -1;
        }
        if(item.getName() == null) {
            return 1;
        }
        return name.compareTo(item.getName());
    }

    public boolean contains(Item item) {
        for(Item i : subItems) {
            if (i.id == item.id) {
                return true;
            }
            if (i.contains(item)) {
                return true;
            }
        }
        return false;
    }

    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null) {
            return false;
        }
        if(!(o instanceof Item)) {
            return false;
        }
        Item i = (Item)o;
        if(id == null && i.getId() != null || id != null && i.getId() ==
null) {
            return false;
        }
        if(id == i.getId() || id.equals(i.getId())) {
            return true;
        }
        return false;
    }

    public int hashCode() {
        if(id == null) {
            return 0;
        }
        return id.hashCode();
    }

    @Column(name="Description"/*, columnDefinition="nvarchar(max)"*/)
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="Id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name="Name"/*, columnDefinition="nvarchar(255)"*/)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToMany(mappedBy="items")
    @OrderBy("startTime")
    public List<Reservation> getReservations() {
        return reservations;
    }

    public void setReservations(List<Reservation> reservations) {
        this.reservations = reservations;
    }

    @ManyToMany
    @JoinTable(name="ItemItem", joinColumns={_at_JoinColumn(name="ItemId")},
        inverseJoinColumns={_at_JoinColumn(name="SubItemId")})
    @OrderBy("name")
    public List<Item> getSubItems() {
        return subItems;
    }

    public void setSubItems(List<Item> subItems) {
        this.subItems = subItems;
    }

    @ManyToMany(mappedBy="subItems")
    @OrderBy("name")
    public List<Item> getSuperItems() {
        return superItems;
    }

    public void setSuperItems(List<Item> superItems) {
        this.superItems = superItems;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(String.format("id = %d", id));
        sb.append(String.format(", name = \"%s\"", name));
        sb.append(String.format(", subItems = %s", subItems));
        sb.append(String.format(", description = \"%s\"", description));
        sb.append("]");
        return sb.toString();
    }
}

Jon