【问题标题】:Exception on JPA Criteria query using date field使用日期字段的 JPA 标准查询异常
【发布时间】:2014-01-26 09:20:53
【问题描述】:

我正在尝试使用 JPA 标准构建一个动态查询,以使用高级搜索表单中的参数从 gae-datastore 检索数据。该表单有五个字段,类型、最小金额、最大金额、从日期和到日期。类型,最小和最大数量,工作得很好,但是对于日期字段我有这个例外

严重:javax.persistence.PersistenceException:在“(DN_THIS.author = '18580476422013912411')和(DN_THIS.date >= Wed Jan 01 00:00:00 CET 2014)和(DN_THIS)中的字符 68 处预期为 ')' .date

这是我的 DAO 中引发异常的方法:

    public List<Outlay> findByParameters(String author, String page, String pageSize,  
    String type, String minAmount, String maxAmount, String fromDate, String toDate)
    throws PersistenceException, ParseException {

    EntityManager em = EMF.get().createEntityManager();

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Outlay> cq = cb.createQuery(Outlay.class);
    Root<Outlay> outlay = cq.from(Outlay.class);

    List<Predicate> predicates = new ArrayList<Predicate>();

    if(author != null && !author.isEmpty()) {
        predicates.add(cb.equal(outlay.<String>get("author"), author));
    }

    if(type != null && !type.isEmpty()) {
        predicates.add(cb.equal(outlay.<String>get("type"), type));
    }

    if(minAmount != null && !minAmount.isEmpty()) {                             
        Double min = Double.parseDouble(minAmount);
        predicates.add(cb.ge(outlay.<Double>get("amount"), min));
    }

    if(maxAmount != null && !maxAmount.isEmpty()) {
        Double max = Double.parseDouble(maxAmount);
        predicates.add(cb.le(outlay.<Double>get("amount"), max));
    }

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");

    if(fromDate != null && !fromDate.isEmpty()) {
        Date from = formatter.parse(fromDate);
        log.info("From: " + formatter.format(from));
        predicates.add(cb.greaterThanOrEqualTo(outlay.<Date>get("date"), from));
    }

    if(toDate != null && !toDate.isEmpty()) {           
        Date to = formatter.parse(toDate);          
        log.info("To: " + formatter.format(to));    
        predicates.add(cb.lessThanOrEqualTo(outlay.<Date>get("date"), to));
    }       

    cq.select(outlay).where(predicates.toArray(new Predicate[]{}));

    log.info("Query: " + cq.toString());

    TypedQuery<Outlay> query = em.createQuery(cq);

    Integer index = 0;   
    if(page != null && !page.isEmpty()) {
        index = Integer.parseInt(page);
    }

    Integer step = 10;      
    if(pageSize != null && !pageSize.isEmpty()) {
        step = Integer.parseInt(pageSize);
    }       

    query.setFirstResult(index * step);
    query.setMaxResults(step);

    List<Outlay> outlayList = new ArrayList<Outlay>();

    try {
        outlayList = query.getResultList();         

        log.info("Retrieved " + outlayList.size() + " Outlay");                                                 
    } finally {         
        em.close();
    }

    return outlayList;
}

这是结果查询

