diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index 0aa40184b..29b40af91 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -69,6 +69,7 @@ public class Compatibility { private boolean _checkDatabaseForCascadePersistToDetachedEntity = false; private boolean _overrideContextClassloader = true; private boolean _parseAnnotationsForQueryMode = true; + private boolean _convertPositionalParametersToNamed = false; /** * Whether to require exact identity value types when creating object @@ -330,6 +331,14 @@ public class Compatibility { public boolean getIgnoreDetachedStateFieldForProxySerialization() { return _ignoreDetachedStateFieldForProxySerialization; } + + public boolean getConvertPositionalParametersToNamed() { + return _convertPositionalParametersToNamed; + } + + public void setConvertPositionalParametersToNamed(boolean c) { + _convertPositionalParametersToNamed = c; + } /** * Whether OpenJPA should flush changes before detaching or serializing an diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java index 084df939d..2e1a63142 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java @@ -2038,7 +2038,8 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con * Create a new query metadata instance. */ protected QueryMetaData newQueryMetaData(Class cls, String name) { - QueryMetaData meta = new QueryMetaData(name); + QueryMetaData meta = + new QueryMetaData(name, _conf.getCompatibilityInstance().getConvertPositionalParametersToNamed()); meta.setDefiningType(cls); return meta; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/QueryMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/QueryMetaData.java index 71b9aff91..e6b3e19a7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/QueryMetaData.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/QueryMetaData.java @@ -25,6 +25,8 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.kernel.Query; +import org.apache.openjpa.kernel.QueryLanguages; +import org.apache.openjpa.kernel.jpql.JPQLParser; import org.apache.openjpa.lib.meta.SourceTracker; import org.apache.openjpa.lib.xml.Commentable; @@ -59,12 +61,14 @@ public class QueryMetaData private int _lineNum; private int _colNum; private String _srcName; + private boolean _convertPositionalParametersToNamed; /** * Construct with the given name. */ - protected QueryMetaData(String name) { + protected QueryMetaData(String name, boolean convertPositionalParametersToNamed) { _name = name; + _convertPositionalParametersToNamed = convertPositionalParametersToNamed; } /** @@ -155,6 +159,9 @@ public class QueryMetaData * The full query string, or null if none. */ public void setQueryString(String query) { + if (query != null && _convertPositionalParametersToNamed && JPQLParser.LANG_JPQL.equals(_language)) { + query = query.replaceAll("[\\?]", "\\:_"); + } _query = query; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/SimpleEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/SimpleEntity.java index 3d4c0a459..63e7db122 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/SimpleEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/SimpleEntity.java @@ -44,7 +44,9 @@ import javax.persistence.Table; query="select a from simple a where a.id=:id and a.name=:name"), @NamedQuery(name="FindOne", query="select s from simple s where s.name = ?1"), - @NamedQuery(name="FindAll", query="select s from simple s") + @NamedQuery(name="FindAll", query="select s from simple s"), + @NamedQuery(name="SelectWithPositionalParameterNonOneStart", + query="select a from simple a where a.id=?900 and a.name=?2") }) @NamedNativeQueries( { diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryConvertPositionalParameters.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryConvertPositionalParameters.java new file mode 100644 index 000000000..40ff94bf1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestQueryConvertPositionalParameters.java @@ -0,0 +1,75 @@ +/* + * 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 javax.persistence.EntityManager; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * This test was added for OPENJPA-1999. 'Add' support for allowing positional parameters to start at something other + * than 1 and allow for missing parameters. + */ +public class TestQueryConvertPositionalParameters extends SingleEMFTestCase { + EntityManager _em; + long _id; + String _name; + + @Override + public void setUp() { + super.setUp(SimpleEntity.class, "openjpa.Compatibility", "ConvertPositionalParametersToNamed=true"); + _em = emf.createEntityManager(); + + _em.getTransaction().begin(); + SimpleEntity se = new SimpleEntity(); + _name = "name--" + System.currentTimeMillis(); + se.setName(_name); + _em.persist(se); + _em.getTransaction().commit(); + _id = se.getId(); + _em.clear(); + } + + @Override + public void tearDown() throws Exception { + if (_em.getTransaction().isActive()) { + _em.getTransaction().rollback(); + } + _em.close(); + // TODO Auto-generated method stub + super.tearDown(); + } + + public void testNamedPositionalStartAtNonOne() { + SimpleEntity se = + _em.createNamedQuery("SelectWithPositionalParameterNonOneStart", SimpleEntity.class).setParameter(900, _id) + .setParameter(2, _name).getSingleResult(); + assertNotNull(se); + } + + public void testJPQLPositionalStartAtNonOne() { + int idPos = 7; + int namePos = 908; + SimpleEntity se = + _em.createQuery("Select s FROM simple s where s.id=?" + idPos + " and s.name=?" + namePos, + SimpleEntity.class).setParameter(idPos, _id).setParameter(namePos, _name).getSingleResult(); + assertNotNull(se); + } + +} diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java index 11a00ee75..0af1b0694 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java @@ -1822,8 +1822,8 @@ public class AnnotationPersistenceMetaDataParser continue; } meta = getRepository().addQueryMetaData(_cls, query.name()); - meta.setQueryString(query.query()); meta.setLanguage(JPQLParser.LANG_JPQL); + meta.setQueryString(query.query()); for (QueryHint hint : query.hints()) meta.addHint(hint.name(), hint.value()); LockModeType lmt = processNamedQueryLockModeType(query); @@ -1895,8 +1895,8 @@ public class AnnotationPersistenceMetaDataParser } meta = getRepository().addQueryMetaData(null, query.name()); - meta.setQueryString(query.query()); meta.setLanguage(QueryLanguages.LANG_SQL); + meta.setQueryString(query.query()); Class res = query.resultClass(); if (ImplHelper.isManagedType(getConfiguration(), res)) meta.setCandidateType(res); diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java index 3bc1fad19..99435fa71 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java @@ -113,7 +113,8 @@ public class EntityManagerImpl private EntityManagerFactoryImpl _emf; private Map _plans = new IdentityHashMap(1); protected RuntimeExceptionTranslator _ret = PersistenceExceptions.getRollbackTranslator(this); - + private boolean _convertPositionalParams = false; + public EntityManagerImpl() { // for Externalizable } @@ -129,6 +130,9 @@ public class EntityManagerImpl _emf = factory; _broker = new DelegatingBroker(broker, _ret); _broker.setImplicitBehavior(this, _ret); + + _convertPositionalParams = + factory.getConfiguration().getCompatibilityInstance().getConvertPositionalParametersToNamed(); } /** @@ -977,6 +981,10 @@ public class EntityManagerImpl public OpenJPAQuery createQuery(String language, String query) { assertNotCloseInvoked(); try { + // We need + if (query != null && _convertPositionalParams && JPQLParser.LANG_JPQL.equals(language)) { + query = query.replaceAll("[\\?]", "\\:_"); + } String qid = query; PreparedQuery pq = JPQLParser.LANG_JPQL.equals(language) ? getPreparedQuery(qid) : null; @@ -1017,11 +1025,10 @@ public class EntityManagerImpl _broker.getClassLoader(), true); String qid = meta.getQueryString(); - PreparedQuery pq = JPQLParser.LANG_JPQL.equals(meta.getLanguage()) - ? getPreparedQuery(qid) : null; - org.apache.openjpa.kernel.Query del = (pq == null || !pq.isInitialized()) - ? _broker.newQuery(meta.getLanguage(), meta.getQueryString()) - : _broker.newQuery(pq.getLanguage(), pq); + PreparedQuery pq = JPQLParser.LANG_JPQL.equals(meta.getLanguage()) ? getPreparedQuery(qid) : null; + org.apache.openjpa.kernel.Query del = + (pq == null || !pq.isInitialized()) ? _broker.newQuery(meta.getLanguage(), meta.getQueryString()) + : _broker.newQuery(pq.getLanguage(), pq); if (pq != null) { pq.setInto(del); @@ -1060,7 +1067,8 @@ public class EntityManagerImpl } protected QueryImpl newQueryImpl(org.apache.openjpa.kernel.Query kernelQuery) { - return new QueryImpl(this, _ret, kernelQuery); + return new QueryImpl(this, _ret, kernelQuery, _convertPositionalParams + && !kernelQuery.getLanguage().equals(QueryLanguages.LANG_SQL)); } /** diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java index e2ec97ff7..b53d22001 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java @@ -93,6 +93,7 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { private transient ReentrantLock _lock = null; private HintHandler _hintHandler; private boolean _relaxBindParameterTypeChecking; + final private boolean _convertPositionalParams; /** * Constructor; supply factory exception translator and delegate. @@ -101,19 +102,21 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { * @param ret Exception translator for this query * @param query The underlying "kernel" query. */ - public QueryImpl(EntityManagerImpl em, RuntimeExceptionTranslator ret, org.apache.openjpa.kernel.Query query) { - _em = em; - _query = new DelegatingQuery(query, ret); - _lock = new ReentrantLock(); - } + public QueryImpl(EntityManagerImpl em, RuntimeExceptionTranslator ret, org.apache.openjpa.kernel.Query query, + boolean convertPositionalParams) { + _em = em; + _query = new DelegatingQuery(query, ret); + _lock = new ReentrantLock(); + _convertPositionalParams = convertPositionalParams; + } /** * Constructor; supply factory and delegate. * * @deprecated */ - public QueryImpl(EntityManagerImpl em, org.apache.openjpa.kernel.Query query) { - this(em, null, query); + public QueryImpl(EntityManagerImpl em, org.apache.openjpa.kernel.Query query, boolean convertPositionalParams) { + this(em, null, query, convertPositionalParams); } /** @@ -677,6 +680,10 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { * parameter of the query or if the argument is of incorrect type */ public OpenJPAQuery setParameter(int pos, Object value) { + if (_convertPositionalParams == true) { + return setParameter("_"+String.valueOf(pos), value); + } + _query.assertOpen(); _em.assertNotCloseInvoked(); _query.lock(); @@ -868,6 +875,9 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { * the same parameter position is bound already. */ public Parameter getParameter(int pos, Class type) { + if (_convertPositionalParams == true) { + return getParameter("_"+String.valueOf(pos), type); + } Parameter param = getParameter(pos); if (param.getParameterType().isAssignableFrom(type)) throw new IllegalArgumentException(param + " does not match the requested type " + type); @@ -944,6 +954,9 @@ public class QueryImpl implements OpenJPAQuerySPI, Serializable { * @throws IllegalArgumentException if the parameter with the given position does not exist */ public Parameter getParameter(int pos) { + if(_convertPositionalParams == true){ + return getParameter("_"+String.valueOf(pos)); + } Parameter param = getDeclaredParameters().get(pos); if (param == null) throw new IllegalArgumentException(_loc.get("param-missing-pos", diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java index d4ac347df..fac0f0325 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java @@ -1704,8 +1704,8 @@ public class XMLPersistenceMetaDataParser meta = getRepository().addQueryMetaData(null, name); meta.setDefiningType(_cls); - meta.setQueryString(attrs.getValue("query")); meta.setLanguage(JPQLParser.LANG_JPQL); + meta.setQueryString(attrs.getValue("query")); String lockModeStr = attrs.getValue("lock-mode"); LockModeType lmt = processNamedQueryLockModeType(log, lockModeStr, name); if (lmt != null) { @@ -1808,8 +1808,8 @@ public class XMLPersistenceMetaDataParser meta = getRepository().addQueryMetaData(null, name); meta.setDefiningType(_cls); - meta.setQueryString(attrs.getValue("query")); meta.setLanguage(QueryLanguages.LANG_SQL); + meta.setQueryString(attrs.getValue("query")); String val = attrs.getValue("result-class"); if (val != null) { Class type = classForName(val);