为什么JPQL Left Join ON Clause导致NullPointerException?

时间:2016-04-22 08:52:31

标签: jpa eclipselink jpql

虽然这个JPQL没有任何异常

Query query = em.createQuery("SELECT DISTINCT(r) FROM Request r LEFT JOIN Requesthistory h 
                                                    WHERE h.request = r 
                                                    AND (r.responsible = :employee OR h.employee = :employee) 
                                                    AND r.requestedby != :employee ORDER BY r.objid DESC");

此JPQL无法正常工作并抛出NullPointerException

Query query = em.createQuery("SELECT DISTINCT(r) FROM Request r LEFT JOIN Requesthistory h 
                                                ON h.request = r 
                                                WHERE (r.responsible = :employee OR h.employee = :employee) 
                                                AND r.requestedby != :employee ORDER BY r.objid DESC");

这两个JPQL之间的唯一区别是ON Clause。

是的,实体和表格中的Request和Requesthistory之间存在1:N关系。

这是我得到的例外:

Exception Description: Query failed to prepare, unexpected error occurred: [java.lang.NullPointerException].
Internal Exception: java.lang.NullPointerException
Query: ReportQuery(referenceClass=Request jpql="SELECT DISTINCT(r) FROM Request r LEFT JOIN Requesthistory h ON h.request = r WHERE  (r.responsible = :employee OR h.employee = :employee) AND r.requestedby != :employee ORDER BY r.objid DESC")
    at org.eclipse.persistence.exceptions.QueryException.prepareFailed(QueryException.java:1590) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:680) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:901) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:613) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:194) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:116) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:102) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:86) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1603) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at com.kadir.dao.notification.RequestDao.findToUserRequests(RequestDao.java:44) [classes:]
    at com.kadir.service.notification.RequestService.getToUserRequests(RequestService.java:43) [classes:]
    at com.kadir.bean.notification.RequestBean.init(RequestBean.java:49) [classes:]
    ... 110 more
Caused by: java.lang.NullPointerException
    at org.eclipse.persistence.internal.expressions.ObjectExpression.getOwnedTables(ObjectExpression.java:583) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.FieldExpression.validateNode(FieldExpression.java:294) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.expressions.Expression.normalize(Expression.java:3275) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.DataExpression.normalize(DataExpression.java:369) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.FieldExpression.normalize(FieldExpression.java:223) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:224) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:574) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:865) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.expressions.ExpressionBuilder.normalize(ExpressionBuilder.java:267) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:825) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:232) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:224) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1449) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:641) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:586) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareReportQuerySelectAllRows(ExpressionQueryMechanism.java:1694) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.ReportQuery.prepareSelectAllRows(ReportQuery.java:1203) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:744) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.ReportQuery.prepare(ReportQuery.java:1071) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:661) [eclipselink.jar:2.5.2.v20140319-9ad6abd]
    ... 120 more

我做错了什么?

更新实体

Request.java

package com.kadir.entity.notification;

import com.kadir.entity.Base;
import com.kadir.entity.humanresource.Employee;
import java.io.Serializable;
import javax.persistence.*;


import java.util.List;


/**
 * The persistent class for the REQUEST database table.
 * 
 */
@Cacheable
@Entity
@Table(name="REQUEST", schema="NOTIFICATION")
@NamedQuery(name="Request.findAll", query="SELECT r FROM Request r")
public class Request extends Base implements Serializable {
    private static final long serialVersionUID = 1L;

    @Column(name="CONTENT")
    private String content;

    @ManyToOne
    @JoinColumn(name="REQUESTEDBY")
    private Employee requestedby;

    @ManyToOne
    @JoinColumn(name="RESPONSIBLEOBJID")
    private Employee responsible;

    @Column(name="TITLE")
    private String title;

    //bi-directional many-to-one association to Requesttype
    @ManyToOne
    @JoinColumn(name="REQUESTTYPEOBJID")
    private Requesttype requesttype;

    //bi-directional many-to-one association to Responsetype
    @ManyToOne
    @JoinColumn(name="RESPONSETYPEOBJID")
    private Responsetype responsetype;

    //bi-directional many-to-one association to Requesthistory
    @OneToMany(mappedBy="request")
    private List<Requesthistory> requesthistories;

    public Request() {
    }

