Generic approach of using LIKE, PERCENT and CONCAT in a JPQL query
I'm doing some JPA 2.0 queries inside my JEE6-app, where I use Named Queries with Parameters. My current query is a "find-by-all" query with just one parameter.
@NamedQuery(name = "UserPE.byAny", query = "SELECT b FROM UserPE b WHERE " +
"b.email LIKE CONCAT('%', :PARAM, '%') OR " +
"b.name LIKE CONCAT('%', :PARAM, '%') OR " +
"b.firstName LIKE CONCAT('%', :PARAM, '%')")
This JPQL query works fine with Hibernate 3.5.4-final.
Now I switched to Eclipselink 2.1.0-RC3 (also tried 2.1.1, with no success), which seems to have a problem how i use concat
and like
. I'm getting an exception saying ...unexpected token [CONCAT]...
when trying to use the same query.
The full stacktrace:
Exception [EclipseLink-8025] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing the query [UserPE.byAny: SELECT b FROM UserPE b WHERE b.email LIKE CONCAT('%', :PARAM, '%') OR b.name LIKE CONCAT('%', :PARAM, '%') OR b.firstName LIKE CONCAT('%', :PARAM, '%')], line 1, column 42: unexpected token [CONCAT].
Internal Exception: NoViableAltException(16!=[743:1: likeValue returns [Object node] : (n= literalString | n= inputParameter );])
at org.eclipse.persistence.exceptions.JPQLException.unexpectedToken(JPQLException.java:372)
at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.handleRecognitionException(JPQLParser.java:319)
at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.addError(JPQLParser.java:245)
at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.reportError(JPQLParser.java:362)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.likeValue(JPQLParser.java:8501)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.likeExpression(JPQLParser.java:8356)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.conditionWithNotExpression(JPQLParser.java:7866)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.simpleConditionalExpressionRemainder(JPQLParser.java:7750)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.simpleConditionalExpression(JPQLParser.java:7625)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.conditionalPrimary(JPQLParser.java:7561)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.conditionalFactor(JPQLParser.java:4507)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.conditionalTerm(JPQLParser.java:4397)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.conditionalExpression(JPQLParser.java:4323)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.whereClause(JPQLParser.java:4280)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.selectStatement(JPQLParser.java:371)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.document(JPQLParser.java:276)
at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.parse(JPQLParser.java:133)
at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.buildParseTree(JPQLParser.java:94)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:198)
at org.eclipse.persistence.internal.jpa.JPAQuery.processJPQLQuery(JPAQuery.java:106)
at org.eclipse.persistence.internal.jpa.JPAQuery.prepare(JPAQuery.java:90)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:464)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:430)
at org.eclipse.persistence.internal.sessions.AbstractSession.processJPAQueries(AbstractSession.java:1747)
at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseS开发者_JS百科essionImpl.java:409)
at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:671)
at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:620)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:228)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:368)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getServerSession(EntityManagerFactoryImpl.java:151)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:207)
at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:195)
at de.comap.controller.AbstractDAO.<init>(AbstractDAO.java:22)
at de.comap.controller.GroupDAO.<init>(GroupDAO.java:20)
at de.comap.controller.GroupDAOTest.beforeClass(GroupDAOTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:640)
at mockit.integration.testng.internal.TestNGTestRunnerDecorator.invokeMethod(TestNGTestRunnerDecorator.java:78)
at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java)
at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:503)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:193)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:123)
at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:183)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:115)
at org.testng.TestRunner.runWorkers(TestRunner.java:1009)
at org.testng.TestRunner.privateRun(TestRunner.java:683)
at org.testng.TestRunner.run(TestRunner.java:553)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:311)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:306)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:268)
at org.testng.SuiteRunner.run(SuiteRunner.java:217)
at org.testng.TestNG.runSuite(TestNG.java:1062)
at org.testng.TestNG.runSuitesLocally(TestNG.java:956)
at org.testng.TestNG.run(TestNG.java:874)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:74)
at org.apache.maven.surefire.testng.TestNGXmlTestSuite.execute(TestNGXmlTestSuite.java:92)
at org.apache.maven.surefire.Surefire.run(Surefire.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:350)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1021)
Caused by: NoViableAltException(16!=[743:1: likeValue returns [Object node] : (n= literalString | n= inputParameter );])
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.likeValue(JPQLParser.java:8465)
... 61 more
This is how the query works with EclipseLink:
@NamedQuery(name = UserPE.BY_ANY, query = "SELECT b FROM UserPE b WHERE " +
"b.email LIKE %:PARAM% OR " +
"b.name LIKE %:PARAM% OR " +
"b.firstName LIKE %:PARAM%")
But this query again does not work with Hibernate. Isn't there a generic approach that works with all JPA providers? Or is the spec too fuzzy in this area?
I would really like my queries to be as independent as possible from the JPA implementation.
JPA Specification says:
The pattern_value is a string literal or a string-valued input parameter
So, an ability to use scalar function there is a provider-specific extension.
I think the only portable way to do it is to perform concatenation in Java code before passing a parameter.
Your error says Eclipse Persistence Services - 2.0.0.v20091127, not 2.1,
this should work in 2.1.
The JPQL spec does not allow functions with a LIKE, so this is provider specific.
精彩评论