OPENJPA-975. Based on patch from B.J. Reed and Jody Grassel

git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/1.0.x@760911 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Dick 2009-04-01 15:07:58 +00:00
parent f47b3e3c1c
commit 716cf61d4e
5 changed files with 209 additions and 3 deletions

View File

@ -215,9 +215,12 @@ public class JDBCExpressionFactory
String single, String multi, String esc) {
if (!(v2 instanceof Const))
throw new UserException(_loc.get("const-only", "matches"));
return new MatchesExpression((Val) v1, (Const) v2, single, multi,
esc != null ? esc : _type.getMappingRepository().
getDBDictionary().searchStringEscape);
if (esc == null && _type.getMappingRepository().
getDBDictionary().requiresSearchStringEscapeForLike == true) {
esc = _type.getMappingRepository().
getDBDictionary().searchStringEscape;
}
return new MatchesExpression((Val) v1, (Const) v2, single, multi, esc);
}
public Subquery newSubquery(ClassMetaData candidate, boolean subs,

View File

@ -208,6 +208,7 @@ public class DBDictionary
public boolean requiresAliasForSubselect = false;
public boolean allowsAliasInBulkClause = true;
public boolean supportsMultipleNontransactionalResultSets = true;
public boolean requiresSearchStringEscapeForLike = true;
public String searchStringEscape = "\\";
public boolean requiresCastForMathFunctions = false;
public boolean requiresCastForComparisons = false;

View File

@ -20,8 +20,16 @@ package org.apache.openjpa.persistence.query;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@NamedQueries( {
@NamedQuery(name = "Employee.findByName",
query = "Select e from Employee e where e.name LIKE :name") ,
@NamedQuery(name = "Employee.findByNameEscaped",
query = "Select e from Employee e where e.name LIKE :name ESCAPE '\\'")
})
@Entity
@Table(name="SUBQ_EMPLOYEE")
public class Employee {

View File

@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.query;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestQueryEscapeCharacters
extends SingleEMFTestCase {
public void setUp() {
setUp(Employee.class, CLEAR_TABLES);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Employee e = new Employee();
e.setName("Mike Dick");
e.setEmpId(1);
em.persist(e);
e = new Employee();
e.setName("Mike Jones");
e.setEmpId(2);
em.persist(e);
e = new Employee();
e.setName("Mike Smith");
e.setEmpId(3);
em.persist(e);
e = new Employee();
e.setName("M%ke Smith");
e.setEmpId(4);
em.persist(e);
em.getTransaction().commit();
em.close();
}
public void tearDown() throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.createQuery("Delete from Employee").executeUpdate();
em.getTransaction().commit();
em.close();
super.tearDown();
}
public void testNormalQuery() {
performFind ("Employee.findByName", "%Dick", 1);
}
public void testMultiResultQuery() {
performFind ("Employee.findByName", "Mike%", 3);
}
public void testEscapedQuery() {
performFind ("Employee.findByNameEscaped",
"M\\%%", 1);
}
public void testDoubleEscapedQuery() {
performFind ("Employee.findByName", "\\\\", 0);
}
public void testWrongEscape() {
performFind ("Employee.findByName", "M|%%", 0);
}
public void testDoubleSlashQuery() {
// get the Dictionary and check the alwaysAddSearchString flag
OpenJPAEntityManagerFactorySPI ojpaEmf =
(OpenJPAEntityManagerFactorySPI) emf;
JDBCConfiguration conf = (JDBCConfiguration)ojpaEmf.getConfiguration();
if (conf.getDBDictionaryInstance().
requiresSearchStringEscapeForLike == true) {
return;
}
performFind ("Employee.findByName", "\\", 0);
}
@SuppressWarnings("unchecked")
public void testDifferentEscapeCharacter () {
OpenJPAEntityManagerFactorySPI ojpaEmf =
(OpenJPAEntityManagerFactorySPI) emf;
JDBCConfiguration conf = (JDBCConfiguration)ojpaEmf.getConfiguration();
// Would be nice to just pass a map to the createEntityManager, but
// seems like it would be too much trouble to get the proper DB type
// and then build the string for the map.
conf.getDBDictionaryInstance().requiresSearchStringEscapeForLike = true;
conf.getDBDictionaryInstance().searchStringEscape = "|";
EntityManager em = emf.createEntityManager();
Query q = em.createNamedQuery("Employee.findByName");
q.setParameter("name", "M|%%");
List<Employee> emps = (List<Employee>) q.getResultList();
assertEquals(1, emps.size());
String unnamedQuery =
"Select e from Employee e where e.name LIKE :name";
q = em.createQuery(unnamedQuery);
q.setParameter("name", "M|%%");
emps = (List<Employee>) q.getResultList();
assertEquals(1, emps.size());
em.close();
}
@SuppressWarnings("unchecked")
private void performFind (String namedQuery, String parameter,
int expected) {
EntityManager em = emf.createEntityManager();
Query q = em.createNamedQuery(namedQuery);
q.setParameter("name", parameter);
List<Employee> emps = (List<Employee>) q.getResultList();
assertEquals(expected, emps.size());
String unnamedQuery =
"Select e from Employee e where e.name LIKE :name";
if (namedQuery.equals("Employee.findByNameEscaped")) {
unnamedQuery =
"Select e from Employee e where e.name LIKE :name ESCAPE '\\'";
}
q = em.createQuery(unnamedQuery);
q.setParameter("name", parameter);
emps = (List<Employee>) q.getResultList();
assertEquals(expected, emps.size());
em.close();
}
}

View File

@ -2400,6 +2400,44 @@ sequence value. May use a placeholder of <literal>{0}</literal> for the variable
sequence name. Defaults to a database-appropriate value.
</para>
</listitem>
<listitem id="DBDictionary.RequiresSearchStringEscapeForLike">
<para>
<indexterm>
<primary>
SQL
</primary>
<secondary>
RequiresSearchStringEscapeForLike
</secondary>
</indexterm>
<literal>RequiresSearchStringEscapeForLike</literal>:
When true, the database requires an escape string for queries that use
<literal>LIKE</literal>. The escape string can be specified using
<literal>searchStringEscape</literal>. Defaults to <literal>true</literal>.
</para>
</listitem>
<listitem id="DBDictionary.SearchStringEscape">
<para>
<indexterm>
<primary>
SQL
</primary>
<secondary>
SearchStringEscape
</secondary>
</indexterm>
<literal>SearchStringEscape</literal>:
The default escape character used when generating SQL <literal>LIKE</literal>
clauses. The escape character is used to escape the wildcard meaning of the
<literal>_</literal> and <literal>%</literal> characters.
Note: since JPQL provides the ability to define the escape character in
the query, this setting is primarily used when translating other query
languages, such as JDOQL. To not use any escape character, set the
<literal>RequiresSearchStringEscapeForLike</literal> property to
<literal>false</literal>. Defaults to <literal>"\\"</literal> (a single backslash
in Java speak).
</para>
</listitem>
</itemizedlist>
</section>
<section id="ref_guide_dbsetup_dbsupport_mysql">