为什么JPA TypedQuery抛出异常:参数值[...]与预期类型[java.lang.Character]不匹配?

时间:2015-10-15 13:27:11

标签: java mysql jpa

我搜索过类似的问题,发现对我这个特别奇怪的问题没有任何帮助。异常消息似乎是可以理解的,但是明智的参考它的原因以及在哪里进行适当的更正仍然胜过我...我喜欢 为什么是期望的类型java.lang .Character ???

我有HashMap<String, Object>名为params,其中包含我需要用来将查询结果过滤到MySQL数据库的所有搜索参数,所以这里是我如何构建查询使用JPA Criteria API。

目的是在所有指定的dbColumns中搜索作为名为search的参数传递的字符串

List<String> dbColumns = Arrays.asList("firstname", "lastname", "username", "email", "mobilenumber", "dateJoined");

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Client> cq = cb.createQuery(Client.class);
Root<Client> entity = cq.from(Client.class);
cq.select(entity);

List<Predicate> predicates = new ArrayList<Predicate>();
if(params.containsKey("search") && StringUtils.isNotBlank(search)){
    String search = String.valueOf(params.get("search"));
    for(String column : dbColumns){
        predicates.add(cb.like(cb.lower(entity.get(column).as(String.class)), "%"+search.toLowerCase()+"%")); 
    }
}

cq.where(predicates.toArray(new Predicate[]{}));
TypedQuery<Client> query = em.createQuery(cq); //<--- Error gets thrown here
return query.getResultList();

但是我一直收到这个错误。 参数值[%tunji%]与此行TypedQuery<Client> query = em.createQuery(cq);

的预期类型[java.lang.Character] 不匹配

请参阅下面的StackTrace片段;

Caused by: java.lang.IllegalArgumentException: Parameter value [%tunji%] did not match expected type [java.lang.Character]
    at org.hibernate.ejb.AbstractQueryImpl.validateParameterBinding(AbstractQueryImpl.java:370) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.ejb.AbstractQueryImpl.registerParameterBinding(AbstractQueryImpl.java:343) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:370) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$1$1.bind(CriteriaQueryCompiler.java:194) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler.compile(CriteriaQueryCompiler.java:247) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:622) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
    at org.jboss.as.jpa.container.AbstractEntityManager.createQuery(AbstractEntityManager.java:96) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]

这实际上让我很困惑,因为我在任何时候都没有提到java.lang.Character。此外,除dateJoined之外的所有MySQL列都是VARCHAR类型,它应映射到java的 String 类型。

dbColumn dateJoined属于MySQL类型DATETIME,因此我使用entity.get(column).as(String.class),以便将日期的String表示与search字符串进行比较。

所以我想知道这个错误可能是什么原因以及如何最好地解决这个问题。任何帮助都将受到高度赞赏。

  

修改

回复@Ish ...这里是Client实体的样子

@Entity
@Table(name="client")
@NamedQueries({
    @NamedQuery(name="Client.findAll", query="SELECT c FROM Client c")
})
public class Client implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(unique=true, nullable=false)
    private int id;

    @Column(length=255)
    private String email;

    @Column(length=50)
    private String firstname;

    @Column(length=50)
    private String lastname;

    @Column(length=20)
    private String mobilenumber;

    @Column(unique=true, length=255)
    private String username;

    @Column(nullable=false, name="datejoined")
    private Date dateJoined;

    //... getters ... and ... setters
}

2 个答案:

答案 0 :(得分:1)

日期无法强制转换为字符串。见this。您对Expression.as()方法的理解不是现实中发生的事情。

答案 1 :(得分:0)

在对各种策略进行了一些很好的实验之后,这就是我最终的工作。

感谢帖子here的引导,让我记住了JPA Tuple接口,它本质上是一个可以返回多个结果类型的对象。所以要执行我的like比较,并且因为Date不能简单地转换为字符串,这里是步骤;

  1. 我将列作为Tuple
  2. 检查The Tuple Object以查看它是否可以从Date
  3. 分配
  4. 如果是,则获取Date-Format表达式并将其传递给like表达式。
  5. 现在,这就是我用以前的代码替换并且工作得非常漂亮;

    List<String> dbColumns = Arrays.asList("firstname", "lastname", "username", "email", "mobilenumber", "dateJoined");
    
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Client> cq = cb.createQuery(Client.class);
    Root<Client> entity = cq.from(Client.class);
    cq.select(entity);
    
    List<Predicate> predicates = new ArrayList<Predicate>();
    if(params.containsKey("search") && StringUtils.isNotBlank(search)){
        String search = String.valueOf(params.get("search"));
        List<Predicate> ORPredicates = new ArrayList<Predicate>();
        for(String column : dbColumns){
            Path<Tuple> tuple = entity.<Tuple>get(column);
            if(tuple.getJavaType().isAssignableFrom(Date.class)){
                Expression<String> dateStringExpr = cb.function("DATE_FORMAT", String.class, entity.get(column), cb.literal("'%d/%m/%Y %r'"));
                ORPredicates.add(cb.like(cb.lower(dateStringExpr), "%"+search.toLowerCase()+"%"));
            }else{
                ORPredicates.add(cb.like(cb.lower(entity.get(column).as(String.class)), "%"+search.toLowerCase()+"%")); 
            }
        }
        predicates.add(cb.or(ORPredicates.toArray(new Predicate[]{})));
    }
    
    cq.where(predicates.toArray(new Predicate[]{}));
    TypedQuery<Client> query = em.createQuery(cq); //<--- Error gets thrown here
    return query.getResultList();
    

    注意 - 值得考虑的事项 -

    1. 我知道,从搜索开始的任何地方开始,我的所有日​​期都会以07/10/2015 10:25:09 PM格式显示,因此我能够知道如何格式化我的like表达式中的比较日期"'%d/%m/%Y %r'"
    2. 这只是日期的一个步骤。大多数其他类型,例如int,long,char ...等......都可以直接转换为String,当我探索更多数据类型时,我肯定会对任何其他无法直接转换为String的类型做同样的事情
    3. 虽然这对我来说非常合适,但我仍然可以对任何对我的策略有任何保留意见的人保持开放态度,我只会将其标记为正确的答案。测试

      如果这有助于你以任何方式...干杯交配!