Author: rmannibucau Date: Sun Jun 7 01:05:22 2015 New Revision: 1683972 URL: http://svn.apache.org/r1683972 Log: OPENJPA-2592 basic stored procedure impl import
Added: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/StoredProcedureQuery.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/XROP.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/StoredProcedure.java openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryResultCallback.java openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MultiQueryMetaData.java openjpa/branches/openjpa_jpa-2.1/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/BatchedResultObjectProvider.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/TestStoredProcedure.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/Procedures.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/EntityWithStoredProcedure.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/StoredProcedureQueryImpl.java Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AbstractQuery.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataFactory.java openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java Sun Jun 7 01:05:22 2015 @@ -46,6 +46,7 @@ public class DBIdentifier extends Identi INDEX, FOREIGN_KEY, CONSTANT, + PROCEDURE, NULL } @@ -603,6 +604,10 @@ public class DBIdentifier extends Identi return newIdentifier(name,id, toUpper, delimit, false); } + public static DBIdentifier newProcedure(String name) { + return newIdentifier(name, DBIdentifierType.PROCEDURE); + } + /** * Constructs a new identifier (potentially a compound QualifiedDBIdentifier) with the provided * name an type. Optionally, converting the name to upper case and delimiting it. Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java Sun Jun 7 01:05:22 2015 @@ -910,6 +910,9 @@ public class JDBCStoreManager implements if (QueryLanguages.LANG_PREPARED_SQL.equals(language)) { return new PreparedSQLStoreQuery(this); } + if (QueryLanguages.LANG_STORED_PROC.equals(language)) { + return new StoredProcedureQuery(this); + } return null; } Added: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/StoredProcedureQuery.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/StoredProcedureQuery.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/StoredProcedureQuery.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/StoredProcedureQuery.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,212 @@ +/* + * 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.jdbc.kernel; + +import org.apache.openjpa.jdbc.meta.MappingRepository; +import org.apache.openjpa.jdbc.meta.QueryResultMapping; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.jdbc.sql.StoredProcedure; +import org.apache.openjpa.kernel.AbstractStoreQuery; +import org.apache.openjpa.kernel.QueryContext; +import org.apache.openjpa.kernel.QueryOperations; +import org.apache.openjpa.kernel.StoreQuery; +import org.apache.openjpa.lib.rop.ResultObjectProvider; +import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.meta.MultiQueryMetaData; +import org.apache.openjpa.meta.QueryMetaData; +import org.apache.openjpa.util.InternalException; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; + +/** + * Executes a stored procedure. + * + * @author ppoddar + * + */ +@SuppressWarnings("serial") +public class StoredProcedureQuery extends AbstractStoreQuery { + private static final Object[] NO_PARAM = new Object[0]; + + JDBCStore _store; + StoredProcedure _proc; + private MultiQueryMetaData _meta; + + public StoredProcedureQuery(JDBCStore store) { + _store = store; + } + + public int getOperation() { + return QueryOperations.OP_SELECT; + } + + public StoredProcedure getProcedure() { + return _proc; + } + + public DBDictionary getDictionary() { + return _store.getDBDictionary(); + } + + @Override + public boolean setQuery(Object meta) { + if (meta == null || meta instanceof MultiQueryMetaData) { + _meta = (MultiQueryMetaData)meta; + return true; + } else { + throw new InternalException("Unknown " + meta); + } + } + + public Executor newDataStoreExecutor(ClassMetaData meta, boolean subclasses) { + List<QueryResultMapping> mappings = null; + List<Class<?>> classes = null; + if (_meta != null) { + List<QueryMetaData> parts = _meta.getComponents(); + if (parts != null && !parts.isEmpty()) { + mappings = new ArrayList<QueryResultMapping>(); + classes = new ArrayList<Class<?>>(); + MappingRepository repos = _store.getConfiguration().getMappingRepositoryInstance(); + for (QueryMetaData part : parts) { + QueryResultMapping mapping = repos.getQueryResultMapping(ctx.getResultMappingScope(), + part.getResultSetMappingName(), + null, true); + if (mapping != null) { + mappings.add(mapping); + } + if (part.getResultType() != null) { + classes.add(part.getResultType()); + } + } + } + } + return new StoredProcedureQueryExecutor(this, mappings, classes); + } + + public boolean supportsParameterDeclarations() { + return false; + } + + public boolean supportsDataStoreExecution() { + return true; + } + + public boolean requiresCandidateType() { + return false; + } + + public boolean requiresParameterDeclarations() { + return false; + } + + + public class StoredProcedureQueryExecutor extends AbstractExecutor { + private final List<Class<?>> _resultClasses; + private final List<QueryResultMapping> _resultMappings; + + public StoredProcedureQueryExecutor(StoredProcedureQuery q, List<QueryResultMapping> resultMapping, + List<Class<?>> classes) { + QueryContext ctx = q.getContext(); + _resultMappings = resultMapping; + _resultClasses = classes; + // Look for the named Stored Procedure in the database + String procName = ctx.getQueryString(); + _proc = getStoredProcedure(_store.getConnection(), _store.getDBDictionary(), procName); + if (_proc == null) { + throw new RuntimeException("Can not find stored procedure " + procName); + } + } + + StoredProcedure getStoredProcedure(Connection conn, DBDictionary dict, String procedureName) { + try { + StoredProcedure sp = dict.getStoredProcedure(conn.getMetaData(), null, null, procedureName); + if (sp != null) { + return sp; + } + } catch (SQLException ex) { + throw new RuntimeException(ex); + } finally { + try { + conn.close(); + } catch (SQLException ex) { + + } + } + throw new RuntimeException("Procedure [" + procedureName + "] not found"); + } + + @Override + public ResultObjectProvider executeQuery(StoreQuery q, Object[] params, Range range) { + try { + DBDictionary dict = _store.getDBDictionary(); + Connection conn = _store.getConnection(); + CallableStatement stmnt = conn.prepareCall(_proc.getCallSQL()); + + final StoredProcedureQuery spq = StoredProcedureQuery.class.cast(q); + for (Column c : spq.getProcedure().getInColumns()) { + dict.setUnknown(stmnt, c.getIndex() + 1, params[c.getIndex()], c); + } + for (Column c : spq.getProcedure().getInOutColumns()) { + final int index = c.getIndex() + 1; + stmnt.registerOutParameter(index, c.getType()); + dict.setUnknown(stmnt, index, params[index - 1], c); + } + for (Column c : spq.getProcedure().getOutColumns()) { + stmnt.registerOutParameter(c.getIndex() + 1, c.getType()); + } + + JDBCFetchConfiguration fetch = (JDBCFetchConfiguration)q.getContext().getFetchConfiguration(); + ResultObjectProvider rop = new XROP(_resultMappings, _resultClasses, _store, fetch, stmnt); + rop.open(); + return rop; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Object[] toParameterArray(StoreQuery q, Map<?, ?> userParams) { + if (userParams == null) return NO_PARAM; + Object[] array = new Object[userParams.size()]; + int i = 0; + StoredProcedureQuery storedProcedureQuery = StoredProcedureQuery.class.cast(q); + for (final Column[] columns : asList( + storedProcedureQuery.getProcedure().getInColumns(), + storedProcedureQuery.getProcedure().getInOutColumns())) { + for (Column c : columns) { + array[i] = userParams.get(c.getIdentifier().getName()); + if (array[i++] == null) { + userParams.get(c.getIndex()); + } + } + } + return array; + } + + } +} Added: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/XROP.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/XROP.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/XROP.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/XROP.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,205 @@ +/* + * 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.jdbc.kernel; + + +import org.apache.openjpa.jdbc.meta.QueryResultMapping; +import org.apache.openjpa.jdbc.sql.ResultSetResult; +import org.apache.openjpa.lib.rop.BatchedResultObjectProvider; +import org.apache.openjpa.lib.rop.ResultObjectProvider; +import org.apache.openjpa.util.InternalException; + +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * Gets multiple Result Object Providers each with different mapping. + * + * @author Pinaki Poddar + * + */ +public class XROP implements BatchedResultObjectProvider { + private final CallableStatement stmt; + private final JDBCFetchConfiguration fetch; + private final List<QueryResultMapping> _multi; + private final List<Class<?>> _resultClasses; + private int index; + private final JDBCStore store; + // Result of first execution + private boolean executionResult; + + public XROP(List<QueryResultMapping> mappings, List<Class<?>> classes, + JDBCStore store, JDBCFetchConfiguration fetch, + CallableStatement stmt) { + _multi = mappings; + _resultClasses = classes; + this.stmt = stmt; + this.fetch = fetch; + this.store = store; + } + + /** + * Does not support random access. + */ + @Override + public boolean supportsRandomAccess() { + return false; + } + + /** + * Opens this provider by executing the underlying Statment. + * The result of execution is memorized. + */ + @Override + public void open() throws Exception { + executionResult = stmt.execute(); + } + + /** + * Gets the current result set, wraps it with a {@link ResultSetResult}, then wraps + * again with appropriate ROP based on the result set mapping. + * <br> + * The ResultSet and the associated connection must not be closed. + * + * @return a provider or null if the underlying statement has no more results. + */ + @Override + public ResultObjectProvider getResultObject() throws Exception { + ResultSet rs = stmt.getResultSet(); + if (rs == null) + return null; + + ResultSetResult res = new ResultSetResult(rs, store.getDBDictionary()); + res.setCloseConnection(false); + res.setCloseStatement(false); + try { + if (_resultClasses != null && _resultClasses.size() > index) { + Class<?> mapping = _resultClasses.get(index); + if (mapping != null) { + return new GenericResultObjectProvider(mapping, store, fetch, res); + } + } + if (_multi != null && _multi.size() > index) { + QueryResultMapping mapping = _multi.get(index); + if (mapping != null) { + return new MappedQueryResultObjectProvider(mapping, store, fetch, res); + } + } + return new SQLProjectionResultObjectProvider(store, fetch, res, null); + } finally { + index++; + } + } + + + /** + * Closes the underlying statement. + */ + @Override + public void close() throws Exception { + stmt.close(); + } + + /** + * Affirms if more result sets are available. + * <br> + * <b.NOTE</b>: The side effect is to advance to the statement's next result. + */ + public boolean hasMoreResults() { + try { + return stmt.getMoreResults(); + } catch (SQLException e) { + return false; + } + } + + public boolean getExecutionResult() { + return executionResult; + } + + /** + * Gets the update count, provided the current result of the statement is not a result set. + */ + @Override + public int getUpdateCount() { + try { + return stmt.getUpdateCount(); + } catch (SQLException e) { + return -1; + } + } + + @Override + public Object getOut(String name) { + try { + return stmt.getObject(name); + } catch (SQLException e) { + return null; + } + } + + @Override + public Object getOut(int position) { + try { + return stmt.getObject(position); + } catch (SQLException e) { + return null; + } + } + + /** + * Throws exception. + */ + @Override + public boolean next() throws Exception { + throw new InternalException(); + } + + /** + * Returns false. + */ + @Override + public boolean absolute(int pos) throws Exception { + return false; + } + + /** + * Returns {@code -1}. + */ + @Override + public int size() throws Exception { + return -1; + } + + /** + * Throws exception. + */ + @Override + public void reset() throws Exception { + throw new InternalException(); + } + + @Override + public void handleCheckedException(Exception e) { + throw new RuntimeException(e); + } + +} Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java Sun Jun 7 01:05:22 2015 @@ -210,7 +210,7 @@ public class MappingRepository extends M * Return the query result mapping for the given name. */ public QueryResultMapping getQueryResultMapping(Class<?> cls, String name, ClassLoader loader, boolean mustExist) { - QueryResultMapping res = null; + QueryResultMapping res; if (_locking) { synchronized (this) { res = getQueryResultMappingInternal(cls, name, loader); @@ -232,13 +232,13 @@ public class MappingRepository extends M // check cache Object key = getQueryResultKey(cls, name); - QueryResultMapping res = (QueryResultMapping) _results.get(key); + QueryResultMapping res = _results.get(key); if (res != null) return res; // get metadata for class, which will find results in metadata file if (cls != null && getMetaData(cls, envLoader, false) != null) { - res = (QueryResultMapping) _results.get(key); + res = _results.get(key); if (res != null) return res; } @@ -250,7 +250,7 @@ public class MappingRepository extends M .getResultSetMappingScope(name, envLoader); // not in cache; load getMetaDataFactory().load(cls, MODE_META | MODE_MAPPING, envLoader); - return (QueryResultMapping) _results.get(key); + return _results.get(key); } /** Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/QueryResultMapping.java Sun Jun 7 01:05:22 2015 @@ -262,6 +262,7 @@ public class QueryResultMapping private Map<List<MetaDataContext>, ColumnMap> _mappings = null; private Map<List<MetaDataContext>, FetchInfo> _eager = null; private FetchInfo _fetchInfo = null; // for top-level + private Collection<String> _constructorParams = null; /** * Supply candidate type on construction. @@ -546,6 +547,13 @@ public class QueryResultMapping info.excludes.clear(fm.getIndex()); } } + + public void addConstructorParam(final String name) { + if (_constructorParams == null) { + _constructorParams = new ArrayList<String>(); + } + _constructorParams.add(name); + } } /** Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java Sun Jun 7 01:05:22 2015 @@ -62,6 +62,9 @@ public class Column private DBIdentifier _typeName = DBIdentifier.NULL; private int _javaType = JavaTypes.OBJECT; private int _size = 0; + private int _precision = -1; + private int _scale = -1; + private int _radix = 10; private int _decimals = 0; private String _defaultStr = null; private Object _default = null; @@ -377,6 +380,29 @@ public class Column _decimals = digits; } + public int getPrecision() { + return _precision; + } + + public void setPrecision(int p) { + _precision = p; + } + + public int getScale() { + return _scale; + } + + public void setScale(int s) { + _scale = s; + } + public int getRadix() { + return _radix; + } + + public void setRadix(int r) { + _radix = r; + } + /** * Return the default value set for the column, if any. */ @@ -492,6 +518,22 @@ public class Column } /** + * Sets nullability of this receiver by the given flag. + * @param flag one of the JDBC nullability flag namely + * <LI> {@link DatabaseMetaData#columnNullableUnknown} : not known if the column can be set to null value + * <LI> {@link DatabaseMetaData#columnNullable} : the column can be set to null value + * <LI> {@link DatabaseMetaData#columnNoNulls} : the column can not be set to null value + */ + public void setNullability(short flag) { + switch (flag) { + case DatabaseMetaData.columnNullableUnknown : _notNull = null; break; + case DatabaseMetaData.columnNullable : _notNull = false; break; + case DatabaseMetaData.columnNoNulls : _notNull = true; break; + + } + } + + /** * Whether this column is auto-assigned a value on insert. */ public boolean isAutoAssigned() { @@ -610,7 +652,7 @@ public class Column /** * Set the column's 0-based index in the owning table. */ - void setIndex(int index) { + public void setIndex(int index) { _index = index; } Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Sun Jun 7 01:05:22 2015 @@ -60,6 +60,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import javax.sql.DataSource; @@ -4702,6 +4703,47 @@ public class DBDictionary return toDBName(namingUtil.getGeneratedKeySequenceName(col, maxAutoAssignNameLength)); } + protected Map<String, StoredProcedure> _procs = new TreeMap<String, StoredProcedure>(); + + /** + * Gets the metadata of the stored procedure by the given name either from the cached version or + * by enquiring the database. + * @param meta the database meta data + * @param catalog the catalog name or null + * @param schema the schema name or null + * @param procedure the procedure name + * @return metadata about the named procedure or null + * @throws SQLException when metadata query goes wrong + */ + public StoredProcedure getStoredProcedure(DatabaseMetaData meta, DBIdentifier catalog, DBIdentifier schema, + String procedure) throws SQLException { + if (_procs.containsKey(procedure)) { + return _procs.get(procedure); + } + ResultSet rs = meta.getProcedureColumns( + getCatalogNameForMetadata(catalog), + getSchemaNameForMetadata(schema), + procedure, + null); + StoredProcedure sp = null; + if (rs.next()) { + sp = new StoredProcedure(rs); + } else { + ResultSet rsExist = meta.getProcedures( + getCatalogNameForMetadata(catalog), + getSchemaNameForMetadata(schema), + procedure); + if (rsExist.next()) { + sp = new StoredProcedure((String) null); + sp.setCatalog(catalog); + sp.setSchema(schema); + sp.setName(procedure); + } + } + _procs.put(procedure, sp); + return sp; + } + /////////////////////////////// // Configurable implementation /////////////////////////////// Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java Sun Jun 7 01:05:22 2015 @@ -59,6 +59,7 @@ public class ResultSetResult private final Statement _stmnt; private final ResultSet _rs; private final DBDictionary _dict; + private boolean _closeStatement = true; private boolean _closeConn = true; private int _row = -1; private int _size = -1; @@ -178,13 +179,17 @@ public class ResultSetResult _closeConn = closeConn; } + public void setCloseStatement(boolean closeStatement) { + _closeStatement = closeStatement; + } + public void close() { super.close(); try { _rs.close(); } catch (SQLException se) { } - if (_stmnt != null) + if (_stmnt != null && _closeStatement) try { _stmnt.close(); } catch (SQLException se) { Added: openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/StoredProcedure.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/StoredProcedure.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/StoredProcedure.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/StoredProcedure.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,338 @@ +/* + * 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.jdbc.sql; + +import org.apache.openjpa.jdbc.identifier.DBIdentifier; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.Schemas; +import org.apache.openjpa.meta.MultiQueryMetaData; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Holds metadata about a Database Stored Procedure. + * This is different than {@link MultiQueryMetaData} which holds the metadata + * about what the user has specified. + * <br> + * An instance of this class can be constructed either by reading from database meta data + * or by programatic assignment. If an instance if created programmatically, then + * its SQL body or parameters can be added. + * <br> + * This class can generate the SQL statement to create, drop or delete this procedure. + * + * + * @author Pinaki Poddar + * + */ +public class StoredProcedure { + private DBIdentifier _catalog; + private DBIdentifier _schema; + private DBIdentifier _name; + private List<Column> _cols = new ArrayList<Column>(); + private List<String> _params = new ArrayList<String>(); + + + private List<String> _sql = new ArrayList<String>(); + private final boolean _fromDatabase; + + /** + * An enumeration on type of parameter for a Stored Procedure. + * The enumerted values has the same ordinal numbers as found + * in corresponding integer values in {@link DatabaseMetaData}. + */ + public enum PARAM {UNKNOW, IN, INOUT, RESULT, OUT, RETURN} + + public enum SQL {NONE,MODIFY,READ, CONTAINS}; + + /** + * Create a procedure of the given name. + */ + public StoredProcedure(String name) { + _name = DBIdentifier.newProcedure(name); + _fromDatabase = false; + } + + /** + * <pre> + * + * 1. PROCEDURE_CAT - String - the procedure catalog name + * 2. PROCEDURE_SCHEM - String - the procedure schema name (possibly null) + * 3. PROCEDURE_NAME - String - the procedure name + * 4. COLUMN_NAME - String - the name of the column + * 5. COLUMN_TYPE - short - the kind of column or parameter, as follows: + * DatabaseMetaData.procedureColumnUnknown - type unknown + * DatabaseMetaData.procedureColumnIn - an IN parameter + * DatabaseMetaData.procedureColumnInOut - an INOUT parameter + * DatabaseMetaData.procedureColumnOut - an OUT parameter + * DatabaseMetaData.procedureColumnReturn - a return value + * DatabaseMetaData.procedureReturnsResult - a result column in a result set + * 6. DATA_TYPE - int - the SQL type of the data, as in java.sql.Types + * 7. TYPE_NAME - String - the SQL type name, for a UDT it is fully qualified + * 8. PRECISION - int - the precision + * 9. LENGTH - int - the length of the data in bytes + * 10.SCALE - short - the scale for numeric types + * 11.RADIX - short - the Radix for numeric data (typically 2 or 10) + * 12.NULLABLE - short - can the data contain null: + * DatabaseMetaData.procedureNoNulls - NULLs not permitted + * DatabaseMetaData.procedureNullable - NULLs are permitted + * DatabaseMetaData.procedureNullableUnknown - NULL status unknown + * 13.REMARKS - String - an explanatory comment about the data item + * </pre> + **/ + + public StoredProcedure(ResultSet rs) throws SQLException { + _fromDatabase = true; + int i = 0; + do { + if (i == 0) { + // get stored procedure metadata + _catalog = DBIdentifier.newCatalog(rs.getString(1)); + _schema = DBIdentifier.newSchema(rs.getString(2)); + _name = DBIdentifier.newIdentifier(rs.getString(3), DBIdentifier.DBIdentifierType.PROCEDURE, false); + } + Column col = new Column(); + _cols.add(col); + col.setIdentifier(DBIdentifier.newColumn(rs.getString(4))); + col.setFlag(rs.getShort(5), true); + col.setType(rs.getInt(6)); + col.setTypeIdentifier(DBIdentifier.newConstant(rs.getString(7))); + col.setPrecision(rs.getInt(8)); + col.setSize(rs.getInt(9)); + col.setScale(rs.getInt(10)); + col.setRadix(rs.getShort(11)); + col.setNullability(rs.getShort(12)); + col.setComment(rs.getString(13)); + col.setIndex(i); + _params.add(col.getIdentifier().getName() + " " + col.getTypeIdentifier().getName()); + i++; + } while (rs.next()); + } + + public void setCatalog(DBIdentifier catalog) { + this._catalog = catalog; + } + + public void setSchema(DBIdentifier schema) { + this._schema = schema; + } + + public void setName(String name) { + this._name = DBIdentifier.newIdentifier(name, DBIdentifier.DBIdentifierType.PROCEDURE, false); + } + + public Column[] getInColumns() { + return getColumns((short)DatabaseMetaData.procedureColumnIn); + } + + public Column[] getInOutColumns() { + return getColumns((short)DatabaseMetaData.procedureColumnInOut); + } + + public Column[] getOutColumns() { + return getColumns((short)DatabaseMetaData.procedureColumnOut); + } + + public Column[] getReturnColumns() { + return getColumns((short)DatabaseMetaData.procedureColumnReturn); + } + + public Column[] getResultColumns() { + return getColumns((short)DatabaseMetaData.procedureColumnResult); + } + + public Column[] getColumns() { + return _cols.toArray(new Column[_cols.size()]); + } + + /** + * Counts the number of columns with the given flag. + * @param flag + * @return + */ + int countColumn(short flag) { + int count = 0; + for (Column col : _cols) { + if (col.getFlag(flag)) count++; + } + return count; + } + + Column[] getColumns(short flag) { // TODO: cache? + List<Column> cols = null; + for (Column col : _cols) { + if (col.getFlag(flag)) { + if (cols == null) cols = new ArrayList<Column>(); + cols.add(col); + } + } + return cols == null ? Schemas.EMPTY_COLUMNS : cols.toArray(new Column[cols.size()]); + } + + + /** + * Gets the name of this procedure. + */ + public String getName() { + return _name.getName(); + } + + /** + * Adds an {@code IN} parameter of the given name and type. + * @param var name of the variable + * @param typeName name of the SQL type e.g. {@code VARCAR(32)} + * @return this procedure instance + */ + public StoredProcedure addParameter(String var, String typeName) { + return addParameter(PARAM.IN, var, typeName); + } + /** + * Adds the given parameter declaration. + * + * @param param type of parameter. + * @param var name of the variable + * @param typeName name of the SQL type e.g. {@code VARCAR(32)} + * @return this procedure instance + */ + public StoredProcedure addParameter(PARAM param, String var, String typeName) { + assertMutable(); + + _params.add(param + " " + var + " " + typeName); + return this; + } + + public StoredProcedure setLanguage(String language) { + _sql.add("LANGUAGE " + language); + return this; + } + + /** + * Gets the SQL for creating this procedure. + */ + public String getCreateSQL() { + StringBuilder buf = new StringBuilder(); + buf.append("CREATE PROCEDURE "); + buf.append(_name); + buf.append(" ("); + for (Iterator<String> p = _params.iterator(); p.hasNext();) { + buf.append(p.next()); + buf.append(p.hasNext() ? "," : ""); + } + buf.append(") "); + //buf.append("("); + for (String s : _sql) buf.append(s).append(" "); + //buf.append(")"); + + return buf.toString().trim(); + } + + /** + * Gets the SQL for dropping this procedure. + */ + public String getDropSQL() { + return "DROP PROCEDURE " + _name; + } + + /** + * Gets the SQL for calling this procedure. + */ + public String getCallSQL() { + StringBuilder buf = new StringBuilder(); + buf.append("CALL "); + buf.append(_name); buf.append(" ("); + for (Iterator<String> p = _params.iterator(); p.hasNext();) { + p.next(); + buf.append("?"); + if (p.hasNext()) buf.append(","); + } + buf.append(")"); + return buf.toString().trim(); + } + + /** + * Adds a read SQL statement via an external method. + */ + public StoredProcedure setSQL(SQL sql) { + switch (sql) { + case CONTAINS : _sql.add("CONTAINS SQL"); break; + case NONE : _sql.add("NO SQL"); break; + case MODIFY : _sql.add("MODIFIES SQL DATA"); break; + case READ : _sql.add("READS SQL DATA"); break; + } + return this; + } + + /** + * Sets the language whose parameter passing convention will be used to pass paramater values. + * @param lang + * @return + */ + public StoredProcedure setParameterStyle(String lang) { + _sql.add("PARAMETER STYLE " + lang); + return this; + } + + public StoredProcedure setExternalName(Class<?> cls, String method, Class<?>... paramTypes) { + assertStaticMethod(cls, method, paramTypes); + _sql.add("EXTERNAL NAME '" + cls.getName() + '.' + method + "'"); + return this; + } + + public StoredProcedure setResult(int i) { + return setResult(i, false); + } + + public StoredProcedure setResult(int i, boolean dynamic) { + assertMutable(); + _sql.add((dynamic ? "DYNAMIC " : "") + "RESULT SETS " + i); + return this; + } + + private void assertStaticMethod(Class<?> cls, String method, Class<?>...paramTypes) { + try { + Method m = cls.getMethod(method, paramTypes); + if (m == null || !Modifier.isStatic(m.getModifiers())) { + throw new RuntimeException("No static method " + method + " with arguments " + + Arrays.toString(paramTypes) + " in " + cls); + } + } catch (Exception ex) { + throw new RuntimeException("No static method " + method + " with arguments " + + Arrays.toString(paramTypes) + " in " + cls, ex); + } + } + + private void assertMutable() { + if (_fromDatabase) { + throw new IllegalStateException(this + " is not mutable"); + } + } + + public String toString() { + return getName(); + } +} Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java Sun Jun 7 01:05:22 2015 @@ -45,6 +45,7 @@ import org.apache.openjpa.kernel.exps.Pa import org.apache.openjpa.kernel.exps.QueryExpressions; import org.apache.openjpa.kernel.exps.Val; import org.apache.openjpa.lib.log.Log; +import org.apache.openjpa.lib.rop.BatchedResultObjectProvider; import org.apache.openjpa.lib.rop.EagerResultList; import org.apache.openjpa.lib.rop.ListResultList; import org.apache.openjpa.lib.rop.MergedResultObjectProvider; @@ -1226,6 +1227,9 @@ public class QueryImpl protected Object toResult(StoreQuery q, StoreQuery.Executor ex, ResultObjectProvider rop, StoreQuery.Range range) throws Exception { + if (rop instanceof BatchedResultObjectProvider) { + return new QueryResultCallback(this, q, ex, (BatchedResultObjectProvider) rop, range); + } // pack projections if necessary String[] aliases = ex.getProjectionAliases(q); if (!ex.isPacking(q)) { Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java Sun Jun 7 01:05:22 2015 @@ -34,6 +34,7 @@ import org.apache.openjpa.util.InternalE public class QueryLanguages { public static final String LANG_SQL = "openjpa.SQL"; + public static final String LANG_STORED_PROC = "openjpa.StoredProcedure.SQL"; public static final String LANG_PREPARED_SQL = "openjpa.prepared.SQL"; public static final String LANG_METHODQL = "openjpa.MethodQL"; Added: openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryResultCallback.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryResultCallback.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryResultCallback.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryResultCallback.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,71 @@ +/* + * 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.kernel; + +import org.apache.openjpa.kernel.StoreQuery.Executor; +import org.apache.openjpa.kernel.StoreQuery.Range; +import org.apache.openjpa.lib.rop.BatchedResultObjectProvider; + +/** + * A callabck is used when a query results in multiple non-identical result sets. + * Designed to use with Stored Procedure Query. + * + * @author ppoddar + */ +public class QueryResultCallback { + private final StoreQuery storeQuery; + private final StoreQuery.Executor executor; + private final BatchedResultObjectProvider parent; + private final StoreQuery.Range range; + private final QueryImpl kernel; + + public QueryResultCallback(QueryImpl kernel, StoreQuery storeQuery, Executor executor, + BatchedResultObjectProvider parent, Range range) { + super(); + this.kernel = kernel; + this.storeQuery = storeQuery; + this.executor = executor; + this.parent = parent; + this.range = range; + } + + public Object callback() throws Exception { + return kernel.toResult(storeQuery, executor, parent.getResultObject(), range); + } + + public boolean hasMoreResults() { + return parent.hasMoreResults(); + } + + public boolean getExecutionResult() { + return parent.getExecutionResult(); + } + + public int getUpdateCount() { + return parent.getUpdateCount(); + } + + public Object getOut(int position) { + return parent.getOut(position); + } + + public Object getOut(String name) { + return parent.getOut(name); + } +} Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java Sun Jun 7 01:05:22 2015 @@ -2002,7 +2002,7 @@ public class MetaDataRepository implemen return null; // check cache - QueryMetaData qm = (QueryMetaData) _queries.get(name); + QueryMetaData qm = _queries.get(name); if (qm != null) return qm; @@ -2030,10 +2030,10 @@ public class MetaDataRepository implemen public QueryMetaData[] getQueryMetaDatas() { if (_locking) { synchronized (this) { - return (QueryMetaData[]) _queries.values().toArray(new QueryMetaData[_queries.size()]); + return _queries.values().toArray(new QueryMetaData[_queries.size()]); } } else { - return (QueryMetaData[]) _queries.values().toArray(new QueryMetaData[_queries.size()]); + return _queries.values().toArray(new QueryMetaData[_queries.size()]); } } @@ -2072,10 +2072,22 @@ public class MetaDataRepository implemen } } + public QueryMetaData addQueryMetaData(QueryMetaData meta) { + if (_locking) { + synchronized (this) { + final QueryMetaData queryMetaData = _queries.get(meta.getName()); + return queryMetaData != null ? queryMetaData : _queries.put(meta.getName(), meta); + } + } else { + final QueryMetaData queryMetaData = _queries.get(meta.getName()); + return queryMetaData != null ? queryMetaData : _queries.put(meta.getName(), meta); + } + } + /** * Create a new query metadata instance. */ - protected QueryMetaData newQueryMetaData(Class<?> cls, String name) { + public QueryMetaData newQueryMetaData(Class<?> cls, String name) { QueryMetaData meta = new QueryMetaData(name, _conf.getCompatibilityInstance().getConvertPositionalParametersToNamed()); meta.setDefiningType(cls); @@ -2118,7 +2130,7 @@ public class MetaDataRepository implemen * Searches all cached query metadata by name. */ public QueryMetaData searchQueryMetaDataByName(String name) { - return (QueryMetaData) _queries.get(name); + return _queries.get(name); } /** Added: openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MultiQueryMetaData.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MultiQueryMetaData.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MultiQueryMetaData.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MultiQueryMetaData.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,189 @@ +/* + * 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.meta; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.openjpa.kernel.QueryLanguages; + + + +/** + * Extends {@link QueryMetaData} to allow multiple {@link QueryMetaData#getResultType() result class} or + * {@link QueryMetaData#getResultSetMappingName() mapping names}. + * <br> + * Designed for mapping the results from a Stored Procudure that can produce more than one {@link java.sql.ResultSet}, + * each being mapped with a different mapping specification. + * + * + * @author Pinaki Poddar + * + */ +@SuppressWarnings("serial") +public class MultiQueryMetaData extends QueryMetaData { + private final String _procedureName; + private final boolean _isTemporary; + private final List<QueryMetaData> _parts = new ArrayList<QueryMetaData>(); + private final List<Parameter> _params = new ArrayList<MultiQueryMetaData.Parameter>(); + + /** + * Create this meta data given a scope of definition, a logical identifier, a procedure name + * and whether its usage is temporary. + * @param scope defining scope + * @param logicalName name as an identifier + * @param procedureName name of the database procedure + */ + public MultiQueryMetaData(Class<?> scope, String logicalName, String procedureName, boolean isTemporary) { + super(logicalName, false); + setLanguage(QueryLanguages.LANG_STORED_PROC); + setSource(null, scope, -1, null); + _procedureName = procedureName; + _isTemporary = isTemporary; + } + + public String getProcedureName() { + return _procedureName; + } + + public List<QueryMetaData> getComponents() { + return _parts; + } + + /** + * Affirms if this metadata is ephimeral. + * Ephimenral metadata is removed from the repository after usage. + * @return + */ + public boolean isEphimeral() { + return _isTemporary; + } + + @Override + public void setResultSetMappingName(String name) { + throw new UnsupportedOperationException("Not allowed to set mapping name. It is automatically set"); + } + + @Override + public void setResultType(Class cls) { + throw new UnsupportedOperationException("Not allowed to set result type. It is automatically set"); + } + + public void addComponent(Class<?> resultClass) { + QueryMetaData part = newQueryMetaData(); + part.setResultType(resultClass); + _parts.add(part); + } + + public void addComponent(String mappingName) { + QueryMetaData part = newQueryMetaData(); + part.setResultSetMappingName(mappingName); + _parts.add(part); + } + + private QueryMetaData newQueryMetaData() { + QueryMetaData part = new QueryMetaData(getName() + "#" + _parts.size(), false); + part.setLanguage(getLanguage()); + part.setSource(null, getDefiningType(), -1, null); + return part; + } + + + /** + * Gets the component metadata at the given part index. + * @param i a valid integer index + * @return + */ + public QueryMetaData getComponent(int i) { + if (i < 0 || i >= _parts.size()) { + throw new ArrayIndexOutOfBoundsException("Invalid index " + i + + ". Available " + _parts.size() + " parts"); + } + return _parts.get(i); + } + + /** + * Gets the number of component metadata contained in this metada. + */ + public int getComponentCount() { + return _parts.size(); + } + + /** + * Registers the given parameter. + * @param p + */ + public void registerParameter(Parameter p) { + _params.add(p); + } + + public List<Parameter> getParameters() { + return _params; + } + + public int getParameterCount() { + return _params.size(); + } + + + /** + * A parameter + * + */ + public static class Parameter { + public enum Mode {IN,OUT,INOUT,CURSOR}; + private final String name; + private final Class<?> type; + private final Mode mode; + private final int position; + + public Parameter(String name, Class<?> type, Mode mode) { + this.name = name; + this.type = type; + this.mode = mode; + this.position = -1; + } + + public Parameter(int position, Class<?> type, Mode mode) { + this.name = null; + this.type = type; + this.mode = mode; + this.position = position; + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } + + public Class<?> getType() { + return type; + } + + public Mode getMode() { + return mode; + } + } + + + +} Added: openjpa/branches/openjpa_jpa-2.1/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/BatchedResultObjectProvider.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/BatchedResultObjectProvider.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/BatchedResultObjectProvider.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/BatchedResultObjectProvider.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,64 @@ +/* + * 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.lib.rop; + + +/** + * A provider for multiple result sets. + * This provider acts as a container of other result providers. The underlying providers + * contain the actual data and are {@link ResultObjectProvider#getResultObject() iterated} + * for the results, while this provider iterates over its underlying providers. + * <br> + * Designed for the specific purpose of getting results from the execution of Stored Procedures + * that can produce more than one result set and an optional update count. Few methods related + * to iterating multiple results and update count mirror the methods in JDBC {@link java.sql.Statement}. + * <br> + * + * @see org.apache.openjpa.kernel.QueryResultCallback + * + * @author Pinaki Poddar + * + */ +public interface BatchedResultObjectProvider extends ResultObjectProvider { + /** + * Gets the next result object provider from its batch. + */ + ResultObjectProvider getResultObject() throws Exception; + + /** + * Affirms if this batch contains more results. + */ + boolean hasMoreResults(); + + /** + * Gets the result of executing the underlying JDBC statement. + * @return a boolean value whose semantics is same as {@link java.sql.PreparedStatement#execute()}. + */ + boolean getExecutionResult(); + + + /** + * Gets the count of records updated by the underlying JDBC statement. + * @return an integer value whose semantics is same as {@link java.sql.CallableStatement#getUpdateCount()}. + */ + int getUpdateCount(); + + Object getOut(String name); + Object getOut(int position); +} Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java Sun Jun 7 01:05:22 2015 @@ -34,6 +34,7 @@ import javax.persistence.AttributeOverri import javax.persistence.AttributeOverrides; import javax.persistence.CollectionTable; import javax.persistence.ColumnResult; +import javax.persistence.ConstructorResult; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.EntityResult; @@ -620,6 +621,9 @@ public class AnnotationPersistenceMappin entityResult.addMapping(field.name(), sColName.getName()); } } + for (ConstructorResult constructorResult : anno.classes()) { + throw new UnsupportedOperationException("JPA 2.1, not yet impl"); + } for (ColumnResult column : anno.columns()) { DBIdentifier sName = DBIdentifier.newColumn(column.name(), delimit()); result.addColumnResult(sName.getName()); @@ -1577,9 +1581,9 @@ public class AnnotationPersistenceMappin } if (xmlRootElementClass != null && StringUtils.isEmpty(pcols[i].columnDefinition()) - && (AccessController.doPrivileged(J2DoPrivHelper + && AccessController.doPrivileged(J2DoPrivHelper .isAnnotationPresentAction(fm.getDeclaredType(), - xmlRootElementClass))).booleanValue()) { + xmlRootElementClass)).booleanValue()) { DBDictionary dict = ((MappingRepository) getRepository()) .getDBDictionary(); if (dict.supportsXMLColumn) Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java Sun Jun 7 01:05:22 2015 @@ -114,5 +114,5 @@ enum MappingTag { X_MAPPING_OVERRIDES, X_SECONDARY_TABLE, X_SECONDARY_TABLES, - X_TABLE, + X_TABLE } Added: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/TestStoredProcedure.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/TestStoredProcedure.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/TestStoredProcedure.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/TestStoredProcedure.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,171 @@ +/* + * 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.jdbc.procedure; + +import org.apache.openjpa.jdbc.procedure.derby.Procedures; +import org.apache.openjpa.jdbc.procedure.entity.EntityWithStoredProcedure; +import org.apache.openjpa.jdbc.sql.DerbyDictionary; +import org.apache.openjpa.persistence.test.DatabasePlatform; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +import java.util.Iterator; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import javax.persistence.StoredProcedureQuery; + +@DatabasePlatform("org.apache.derby.jdbc.EmbeddedDriver") +public class TestStoredProcedure extends SingleEMFTestCase { + @Override + public void setUp() { + setUp( + "openjpa.RuntimeUnenhancedClasses", "unsupported", + "openjpa.DynamicEnhancementAgent", "false", + EntityWithStoredProcedure.class, EntityWithStoredProcedure.Mapping2.class); + setSupportedDatabases(DerbyDictionary.class); + } + + public void testSimple() { + Procedures.simpleCalled = false; + + EntityManager em = emf.createEntityManager(); + exec(em, "CREATE PROCEDURE TESTSIMPLE() " + + "PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME " + + "'" + Procedures.class.getName() + ".simple'"); + StoredProcedureQuery procedure = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.simple"); + assertFalse(procedure.execute()); + em.close(); + assertTrue(Procedures.simpleCalled); + } + + public void testInParams() { + Procedures.inParamsInteger = -1; + Procedures.inParamsString = null; + + EntityManager em = emf.createEntityManager(); + exec(em, "CREATE PROCEDURE TESTINS(some_number INTEGER,some_string VARCHAR(255)) " + + "PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME " + + "'" + Procedures.class.getName() + ".inParams'"); + StoredProcedureQuery procedure = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.inParams"); + procedure.setParameter("SOME_NUMBER", 2015); + procedure.setParameter("SOME_STRING", "openjpa"); + assertFalse(procedure.execute()); + assertEquals(2015, Procedures.inParamsInteger); + assertEquals("openjpa", Procedures.inParamsString); + + // null case + Procedures.inParamsInteger = -1; + Procedures.inParamsString = null; + StoredProcedureQuery procedure2 = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.inParams"); + procedure2.setParameter("SOME_NUMBER", 20152); + assertFalse(procedure2.execute()); + em.close(); + assertEquals(20152, Procedures.inParamsInteger); + assertNull(Procedures.inParamsString); + } + + public void testOut() { + EntityManager em = emf.createEntityManager(); + exec(em, "CREATE PROCEDURE XTWO(IN some_number INTEGER,OUT x2 INTEGER) " + + "PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME " + + "'" + Procedures.class.getName() + ".x2'"); + StoredProcedureQuery procedure = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.x2"); + procedure.setParameter("SOME_NUMBER", 5); + assertFalse(procedure.execute()); + // assertEquals(10, procedure.getOutputParameterValue("result")); // not impl by derby + assertEquals(10, procedure.getOutputParameterValue(2)); + em.close(); + } + + public void testInOut() { + EntityManager em = emf.createEntityManager(); + exec(em, "CREATE PROCEDURE XINOUT(INOUT P INTEGER) " + + "PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME " + + "'" + Procedures.class.getName() + ".inout'"); + StoredProcedureQuery procedure = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.inout"); + procedure.setParameter("P", 5); + assertFalse(procedure.execute()); + // assertEquals(10, procedure.getOutputParameterValue("p")); // not impl by derby + assertEquals(10, procedure.getOutputParameterValue(1)); + em.close(); + } + + public void testMapping() { + EntityManager em = emf.createEntityManager(); + { + em.getTransaction().begin(); + for (int i = 0; i < 2; i++) { + final EntityWithStoredProcedure entity = new EntityWithStoredProcedure(); + entity.setId(1 + i); + entity.setName("#" + entity.getId()); + em.persist(entity); + } + em.getTransaction().commit(); + em.clear(); + } + + exec(em, "CREATE PROCEDURE MAPPING() " + + "PARAMETER STYLE JAVA LANGUAGE JAVA DYNAMIC RESULT SETS 2 EXTERNAL NAME " + + "'" + Procedures.class.getName() + ".mapping'"); + StoredProcedureQuery procedure = em.createNamedStoredProcedureQuery("EntityWithStoredProcedure.mapping"); + assertTrue(procedure.execute()); + final Iterator r1 = procedure.getResultList().iterator(); + final EntityWithStoredProcedure next1 = EntityWithStoredProcedure.class.cast(r1.next()); + assertEquals(1, next1.getId()); + assertEquals("#1", next1.getName()); + assertNotNull(next1); + final EntityWithStoredProcedure next2 = EntityWithStoredProcedure.class.cast(r1.next()); + assertNotNull(next2); + assertEquals(2, next2.getId()); + assertEquals("#2", next2.getName()); + assertFalse(r1.hasNext()); + assertTrue(procedure.hasMoreResults()); + final Iterator r2 = procedure.getResultList().iterator(); + final EntityWithStoredProcedure.Mapping2 next3 = EntityWithStoredProcedure.Mapping2.class.cast(r2.next()); + assertNotNull(next3); + assertFalse(r2.hasNext()); + assertEquals(next2.getId(), next3.getId()); + assertEquals(next2.getName(), next3.getName()); + + { + em.getTransaction().begin(); + for (int i = 0; i < 2; i++) { + em.remove(em.find(EntityWithStoredProcedure.class, i + 1L)); + } + em.getTransaction().commit(); + em.clear(); + } + em.close(); + } + + private void exec(final EntityManager em, final String proc) { + final EntityTransaction tx = em.getTransaction(); + tx.begin(); + try { + em.createNativeQuery(proc).executeUpdate(); + tx.commit(); + } catch (final Exception e) { // already exists or another error + e.printStackTrace(); + try { + tx.rollback(); + } catch (final Exception ignored) { + // no-op + } + } + } +} Added: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/Procedures.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/Procedures.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/Procedures.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/derby/Procedures.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,59 @@ +/* + * 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.jdbc.procedure.derby; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; + +import static junit.framework.Assert.fail; + +public final class Procedures { + public static boolean simpleCalled; + + public static void simple() { + simpleCalled = true; + } + + public static int inParamsInteger; + public static String inParamsString; + + public static void inParams(int integer, String string) { + inParamsInteger = integer; + inParamsString = string; + } + + public static void x2(int integer, int[] out) { + out[0] = integer * 2; + } + + public static void inout(int[] p) { + p[0] = p[0] * 2; + } + + public static void mapping(ResultSet[] r0, ResultSet[] r1) { + try { + Connection c = DriverManager.getConnection("jdbc:default:connection"); + r0[0] = c.createStatement().executeQuery("SELECT * FROM EntityWithStoredProcedure order by id"); + r1[0] = c.createStatement().executeQuery("SELECT * FROM EntityWithStoredProcedure where id = 2"); + } catch (final Exception ex) { + fail(ex.getMessage()); + } + } +} Added: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/EntityWithStoredProcedure.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/EntityWithStoredProcedure.java?rev=1683972&view=auto ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/EntityWithStoredProcedure.java (added) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/procedure/entity/EntityWithStoredProcedure.java Sun Jun 7 01:05:22 2015 @@ -0,0 +1,145 @@ +/* + * 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.jdbc.procedure.entity; + +import javax.persistence.Entity; +import javax.persistence.EntityResult; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQueries; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.SqlResultSetMappings; +import javax.persistence.StoredProcedureParameter; + +import static javax.persistence.ParameterMode.INOUT; +import static javax.persistence.ParameterMode.OUT; + +@Entity +@NamedStoredProcedureQueries({ + @NamedStoredProcedureQuery( + name = "EntityWithStoredProcedure.simple", + procedureName = "TESTSIMPLE" + ), + @NamedStoredProcedureQuery( + name = "EntityWithStoredProcedure.inParams", + procedureName = "TESTINS", + parameters = { + @StoredProcedureParameter(name = "SOME_NUMBER", type = Integer.class), + @StoredProcedureParameter(name = "SOME_STRING", type = String.class) + } + ), + @NamedStoredProcedureQuery( + name = "EntityWithStoredProcedure.x2", + procedureName = "XTWO", + parameters = { + @StoredProcedureParameter(name = "SOME_NUMBER", type = Integer.class), + @StoredProcedureParameter(name = "RESULT", type = Integer.class, mode = OUT) + } + ), + @NamedStoredProcedureQuery( + name = "EntityWithStoredProcedure.inout", + procedureName = "XINOUT", + parameters = { + @StoredProcedureParameter(name = "P ", type = Integer.class, mode = INOUT) + } + ), + @NamedStoredProcedureQuery( + name = "EntityWithStoredProcedure.mapping", + procedureName = "MAPPING", + resultSetMappings = {"mapping1", "mapping2"} + ) +}) +@SqlResultSetMappings({ + @SqlResultSetMapping( + name = "mapping1", + entities = @EntityResult(entityClass = EntityWithStoredProcedure.class) + ), + @SqlResultSetMapping( + name = "mapping2", + entities = @EntityResult(entityClass = EntityWithStoredProcedure.Mapping2.class) + ) +}) +public class EntityWithStoredProcedure { + @Id + private long id; + private String name; + + public EntityWithStoredProcedure() { + // no-op + } + + public EntityWithStoredProcedure(final long id, final String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EntityWithStoredProcedure that = (EntityWithStoredProcedure) o; + return id == that.id && name.equals(that.name); + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + name.hashCode(); + return result; + } + + @Entity + public static class Mapping2 { + @Id + private long id; + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java Sun Jun 7 01:05:22 2015 @@ -223,9 +223,9 @@ public abstract class AbstractPersistenc * Safely close the given factory. */ protected boolean closeEMF(EntityManagerFactory emf) { - boolean brc = false; + boolean brc; if (emf == null || !emf.isOpen()) { - return brc; + return false; } try { closeAllOpenEMs(emf); Modified: openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AbstractQuery.java URL: http://svn.apache.org/viewvc/openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AbstractQuery.java?rev=1683972&r1=1683971&r2=1683972&view=diff ============================================================================== --- openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AbstractQuery.java (original) +++ openjpa/branches/openjpa_jpa-2.1/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AbstractQuery.java Sun Jun 7 01:05:22 2015 @@ -83,6 +83,10 @@ public abstract class AbstractQuery<X> i return result; } + public boolean isProcedure() { + return QueryLanguages.LANG_STORED_PROC.equals(getLanguage()); + } + public boolean isNative() { return QueryLanguages.LANG_SQL.equals(getLanguage()); } @@ -131,8 +135,8 @@ public abstract class AbstractQuery<X> i if (pos < 1) { throw new IllegalArgumentException(_loc.get("illegal-index", pos).getMessage()); } - Parameter<?> param = null; - if (isNative()) { + Parameter<?> param; + if (isNative() || isProcedure()) { param = new ParameterImpl<Object>(pos, Object.class); declareParameter(pos, param); } else {