SELECT DN_THIS FROM com.mycompany.model.Outlay DN_THIS WHERE (DN_THIS.author = '18580476422013912411') AND (DN_THIS.date >= Wed Jan 01 00:00:00 CET 2014) AND (DN_THIS.date

这是我的实体

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Outlay implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id;

    private String author;
    private String type;
    private Double amount;
    private Date date;
    private String description; 

    Outlay() { }

    public Outlay(String author, String type, Double amount, Date date,
            String description) {
        super();
        this.author = author;
        this.type = type;
        this.amount = amount;
        this.date = date;
        this.description = description;
    }

    public long getId() {
        return id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Double getAmount() {
        return amount;
    }

    public void setAmount(Double amount) {
        this.amount = amount;
    }

    @Temporal(TemporalType.DATE)
    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public String getDescription() {
        return description;
    }

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

    @Override
    public String toString() {
        return "Outlay [id=" + id + ", author=" + author + ", type=" + type
                + ", amount=" + amount + ", date=" + date + "]";
    }   
}

奇怪的是,我的 DAO 中也有类似的方法

    public Long countByParameters(String author, String type, String minAmount,  
        String maxAmount, String fromDate, String toDate)
        throws PersistenceException, ParseException {

    EntityManager em = EMF.get().createEntityManager();

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Long> cq = cb.createQuery(Long.class);
    Root<Outlay> outlay = cq.from(Outlay.class);

    List<Predicate> predicates = new ArrayList<Predicate>();

    if(author != null && !author.isEmpty()) {
        predicates.add(cb.equal(outlay.<String>get("author"), author));
    }

    if(type != null && !type.isEmpty()) {
        predicates.add(cb.equal(outlay.<String>get("type"), type));
    }

    if(minAmount != null && !minAmount.isEmpty()) {                             
        Double min = Double.parseDouble(minAmount);
        predicates.add(cb.ge(outlay.<Double>get("amount"), min));
    }

    if(maxAmount != null && !maxAmount.isEmpty()) {
        Double max = Double.parseDouble(maxAmount);
        predicates.add(cb.le(outlay.<Double>get("amount"), max));
    }

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");

    if(fromDate != null && !fromDate.isEmpty()) {
        Date from = formatter.parse(fromDate);
        log.info("From: " + formatter.format(from));
        predicates.add(cb.greaterThanOrEqualTo(outlay.<Date>get("date"), from));
    }

    if(toDate != null && !toDate.isEmpty()) {           
        Date to = formatter.parse(toDate);          
        log.info("To: " + formatter.format(to));            
        predicates.add(cb.lessThanOrEqualTo(outlay.<Date>get("date"), to));
    }       

    cq.select(cb.count(outlay)).where(predicates.toArray(new Predicate[]{}));

    log.info("Query: " + cq.toString());

    Long count = 0l;

    try {           
        count = em.createQuery(cq).getSingleResult();

        log.info("OutlayList " + count + " size");                                                  
    } finally {         
        em.close();
    }

    return count; 
}

这很完美。

我的设置是 GAE SDK 1.8.9、JPA 2.0、datanucleus-core 3.1.3、datanucleus-jpa-api 3.1.3 和 datanucleus-appengine-2.1.2。

任何帮助将不胜感激! 提前致谢。

【问题讨论】:

    标签: google-cloud-datastore jpa-2.0 criteria datanucleus


    【解决方案1】:

    这是我的解决方案:

    DAO 类

        public List<Outlay> findByParameters(String author, String page, String pageSize,  
        String type, String minAmount, String maxAmount, String fromDate, String toDate)
        throws PersistenceException, ParseException {
    
        EntityManager em = EMF.get().createEntityManager();
    
        CriteriaBuilder cb = em.getCriteriaBuilder();       
        CriteriaQuery<Outlay> cq = cb.createQuery(Outlay.class);
        Root<Outlay> outlay = cq.from(Outlay.class);
    
        List<Predicate> predicates = new ArrayList<Predicate>();
    
        if(author != null && !author.isEmpty()) {
            predicates.add(cb.equal(outlay.get(Outlay_.author), 
                cb.parameter(String.class, "author")));
        }
    
        if(type != null && !type.isEmpty()) {
            predicates.add(cb.equal(outlay.get(Outlay_.type), 
                cb.parameter(String.class, "type")));
        }
    
        if(minAmount != null && !minAmount.isEmpty()) {
            predicates.add(cb.ge(outlay.get(Outlay_.amount), 
                cb.parameter(Double.class, "minAmount")));
        }
    
        if(maxAmount != null && !maxAmount.isEmpty()) {
            predicates.add(cb.le(outlay.get(Outlay_.amount), 
                cb.parameter(Double.class, "maxAmount")));
        }
    
        if(fromDate != null && !fromDate.isEmpty()) {
            predicates.add(cb.greaterThanOrEqualTo(outlay.get(Outlay_.date), 
                cb.parameter(Date.class, "fromDate")));
        }
    
        if(toDate != null && !toDate.isEmpty()) {
            predicates.add(cb.lessThanOrEqualTo(outlay.get(Outlay_.date), 
                    cb.parameter(Date.class, "toDate")));           
        }
    
        cq.select(outlay).where(predicates.toArray(new Predicate[]{}));
    
        TypedQuery<Outlay> query = em.createQuery(cq);
    
        if(author != null && !author.isEmpty()) {
            query.setParameter("author", author);
        }
    
        if(type != null && !type.isEmpty()) {
            query.setParameter("type", type);
        }
    
        if(minAmount != null && !minAmount.isEmpty()) {
            query.setParameter("minAmount", Double.parseDouble(minAmount));
        }
    
        if(maxAmount != null && !maxAmount.isEmpty()) {
            query.setParameter("maxAmount", Double.parseDouble(maxAmount));
        }
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
    
        if(fromDate != null && !fromDate.isEmpty()) {
            query.setParameter("fromDate", formatter.parse(fromDate));
        }
    
        if(toDate != null && !toDate.isEmpty()) {
            query.setParameter("toDate", formatter.parse(toDate));
        }
    
        Integer index = 0;   
        if(page != null && !page.isEmpty()) {
            index = Integer.parseInt(page);
        }
    
        Integer step = 10;      
        if(pageSize != null && !pageSize.isEmpty()) {
            step = Integer.parseInt(pageSize);
        }       
    
        query.setFirstResult(index * step);
        query.setMaxResults(step);
    
        List<Outlay> outlayList = new ArrayList<Outlay>();
    
        try {
            outlayList = query.getResultList();
    
            log.info("Retrieved " + outlayList.size() + " Outlay");                                                 
        } finally {         
            em.close();
        }
    
        return outlayList;
    }
    

    然后添加元模型类:

    package com.mycompany.model;
    
    import java.util.Date;
    
    import javax.persistence.metamodel.SingularAttribute;
    
    @javax.persistence.metamodel.StaticMetamodel(com.mycompany.model.Outlay.class)
    
    public class Outlay_ {
        public static volatile SingularAttribute<Outlay, String> author;
        public static volatile SingularAttribute<Outlay, String> type;
        public static volatile SingularAttribute<Outlay, Double> amount;
        public static volatile SingularAttribute<Outlay, Date> date;    
    }
    

    【讨论】:

      【解决方案2】:

      我总是会指定参数而不是文字,从那时起,您将获得查询/语句重用的好处,尤其是日期文字,因为数据存储并不普遍支持它们,而参数是

      【讨论】:

      • 那么 JPA 元模型呢? Google App Engine 是否支持?
      • 怎么样? JPA 元模型与类相关,而不是与数据存储相关,所以不明白为什么不这样。但是你上面发布的问题不是关于元模型,而是关于一些例外,这就是我的回答所涉及的。
      • 我正在考虑使用元模型来重新定义我的查询(如本页所述docs.oracle.com/javaee/6/tutorial/doc/gjivm.html)。现在我将尝试使用您所说的参数。
      • 如何处理可变数量的参数和 JPA Criteria 查询?我用谷歌搜索(找到 link ),但找不到合适的方法。
      • 正如该问题的答案所说的很清楚,使用 CriteriaBuilder.parameter 是包含参数的方式,您可以在其中为每个参数设置名称。不知道变量数与这个有什么关系......他们有名字
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      • 1970-01-01
      • 2012-02-29
      • 2012-09-25
      • 2012-01-19
      • 2019-03-09
      • 1970-01-01
      相关资源
      最近更新 更多