    public String getContent() {
        return this.content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Employee getRequestedby() {
        return this.requestedby;
    }

    public void setRequestedby(Employee requestedby) {
        this.requestedby = requestedby;
    }

    public Employee getResponsible() {
        return this.responsible;
    }

    public void setResponsible(Employee responsible) {
        this.responsible = responsible;
    }

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Requesttype getRequesttype() {
        return this.requesttype;
    }

    public void setRequesttype(Requesttype requesttype) {
        this.requesttype = requesttype;
    }

    public Responsetype getResponsetype() {
        return this.responsetype;
    }

    public void setResponsetype(Responsetype responsetype) {
        this.responsetype = responsetype;
    }

    public List<Requesthistory> getRequesthistories() {
        return this.requesthistories;
    }

    public void setRequesthistories(List<Requesthistory> requesthistories) {
        this.requesthistories = requesthistories;
    }

    public Requesthistory addRequesthistory(Requesthistory requesthistory) {
        getRequesthistories().add(requesthistory);
        requesthistory.setRequest(this);

        return requesthistory;
    }

    public Requesthistory removeRequesthistory(Requesthistory requesthistory) {
        getRequesthistories().remove(requesthistory);
        requesthistory.setRequest(null);

        return requesthistory;
    }

}

RequestHistory.java

package com.kadir.entity.notification;

import com.kadir.entity.humanresource.Employee;
import com.kadir.entity.Base;
import java.io.Serializable;
import javax.persistence.*;

/**
 * The persistent class for the REQUESTHISTORY database table.
 * 
 */
@Cacheable
@Entity
@Table(name="REQUESTHISTORY", schema="NOTIFICATION")
@NamedQuery(name="Requesthistory.findAll", query="SELECT r FROM Requesthistory r")
public class Requesthistory extends Base implements Serializable {
    private static final long serialVersionUID = 1L;

    @ManyToOne
    @JoinColumn(name="EMPLOYEEOBJID")
    private Employee employee;

    @Column(name="EXPLANATION")
    private String explanation;

    //bi-directional many-to-one association to Request
    @ManyToOne
    @JoinColumn(name="REQUESTOBJID")
    private Request request;

    //bi-directional many-to-one association to Responsetype
    @ManyToOne
    @JoinColumn(name="RESPONSETYPEOBJID")
    private Responsetype responsetype;

    public Requesthistory() {
    }

    public Employee getEmployee() {
        return this.employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public String getExplanation() {
        return this.explanation;
    }

    public void setExplanation(String explanation) {
        this.explanation = explanation;
    }

    public Request getRequest() {
        return this.request;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public Responsetype getResponsetype() {
        return this.responsetype;
    }

    public void setResponsetype(Responsetype responsetype) {
        this.responsetype = responsetype;
    }

}

Base.java

package com.kadir.entity;

import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;

@Cacheable
@MappedSuperclass
public abstract class Base {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "OBJID")
    private BigInteger objid;

    @Column(name = "CREATEDBY")
    private String createdby;

    @Column(name = "CREATEDDATE")
    private Timestamp createddate;

    @Version
    @Column(name = "ROWVERSION")
    private Integer rowversion;

    @Column(name = "UPDATEDBY")
    private String updatedby;

    @Column(name = "UPDATEDDATE")
    private Timestamp updateddate;

    @Column(name = "ARCHIVED", columnDefinition = "int default 0")
    private int archived;

    public BigInteger getObjid() {
        return this.objid;
    }

    public void setObjid(BigInteger objid) {
        this.objid = objid;
    }

    public String getCreatedby() {
        return this.createdby;
    }

    public void setCreatedby(String createdby) {
        this.createdby = createdby;
    }

    public Date getCreateddate() {
        return this.createddate;
    }

    public void setCreateddate(Timestamp createddate) {
        this.createddate = createddate;
    }

    public Integer getRowversion() {
        return this.rowversion;
    }

    public void setRowversion(Integer rowversion) {
        this.rowversion = rowversion;
    }

    public String getUpdatedby() {
        return this.updatedby;
    }

    public void setUpdatedby(String updatedby) {
        this.updatedby = updatedby;
    }

    public Timestamp getUpdateddate() {
        return this.updateddate;
    }

    public void setUpdateddate(Timestamp updateddate) {
        this.updateddate = updateddate;
    }

    public int getArchived() {
        return archived;
    }

    public void setArchived(int archived) {
        this.archived = archived;
    }
}

1 个答案:

答案 0 :(得分:1)

我认为问题在于您的继承映射。 映射的超级类Base没有定义继承策略,因此默认使用一个SINGLE_TABLE。接下来,在每个子库@Table(name="...")Request上定义Requesthistory注释使用SINGLE_TABLE策略没有意义,因为此策略将所有子类映射到一个共享表。 我认为TABLE_PER_CLASS是你想要的策略。

@Cacheable
@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Base {

仔细查看您的查询后更新

在jpql中,join子句在sql中使用有点不同。您不能加入任意实体,然后通过ON或WHERE子句绑定它们。您只能加入与抽象模式关联的实体。

尝试以下(jpql valid)查询:

 SELECT DISTINCT(r) 
 FROM Request r 
 LEFT JOIN r.requesthistories h 
 WHERE (r.responsible = :employee OR h.employee = :employee)  
     AND r.requestedby != :employee 
 ORDER BY r.objid DESC

(注意:非关联实体的查询是可行的,但它是不同的主题。)