Hibernate Criteria API 中的 SQL 'LIKE' 运算符

Posted

技术标签:

【中文标题】Hibernate Criteria API 中的 SQL \'LIKE\' 运算符【英文标题】:SQL 'LIKE' operator in Hibernate Criteria APIHibernate Criteria API 中的 SQL 'LIKE' 运算符 【发布时间】:2012-07-04 22:01:03 【问题描述】:

我想用 Hibernate Criteria 实现一些通用过滤器。它应该像 SQL 中的 LIKE 运算符一样工作:

SELECT * FROM table WHERE table.ANYCOLOUMNHERE LIKE '%'||anyvaluehere||'%'

我有Map<String, String>,其中 key 是列名,value 是它的值。

我尝试过这样的事情:

for (Entry<String, String> filter : filters.entrySet()) 
    crit.add(Restrictions.ilike(filter.getKey(), filter.getValue(), MatchMode.ANYWHERE));

但是当字段类型不是String时,会导致java.lang.ClassCastException:

[com.nsn.util.LoggerUtilerror] (http-localhost-127.0.0.1-8080-1) Error while getting alarms: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
    at org.hibernate.type.descriptor.java.LongTypeDescriptor.unwrap(LongTypeDescriptor.java:36) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.type.descriptor.sql.BigIntTypeDescriptor$1.doBind(BigIntTypeDescriptor.java:57) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:92) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:305) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:300) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1891) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1862) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1737) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:828) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2447) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2433) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2263) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2258) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:122) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1535) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:374) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at org.hibernate.internal.CriteriaImpl.uniqueResult(CriteriaImpl.java:396) [hibernate-core-4.1.1.Final.jar:4.1.1.Final]
    at com.nsn.entities_proccess.AlarmDAOImpl.getAlarms(AlarmDAOImpl.java:93) [classes:]
    at com.nsn.boundary_process.LazyAlarmDataModel.load(LazyAlarmDataModel.java:50) [classes:]
    at org.primefaces.component.datatable.DataTable.loadLazyData(DataTable.java:677) [primefaces-3.3.1.jar:]
    at org.primefaces.component.datatable.DataTableRenderer.encodeEnd(DataTableRenderer.java:92) [primefaces-3.3.1.jar:]
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1786) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:518) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.component.UIData.visitTree(UIData.java:1411) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIForm.visitTree(UIForm.java:371) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:376) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:297) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIViewRoot.encodeChildren(UIViewRoot.java:981) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:391) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139) [jsf-impl-2.1.7-jbossorg-2.jar:]
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_05]

有没有办法解决这个问题?

【问题讨论】:

异常信息是什么? 【参考方案1】:

您可以像这样使用like() 限制条件:

session = sessionFactory.openSession();
Criteria query = session.createCriteria(Pojo.class);
query.add(Restrictions.like("anycolumn", "anyvalue", MatchMode.START));

它会给你一个以“anyvalue”开头的字符串列表。

【讨论】:

【参考方案2】:

当休眠会话不存在时,您也可以使用 DetachedCriteria。

DetachedCriteria criteria = DetachedCriteria.forClass(Pojo.class);
criteria.add(Restrictions.like("column", value, MatchMode.ANYWHERE));

它将匹配 column 字符串中任意位置的 value。 您可以使用不同类型的模式。

参考页面:https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/criterion/MatchMode.html

分离标准: - 当休眠会话不存在时,分离标准是非常好的替代。

使用 DetachedCriteria 与 Criteria 完全相同,只是您可以在不访问会话的情况下进行查询的初始创建和设置。在运行查询时,您必须使用getExecutableCriteria(session) 将其转换为可执行查询。

如果您正在构建复杂的查询(可能通过多步骤过程),这很有用,因为您不需要在任何地方访问 Session。运行查询时,您只需要最后一步的 Session。

在底层,DetachedCriteria 使用 CriteriaImpl,它与调用 session.createCriteria() 时获得的类相同。

【讨论】:

MatchMode.ANYWHERE 将匹配列字符串中任何位置的值...但是 DetachedCriteria 有什么作用?【参考方案3】:

使用枚举 MatchMode 来帮助你:

Criterion c1 = Restrictions.like("AttributeName", "AttributeValue", MatchMode.END);

不要使用任何特殊字符,例如 * 参考Read This Like

【讨论】:

【参考方案4】:

在这种情况下,我建议使用Query object 而不是Criteria object

我不记得 Criteria 是否像 Query 对象那样将它不理解的东西直接传递到数据库。基本上,这意味着如果您使用 Hibernate 解析器无法理解的特定于数据库的函数,它将“按原样”传递给数据库。

例子:

Query queryObject = session.createQuery("from ClassName where VARIABLENAME like '%'||anyvaluehere||'%' order by VARIABLENAME");
List<YourClass> resultList= queryObject.list();

更多信息请见here

【讨论】:

你能举个小例子吗?尤其是如何实现排序【参考方案5】:

当字段类型不是String时,会引发java.lang.ClassCastException

那是因为它只适用于字符串/varchar 字段/列。

【讨论】:

那么有没有办法用标准实现 sql 'like' 行为? 是的,有很多不同的方式,但你会如何“喜欢”一个布尔值?还是整数?还是福? 喜欢在 sql 中工作。在这里您可以选择任何不关心列类型的内容。 @Divers 它的oracle特定的,当然不适用于hibernate

以上是关于Hibernate Criteria API 中的 SQL 'LIKE' 运算符的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate Criteria API - 过滤集合属性

Hibernate Criteria API:获取 n 个随机行

Hibernate Criteria Api 是不是完全防止 SQL 注入

(懒惰)使用 Hibernate Criteria API 的 LEFT OUTER JOIN

Criteria API JPA Hibernate:外键上的不同选择

如何使用 JPA Criteria API / Hibernate 按 Case 语句分组