optional parameters jpa 2.1

2019-05-17 19:06发布

问题:

I have this class,

@Entity
public class Message {

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

  @Column( nullable = false )
  private String mobile;

  @Column( nullable = false )
  private String message;

  @Column( nullable = false )
  private Lang lang;

  @Column( nullable = false )
  private int status;

  @Column( nullable = false )
  private Calendar creationDate;
  ...
}

and I would like to be able to query the table with optional parameters from a form. I'm using JPA 2.1 and Hibernate. Is there way to do this without multiple query statements or without tightly coupling my application to hibernate or the specific database that i'm using?

Here is the form i'm using:

<form action="${pageContext.request.contextPath}/search">
<label>From</label>
<input type="datetime" placeholder="dd/mm/yy hh/mm" name="from" required> 
<label>To</label>
<input type="datetime" placeholder="dd/mm/yy hh/mm" name="to" required> 
<select name="lang">
    <option value="english">English</option>
    <option value="arabic">Arabic</option>
</select>
<input type="tel" placeholder="Recipient" name="recipient">
<select name="status">
    <option value="new">New</option>
    <option value="picked">Picked</option>
    <option value="sent">Sent</option>
    <option value="rejected">Rejected</option>
</select>
<input type="submit" value="Search">

UPDATE::::::::::::::::::::::::::::

I tried this:

    public List<Message> getMessages(Calendar fromDate, Calendar toDate, String lang, String recipient, String status) {
    /*TypedQuery<Message> query = em.createQuery(
            "SELECT e FROM Message e WHERE e.creationDate BETWEEN :fromDate AND :toDate", Message.class);
    query.setParameter("fromDate", fromDate);
    query.setParameter("toDate", toDate);
    List<Message> list = query.getResultList();
    return list;*/

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

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Message> query = cb.createQuery(Message.class);
    Root<Message> root = query.from(Message.class);

    predicates.add(cb.between(root.get("creationDate"), fromDate, toDate));

    if (lang != null && !lang.equalsIgnoreCase("any")) {
           predicates.add(cb.equal(root.get("lang"), lang));
        }

    if (recipient != null && !recipient.equals("")) {
           predicates.add(cb.equal(root.get("mobile"), recipient));
        }

    if (status != null && !status.equalsIgnoreCase("any")) {
           predicates.add(cb.equal(root.get("mobile"), recipient));
        }

    query.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
    List<Message> list = em.createQuery(query).getResultList();

    return list;
}

but I'm getting the following error:

java.lang.IllegalArgumentException: Parameter value [0] did not match expected type [entity.Lang (n/a)]
at org.hibernate.jpa.spi.BaseQueryImpl.validateBinding(BaseQueryImpl.java:897)
at org.hibernate.jpa.internal.QueryImpl.access$000(QueryImpl.java:61)
at org.hibernate.jpa.internal.QueryImpl$ParameterRegistrationImpl.bindValue(QueryImpl.java:235)
at org.hibernate.jpa.spi.BaseQueryImpl.setParameter(BaseQueryImpl.java:638)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:163)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:32)
at org.hibernate.jpa.criteria.compile.CriteriaCompiler$1$1.bind(CriteriaCompiler.java:109)
at org.hibernate.jpa.criteria.CriteriaQueryImpl$1.buildCompiledQuery(CriteriaQueryImpl.java:369)
at org.hibernate.jpa.criteria.compile.CriteriaCompiler.compile(CriteriaCompiler.java:130)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:699)
at dao.MessageDAO.getMessages(MessageDAO.java:63)

回答1:

Short answer: yes. You do not need to use hibernate-specific classes to build a query that fulfills your needs.

However, you may want to filter out your optional parameters before building your query. If tel is null/empty you probably don't want to have it in your query. So just don't add it to the query. Look at the Java EE persistence tutorial and the CriteriaBuilder javadoc to build your TypedQuery the way you need it.

Pseudo code (clearly untested... but I hope you get the idea):

List<Predicate> predicates = ...
CriteriaBuilder cb = ...
Root<?> table = ...
if (isNotBlank(tel)) {
   predicates.add(cb.equal(table.get("tel"), tel));
}
if (isNotBlank(...)) {
   predicates.add(cb.like(table.get("..."), ...));
}
...
query.where(cb.and(predicates.toArray(Predicate[]::new)));

Maybe also the following helps: Really dynamic JPA CriteriaBuilder



回答2:

Here you have a better way how to update based on optional parameters

public int updateMyEntity(String value1, String value2){

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaUpdate<MyEntity> cu = cb.createCriteriaUpdate(MyEntity.class);
    Root<MyEntity> root = cu.from(MyEntity.class);

    if (value1 != null) {
        cu.where(cb.equal(root.get(MyEntity.value1), value1));
    }

    if (value2 != null) {
        cu.where(cb.equal(root.get(MyEntity.value2), value2));
    }

    // set 
    cu.set(MyEntity_.isAvailable,  true);

    return em.createQuery(cu).executeUpdate();
}