Domanda

I'm trying to build a dynamic query, using JPA Criteria, to retrieve data from gae-datastore, using parameters from an advanced search form. The form has five fields, type, min amount, max amount, from date and to date. Type, min and max amount, work quite well, but with date fields I've got this exception

SEVERE: javax.persistence.PersistenceException: expected ')' at character 68 in "(DN_THIS.author = '18580476422013912411') AND (DN_THIS.date >= Wed Jan 01 00:00:00 CET 2014) AND (DN_THIS.date <= Fri Jan 31 00:00:00 CET 2014)"

This is the method in my DAO that throw the exception:

    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;
}

This is the resulting query

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 <= Fri Jan 31 00:00:00 CET 2014)

This is my entity

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 + "]";
    }   
}

The curious thing is that I've got a similar method in my 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; 
}

and this works perfectly.

My setup is GAE SDK 1.8.9, JPA 2.0, datanucleus-core 3.1.3, datanucleus-jpa-api 3.1.3 and datanucleus-appengine-2.1.2.

Any help will be appreciated! Thanks in advance.

È stato utile?

Soluzione

This is my solution:

The DAO class

    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;
}

Then metamodel class added:

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;    
}

Altri suggerimenti

I would always specify parameters rather than literals, since then you get the benefit of query/statement reuse, particularly with date literals, since they aren't universally supported by datastores, whereas parameters are

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top