You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2008/09/09 04:03:38 UTC
svn commit: r693341 [1/2] - in /openjpa/branches/sql-cache:
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-kerne...
Author: ppoddar
Date: Mon Sep 8 19:03:37 2008
New Revision: 693341
URL: http://svn.apache.org/viewvc?rev=693341&view=rev
Log:
Savepoint commit for Prepared Query cache with 2 tests failing
Added:
openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java
openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/util/ParameterMap.java
openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/
openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestParameterMap.java
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java
Modified:
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java
openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java
openjpa/branches/sql-cache/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties
openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestNativeQueryParameterBinding.java
openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java Mon Sep 8 19:03:37 2008
@@ -736,7 +736,8 @@
ExpressionParser ep = QueryLanguages.parserForLanguage(language);
if (ep != null)
return new JDBCStoreQuery(this, ep);
- if (QueryLanguages.LANG_SQL.equals(language))
+ if (QueryLanguages.LANG_SQL.equals(language)
+ || QueryLanguages.LANG_PREPARED_SQL.equals(language))
return new SQLStoreQuery(this);
return null;
}
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Mon Sep 8 19:03:37 2008
@@ -658,14 +658,27 @@
idx++;
}
}
-
- String[] sql = new String[sels.size()];
+ // add a sentinel null String to denote that extra SQL will be
+ // required during execution
+ String[] sql = new String[sels.size() + (hasExtraSQL(sels) ? 1 : 0)];
for (int i = 0; i < sels.size(); i++)
sql[i] = ((Select) sels.get(i)).toSelect(false, fetch).getSQL(true);
return sql;
}
/**
+ * Affirms if any of the given Selects will require extra Select to load
+ * requisite data
+ */
+ boolean hasExtraSQL(List sels) {
+ for (Object sel : sels) {
+ if (sel instanceof Select && ((Select)sel).hasNewEagerSelects())
+ return true;
+ }
+ return false;
+ }
+
+ /**
* This method is to provide override for non-JDBC or JDBC-like
* implementation of executing update.
*/
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java Mon Sep 8 19:03:37 2008
@@ -33,6 +33,7 @@
import java.util.List;
import java.util.Set;
+import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.MappingRepository;
@@ -409,5 +410,40 @@
throws SQLException {
return stmnt.executeQuery();
}
+
+ /**
+ * Counts number of bind parameter marker <code>'?'</code> in the
+ * query string. The type of these parameters are unknown but the
+ * count can be used.
+ */
+ public LinkedMap getParameterTypes(StoreQuery q) {
+ int count = -1;
+ try {
+ count = countParamMarker(q.getContext().getQueryString());
+ } catch (IOException e) {
+
+ }
+ LinkedMap map = new LinkedMap();
+ for (int i = 0; i < count; i++) {
+ map.put(i+1, null);
+ }
+ return map;
+ }
+
+ private static int countParamMarker(String sql) throws IOException {
+ if (sql.indexOf("?") == -1)
+ return 0;
+
+ StreamTokenizer tok = new StreamTokenizer(new StringReader(sql));
+ tok.resetSyntax();
+ tok.quoteChar('\'');
+ tok.wordChars('?', '?');
+ int count = 0;
+ for (int ttype; (ttype = tok.nextToken()) != StreamTokenizer.TT_EOF;) {
+ if (ttype == StreamTokenizer.TT_WORD && "?".equals(tok.sval))
+ count++;
+ }
+ return count;
+ }
}
}
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java Mon Sep 8 19:03:37 2008
@@ -123,9 +123,9 @@
SQLBuffer sql, int index) {
ParamExpState pstate = (ParamExpState) state;
if (pstate.otherLength > 1)
- sql.appendValue(((Object[]) pstate.sqlValue)[index],
+ sql.appendBindParameter(((Object[]) pstate.sqlValue)[index],
pstate.getColumn(index));
else
- sql.appendValue(pstate.sqlValue, pstate.getColumn(index));
+ sql.appendBindParameter(pstate.sqlValue, pstate.getColumn(index));
}
}
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java Mon Sep 8 19:03:37 2008
@@ -819,6 +819,10 @@
SelectExecutor ex = sel.getEager(key);
return (ex == sel) ? this : ex;
}
+
+ public boolean hasNewEagerSelects() {
+ return sel.hasNewEagerSelects();
+ }
public Joins newJoins() {
return sel.newJoins();
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java Mon Sep 8 19:03:37 2008
@@ -25,6 +25,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -33,7 +34,6 @@
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.exps.Val;
import org.apache.openjpa.jdbc.schema.Column;
-import org.apache.openjpa.jdbc.schema.ForeignKey;
import org.apache.openjpa.jdbc.schema.Sequence;
import org.apache.openjpa.jdbc.schema.Table;
import serp.util.Numbers;
@@ -41,22 +41,33 @@
/**
* Buffer for SQL statements that can be used to create
* java.sql.PreparedStatements.
+ *
+ * The buffer holds both the literals and bind variables in the same parameter
+ * list and substitutes them to PreparedStatement. However, a bind variable
+ * is distinguished from the literals by a bit mask on the index of the list
+ * index.
*
* @author Marc Prud'hommeaux
* @author Abe White
+ * @author Pinaki Poddar
* @since 0.2.4
*/
public final class SQLBuffer
implements Serializable, Cloneable {
private static final String PARAMETER_TOKEN = "?";
-
+ private static final char PARAMETER_TOKEN_CHAR = '?';
+ private static final String SINGLE_QUOTE = "'";
+ private static final String NULL_STRING = "NULL";
+ private static final String OPEN_BRACKET = "(";
+ private static final String CLOSE_BRACKET = ")";
+
private final DBDictionary _dict;
private final StringBuffer _sql = new StringBuffer();
private List _subsels = null;
private List _params = null;
+ private BitSet _varIndex = null;
private List _cols = null;
- private List _nonFKParams = null;
/**
* Default constructor.
@@ -123,8 +134,10 @@
_sql.insert(sqlIndex, buf._sql.toString());
if (buf._params != null) {
- if (_params == null)
+ if (_params == null) {
_params = new ArrayList();
+ _varIndex = new BitSet();
+ }
if (_cols == null && buf._cols != null) {
_cols = new ArrayList();
while (_cols.size() < _params.size())
@@ -133,6 +146,8 @@
if (paramIndex == _params.size()) {
_params.addAll(buf._params);
+ for (int i = 0; i < buf._varIndex.size(); i++)
+ _varIndex.set(paramIndex + i, buf._varIndex.get(i));
if (buf._cols != null)
_cols.addAll(buf._cols);
else if (_cols != null)
@@ -140,6 +155,11 @@
_cols.add(null);
} else {
_params.addAll(paramIndex, buf._params);
+ for (int i = 0; i < buf._varIndex.size(); i++) {
+ _varIndex.set(paramIndex + i + buf._varIndex.size(),
+ _varIndex.get(paramIndex+i));
+ _varIndex.set(paramIndex + i, buf._varIndex.get(i));
+ }
if (buf._cols != null)
_cols.addAll(paramIndex, buf._cols);
else if (_cols != null)
@@ -147,11 +167,6 @@
_cols.add(paramIndex, null);
}
}
- if (buf._nonFKParams != null) {
- if (_nonFKParams == null)
- _nonFKParams = new ArrayList();
- _nonFKParams.addAll(buf._nonFKParams);
- }
}
public SQLBuffer append(Table table) {
@@ -193,14 +208,14 @@
*/
private SQLBuffer append(Select sel, JDBCFetchConfiguration fetch,
boolean count) {
- _sql.append("(");
+ _sql.append(OPEN_BRACKET);
Subselect sub = new Subselect();
sub.select = sel;
sub.fetch = fetch;
sub.count = count;
sub.sqlIndex = _sql.length();
sub.paramIndex = (_params == null) ? 0 : _params.size();
- _sql.append(")");
+ _sql.append(CLOSE_BRACKET);
if (_subsels == null)
_subsels = new ArrayList(2);
@@ -224,6 +239,13 @@
}
return false;
}
+
+ /**
+ * Appends a bind variable.
+ */
+ public SQLBuffer appendBindParameter(Object o, Column col) {
+ return appendValue(o, col, true);
+ }
/**
* Append a parameter value.
@@ -231,13 +253,17 @@
public SQLBuffer appendValue(Object o) {
return appendValue(o, null);
}
-
+
+ public SQLBuffer appendValue(Object o, Column col) {
+ return appendValue(o, col, false);
+ }
+
/**
* Append a parameter value for a specific column.
*/
- public SQLBuffer appendValue(Object o, Column col) {
+ public SQLBuffer appendValue(Object o, Column col, boolean isParam) {
if (o == null)
- _sql.append("NULL");
+ _sql.append(NULL_STRING);
else if (o instanceof Raw)
_sql.append(o.toString());
else {
@@ -245,37 +271,20 @@
// initialize param and col lists; we hold off on col list until
// we get the first non-null col
- if (_params == null)
+ if (_params == null) {
_params = new ArrayList();
+ _varIndex = new BitSet();
+ }
if (col != null && _cols == null) {
_cols = new ArrayList();
while (_cols.size() < _params.size())
_cols.add(null);
}
+ _varIndex.set(_params.size(), isParam);
_params.add(o);
if (_cols != null)
_cols.add(col);
- if (col == null)
- return this;
- boolean isFK = false;
- ForeignKey[] fks = col.getTable().getForeignKeys();
- for (int i = 0; i < fks.length; i++) {
- Column[] cols = fks[i].getColumns();
- for (int j = 0; j < cols.length; j++) {
- if (cols[j] == col) {
- isFK = true;
- break;
- }
- }
- if (isFK)
- break;
- }
- if (!isFK) {
- if (_nonFKParams == null)
- _nonFKParams = new ArrayList();
- _nonFKParams.add(o);
- }
}
return this;
}
@@ -399,21 +408,16 @@
return (_params == null) ? Collections.EMPTY_LIST : _params;
}
- public List getNonFKParameters() {
- return (_nonFKParams == null) ? Collections.EMPTY_LIST : _nonFKParams;
- }
/**
* Return the SQL for this buffer.
*/
public String getSQL() {
return getSQL(false);
}
-
+
/**
* Returns the SQL for this buffer.
*
- * @param replaceParams if true, then replace parameters with the
- * actual parameter values
*/
public String getSQL(boolean replaceParams) {
resolveSubselects();
@@ -423,21 +427,26 @@
StringBuffer buf = new StringBuffer();
Iterator pi = _params.iterator();
+ int iParam = 0;
for (int i = 0; i < sql.length(); i++) {
- if (sql.charAt(i) != '?') {
+ if (sql.charAt(i) != PARAMETER_TOKEN_CHAR) {
buf.append(sql.charAt(i));
continue;
}
-
Object param = pi.hasNext() ? pi.next() : null;
+ // bind parameter variables are never replaced
+ if (_varIndex.get(iParam++)) {
+ buf.append(PARAMETER_TOKEN);
+ continue;
+ }
if (param == null)
- buf.append("NULL");
+ buf.append(NULL_STRING);
else if (param instanceof Number || param instanceof Boolean)
buf.append(param);
else if (param instanceof String || param instanceof Character)
- buf.append("'").append(param).append("'");
+ buf.append(SINGLE_QUOTE).append(param).append(SINGLE_QUOTE);
else
- buf.append("?");
+ buf.append(PARAMETER_TOKEN);
}
return buf.toString();
}
@@ -598,7 +607,7 @@
_dict.setUnknown(ps, i + 1, _params.get(i), col);
}
}
-
+
public int hashCode() {
int hash = _sql.hashCode();
return (_params == null) ? hash : hash ^ _params.hashCode();
@@ -629,7 +638,7 @@
* @param val
*/
public void addCastForParam(String oper, Val val) {
- if (_sql.charAt(_sql.length() - 1) == '?') {
+ if (_sql.charAt(_sql.length() - 1) == PARAMETER_TOKEN_CHAR) {
String castString = _dict.addCastAsType(oper, val);
if (castString != null)
_sql.replace(_sql.length() - 1, _sql.length(), castString);
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java Mon Sep 8 19:03:37 2008
@@ -657,6 +657,11 @@
* Return the eager select for the given key.
*/
public SelectExecutor getEager(FieldMapping key);
+
+ /**
+ * Affirms if this select will use extra selects for eager loading.
+ */
+ public boolean hasNewEagerSelects();
/**
* Return a new instance to use for joining.
Modified: openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java Mon Sep 8 19:03:37 2008
@@ -1809,6 +1809,16 @@
public Map getEagerMap() {
return _eager;
}
+
+ public boolean hasNewEagerSelects() {
+ if (_eager == null)
+ return false;
+ for (Object eagerRes : _eager.values()) {
+ if (eagerRes != this)
+ return true;
+ }
+ return false;
+ }
public SelectExecutor getEager(FieldMapping key) {
if (_eager == null || !_eagerKeys.contains(key))
Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java Mon Sep 8 19:03:37 2008
@@ -1526,4 +1526,24 @@
* @since 1.3.0
*/
public void setInitializeEagerly(boolean flag);
+
+ /**
+ * Configuration settings for the Prepared Query Cache to use.
+ * @see PreparedQueryCacheValue
+ * @since 1.3.0
+ */
+ public String getPreparedQueryCache();
+
+ /**
+ * Configuration settings for the Prepared Query Cache to use.
+ * @see PreparedQueryCacheValue
+ * @since 1.3.0
+ */
+ public void setPreparedQueryCache(String cache);
+
+ /**
+ * Gets the modifable map of the cached prepared query indexed by query
+ * String.
+ */
+ public Map getPreparedQueryCacheInstance();
}
Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java Mon Sep 8 19:03:37 2008
@@ -136,6 +136,7 @@
public IntValue runtimeUnenhancedClasses;
public CacheMarshallersValue cacheMarshallerPlugins;
public BooleanValue eagerInitialization;
+ public PreparedQueryCacheValue preparedQueryCachePlugin;
// custom values
public BrokerFactoryValue brokerFactoryPlugin;
@@ -493,6 +494,13 @@
"getQueryCompilationCacheInstance");
addValue(queryCompilationCachePlugin);
+ preparedQueryCachePlugin = new PreparedQueryCacheValue(
+ "PreparedQueryCache");
+ preparedQueryCachePlugin.setInstantiatingGetter(
+ "getPreparedQueryCacheInstance");
+ addValue(preparedQueryCachePlugin);
+ preparedQueryCachePlugin.setDynamic(true);
+
runtimeUnenhancedClasses = addInt("RuntimeUnenhancedClasses");
runtimeUnenhancedClasses.setAliases(new String[] {
"supported", String.valueOf(
@@ -1395,6 +1403,20 @@
return (Map) queryCompilationCachePlugin.get();
}
+ public String getPreparedQueryCache() {
+ return preparedQueryCachePlugin.getString();
+ }
+
+ public void setPreparedQueryCache(String queryCompilationCache) {
+ preparedQueryCachePlugin.setString(queryCompilationCache);
+ }
+
+ public Map getPreparedQueryCacheInstance() {
+ if (preparedQueryCachePlugin.get() == null)
+ preparedQueryCachePlugin.instantiate(Map.class, this);
+ return (Map) preparedQueryCachePlugin.get();
+ }
+
public StoreFacadeTypeRegistry getStoreFacadeTypeRegistry() {
return _storeFacadeRegistry;
}
Added: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java (added)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,78 @@
+/*
+ * 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.conf;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.conf.PluginValue;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.openjpa.lib.util.ParseException;
+import org.apache.openjpa.util.CacheMap;
+
+/**
+ * A cache of prepared queries indexed by an identifier.
+ *
+ * @author Pinaki Poddar
+ * @since 1.3.0
+ * @nojavadoc
+ */
+public class PreparedQueryCacheValue
+ extends PluginValue {
+
+ public static final String[] ALIASES = {
+ "true", CacheMap.class.getName(),
+ "all", ConcurrentHashMap.class.getName(),
+ "false", null,
+ };
+
+ public PreparedQueryCacheValue(String prop) {
+ super(prop, true);
+ setAliases(ALIASES);
+ setDefault(ALIASES[0]);
+ setClassName(ALIASES[1]);
+ }
+
+ public Object newInstance(String clsName, Class type,
+ Configuration conf, boolean fatal) {
+ // make sure map handles concurrency
+ Map map;
+
+ try {
+ map = (Map) super.newInstance(clsName, type, conf, fatal);
+ } catch (ParseException pe) {
+ // For comment on special classloading see QueryCompilationCacheValue
+ map = (Map) super.newInstance(clsName,
+ PreparedQueryCacheValue.class, conf, fatal);
+ } catch (IllegalArgumentException iae) {
+ map = (Map) super.newInstance(clsName,
+ PreparedQueryCacheValue.class, conf, fatal);
+ }
+
+ if (map != null && !(map instanceof Hashtable)
+ && !(map instanceof CacheMap)
+ && !(map instanceof
+ org.apache.openjpa.lib.util.concurrent.ConcurrentMap)
+ && !(map instanceof java.util.concurrent.ConcurrentMap))
+ map = Collections.synchronizedMap(map);
+ return map;
+ }
+}
Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java Mon Sep 8 19:03:37 2008
@@ -34,6 +34,7 @@
public class QueryLanguages {
public static final String LANG_SQL = "openjpa.SQL";
+ public static final String LANG_PREPARED_SQL = "openjpa.SQL.Prepared";
public static final String LANG_METHODQL = "openjpa.MethodQL";
private static Map _expressionParsers = new HashMap();
Added: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/util/ParameterMap.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/util/ParameterMap.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/util/ParameterMap.java (added)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/util/ParameterMap.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,393 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.openjpa.lib.util.Localizer;
+
+/**
+ * Structure for holding binding query parameters.
+ *
+ * Allows either named or positional parameter keys. The parameter key type is
+ * determined at the first insertion unless specified at construction.
+ *
+ * Maintains parameter ordering.
+ * The parameter ordering for named keys is same as the insertion order.
+ * Positional parameters can use an starting offset that defaults to 1.
+ *
+ * Allows to set expected types of values for each parameter.
+ *
+ * Can validate itself.
+ *
+ * Can convert a named parameter map to a positional parameter map.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class ParameterMap implements Map {
+ public static enum Type {POSITIONAL, NAMED};
+
+ private static final Localizer _loc =
+ Localizer.forPackage(ParameterMap.class);
+
+ private Map _delegate = null;
+ private final Map<Object,Class> _valueTypes = new HashMap<Object,Class>();
+ private Type _type = null;
+ private final int _offset;
+
+ /**
+ * Construct a map with indefinite key type and positional offset of 1.
+ */
+ public ParameterMap() {
+ this(null, 1);
+ }
+
+ /**
+ * Construct a map with indefinite key type and given positional offset.
+ */
+ public ParameterMap(int offset) {
+ this(null, offset);
+ }
+
+ /**
+ * Construct a map with indefinite key type and positional offset of 1.
+ */
+ public ParameterMap(Type type) {
+ this(type, 1);
+ }
+
+ /**
+ * Construct a map with given key type and given positional offset.
+ * Even if this map is not positional then the offset is used when the
+ * map is converted to positional map.
+ */
+ public ParameterMap(Type type, int offset) {
+ setType(type);
+ _offset = offset;
+
+ }
+
+ private void setType(Type type) {
+ _type = type;
+ if (isNamed())
+ _delegate = new LinkedMap();
+ if (isPositional())
+ _delegate = new TreeMap<Integer, Object>();
+ }
+
+ /**
+ * Affirms if this receiver contains named parameter keys.
+ *
+ * @return false also denotes that the parameter key type is not yet known.
+ * Map that are constructed with unspecified key type becomes known at
+ * first insertion.
+ *
+ * @see #put(Object, Object)
+ */
+ public boolean isNamed() {
+ return Type.NAMED.equals(_type);
+ }
+
+
+ /**
+ * Affirms if this receiver contains positional parameter keys.
+ *
+ * @return false also denotes that the parameter key type is not yet known.
+ * Map that are constructed with unspecified key type becomes known at
+ * first insertion.
+ *
+ * @see #put(Object, Object)
+ */
+ public boolean isPositional() {
+ return Type.POSITIONAL.equals(_type);
+ }
+
+ /**
+ * Gets the enumerated key type allowed in this receiver.
+ */
+ public Type getType() {
+ return _type;
+ }
+
+ /**
+ * Clears this receiver for complete reuse. This includes clearing the
+ * bound values, their allowed types as well as the type of allowed keys.
+ */
+ public void clear() {
+ if (_delegate == null)
+ return;
+ _delegate = null;
+ _valueTypes.clear();
+ _type = null;
+ }
+
+ /**
+ * Clears all the bound keys by nullifying their values. The allowed value
+ * type for each key and the allowed key type remain.
+ */
+ public void clearBinding() {
+ if (_delegate == null)
+ return;
+ for (Object key : keySet())
+ _delegate.put(key, null);
+ }
+
+ /**
+ * Affirms if this receiver contains the given key.
+ */
+ public boolean containsKey(Object key) {
+ return _delegate == null ? false : _delegate.containsKey(key);
+ }
+
+ /**
+ * Affirms if this receiver contains the given value.
+ */
+ public boolean containsValue(Object value) {
+ return _delegate == null ? null : _delegate.containsValue(value);
+ }
+
+ /**
+ * Gets the keys known to this receiver.
+ */
+ public Set entrySet() {
+ return _delegate == null ? Collections.EMPTY_SET : _delegate.entrySet();
+ }
+
+ /**
+ * Gets the value for the given key.
+ *
+ * @return null if the key does not exist or if the key exists with null
+ * value.
+ */
+ public Object get(Object key) {
+ return _delegate == null ? null : _delegate.get(key);
+ }
+
+ /**
+ * Affirms if this receiver has no key.
+ *
+ */
+ public boolean isEmpty() {
+ return _delegate == null || _delegate.isEmpty();
+ }
+
+ /**
+ * Puts the given key-value pair. If this is the first key inserted and the
+ * key type is unspecified during construction, then the type of the given
+ * key determines whether this receiver will hold named or positional
+ * parameters.
+ *
+ * @param key must be either String or Integer and must match the allowed
+ * key type of this receiver unless this is a first key insertion and the
+ * allowed key type is unspecified at construction.
+ *
+ * @param value is validated if an allowed value type for the given key is
+ * set.
+ *
+ */
+ public Object put(Object key, Object value) {
+ if (key == null)
+ newValidationException("param-null-key", new Object[]{});
+ Type type = determineKeyType(key);
+ if (_type == null) {
+ setType(type);
+ } else {
+ if (!_type.equals(type))
+ newValidationException("param-mismatch-key-type", key,
+ key.getClass(), _type);
+
+ if (isPositional() && ((Integer)key).intValue() < _offset)
+ newValidationException("param-bad-key-index", key, _offset);
+ }
+
+ assertCompatiableValue(key, value);
+ return _delegate.put(key, value);
+ }
+
+ public void putAll(Map t) {
+ _delegate.putAll(t);
+ }
+
+ public Object remove(Object key) {
+ return (_delegate == null) ? null : _delegate.remove(key);
+ }
+
+ public int size() {
+ return (_delegate == null) ? 0 : _delegate.size();
+ }
+
+ /**
+ * Sets the type of value that the given key can be bound to. If a value
+ * exists for the key, then the value is validated against the given type.
+ *
+ * @param key the key may or may not have been bound to a value.
+ * @param type the type of the value that the given key can be bound to.
+ */
+ public void setValueType(Object key, Class expectedType) {
+ _valueTypes.put(key, expectedType);
+ if (containsKey(key)) {
+ Object value = get(key);
+ assertCompatiableValue(key, value);
+ }
+ }
+
+ /**
+ * Gets the type of value the given key can bind to.
+ *
+ * @return null denotes the value type for the given key is either not set
+ * or set to null explicitly.
+ *
+ */
+ public Class getValueType(Object key) {
+ return _valueTypes.get(key);
+ }
+
+ /**
+ * Gets the set of keys. The returned set has deterministic iteration order.
+ * For named parameter type, the resultant order is the order in which
+ * keys were inserted.
+ * For positional parameter type, the resultant order is the natural order
+ * of the key indices.
+ */
+ public Set keySet() {
+ return (_delegate == null) ? Collections.EMPTY_SET : _delegate.keySet();
+ }
+
+ /**
+ * Gets the values. The returned collection has deterministic iteration
+ * order.
+ * For named parameter type, the resultant order is the order in which
+ * key-value pairs were inserted.
+ * For positional parameter type, the resultant order is the natural order
+ * of the key indices.
+ *
+ */
+ public Collection values() {
+ return (_delegate == null) ? Collections.EMPTY_SET : _delegate.values();
+ }
+
+ /**
+ * Get the unmodifable map of key-value in deterministic iteration order.
+ * For named parameter type, the resultant order is the order in which
+ * key-value pairs were inserted.
+ * For positional parameter type, the resultant order is the natural order
+ * of the key indices.
+ */
+ public Map getMap() {
+ return isEmpty() ? Collections.EMPTY_MAP :
+ Collections.unmodifiableMap(_delegate);
+ }
+
+ /**
+ * Create a positional map as a copy of this receiver.
+ * If this receiver is a named map, then the new positional map will have
+ * integer keys corresponding to insertion order of the original keys.
+ *
+ */
+ public ParameterMap toPositional() {
+ ParameterMap positional = new ParameterMap(Type.POSITIONAL, _offset);
+ Set keys = keySet();
+ int i = _offset;
+ for (Object key : keys) {
+ Object newKey = (isNamed()) ? new Integer(i++) : key;
+ positional.put(newKey, get(key));
+ Class expectedType = getValueType(key);
+ if (_valueTypes.containsKey(key))
+ setValueType(newKey, expectedType);
+ }
+ return positional;
+ }
+
+ /**
+ * Validate the parameters against a set of expected parameter types.
+ * If strict then also checks that each given expected key exists in
+ * this receiver.
+ */
+ public boolean validate(LinkedMap expected, boolean strict) {
+ if (expected.size() != size())
+ newValidationException("param-invalid-size",
+ expected.size(), expected, size(), getMap());
+ if (!strict) return true;
+ for (Object key : expected.keySet()) {
+ key = isNamed() ? key.toString() : Integer.parseInt(key.toString());
+ if (!containsKey(key))
+ newValidationException("param-missing", key, expected, getMap());
+ }
+ return true;
+ }
+
+ void assertCompatiableValue(Object key, Object value) {
+ Class expected = _valueTypes.get(key);
+ if (expected == null)
+ return;
+ if (value == null) {
+ if (expected.isPrimitive())
+ newValidationException("param-null-primitive", key, expected);
+ return;
+ }
+ if (expected.isPrimitive()) {
+ if (unwrap(value.getClass()) != expected)
+ newValidationException("param-mismatch-value-type",
+ key, value, value.getClass(), expected);
+ } else if (!expected.isAssignableFrom(value.getClass())) {
+ newValidationException("param-mismatch-value-type",
+ key, value, value.getClass(), expected);
+ }
+ }
+
+ private Type determineKeyType(Object key) {
+ if (key instanceof Integer)
+ return Type.POSITIONAL;
+ else if (key instanceof String)
+ return Type.NAMED;
+ else
+ newValidationException("param-bad-key-type", key, key.getClass());
+ return null;//unreachable
+ }
+
+
+ Class unwrap(Class c) {
+ if (c == null)
+ return c;
+ if (c == Boolean.class) return boolean.class;
+ if (c == Byte.class) return byte.class;
+ if (c == Character.class) return char.class;
+ if (c == Double.class) return double.class;
+ if (c == Float.class) return float.class;
+ if (c == Integer.class) return int.class;
+ if (c == Long.class) return long.class;
+ if (c == Short.class) return short.class;
+ return c;
+ }
+
+ public String toString() {
+ return _type + " values " + _delegate + " types " + _valueTypes;
+ }
+
+ void newValidationException(String msgKey, Object... args) {
+ throw new IllegalArgumentException(_loc.get(msgKey, args).toString());
+ }
+}
Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties Mon Sep 8 19:03:37 2008
@@ -73,3 +73,17 @@
no-store-exts: No store-specific facade found matching "{0}". Using default.
objectid-abstract: Cannot create new application identity instance for \
abstract class "{0}".
+param-null-key: Null parameter keys are not allowed.
+param-bad-key-type: Parameter key "{0}" of "{1}" is not allowed. Parameter key \
+ can be either String or integer.
+param-mismatch-key-type: Parameter key "{0}" of "{1}" does not match with \
+ its expected "{2}".
+param-bad-key-index: Parameter key "{0}" less than "{1}" is not allowed.
+param-null-primitive: Parameter "{0}" is of primitive type "{1}". Its value \
+ can not be null.
+param-mismatch-value-type: Parameter "{0}" can not be set to "{1}" of "{2}" \
+ as the expected value type is "{3}".
+param-invalid_size: Expected {0} parameter(s) of type "{1}" is different in \
+ number than available {2} parameter(s) of type "{3}".
+param-missing: Parameter "{0}" is missing in expected parameter(s) of type \
+ "{1}". Available parameters are "{2}".
\ No newline at end of file
Added: openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestParameterMap.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestParameterMap.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestParameterMap.java (added)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestParameterMap.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,286 @@
+/*
+ * 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 java.util.Iterator;
+import java.util.Map;
+
+import org.apache.openjpa.util.ParameterMap;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the properties of ParameterMap.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class TestParameterMap extends TestCase {
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testTypeIsDeterminedOnFirstInsertion() {
+ ParameterMap m1 = new ParameterMap();
+ assertFalse(m1.isNamed());
+ assertFalse(m1.isPositional());
+ m1.put("key", "value");
+ assertTrue(m1.isNamed());
+ assertFalse(m1.isPositional());
+
+ ParameterMap m2 = new ParameterMap();
+ assertFalse(m2.isNamed());
+ assertFalse(m2.isPositional());
+ m2.put(1, "value");
+ assertFalse(m2.isNamed());
+ assertTrue(m2.isPositional());
+ }
+
+ public void testTypeIsDeterminedOnConstruction() {
+ assertTrue(new ParameterMap(ParameterMap.Type.NAMED).isNamed());
+ assertTrue(new ParameterMap(ParameterMap.Type.POSITIONAL).isPositional());
+
+ }
+
+ public void testConvertNamedToPositional() {
+ ParameterMap m = new ParameterMap();
+ m.put("key1", "value1");
+ m.put("key2", "value2");
+
+ ParameterMap positional = m.toPositional();
+ assertTrue(positional.isPositional());
+ assertEquals("value1", positional.get(1));
+ assertEquals("value2", positional.get(2));
+ }
+
+ public void testConvertPositionalToPositional() {
+ ParameterMap m = new ParameterMap();
+ m.put(2, "value2");
+ m.put(1, "value1");
+
+ ParameterMap positional = m.toPositional();
+ assertTrue(positional.isPositional());
+ assertEquals("value1", positional.get(1));
+ assertEquals("value2", positional.get(2));
+ }
+
+ public void testNamedKeysAreInInsertionOrder() {
+ ParameterMap m = new ParameterMap();
+ m.put("key1", "value1");
+ m.put("key2", "value2");
+
+ Iterator keys = m.keySet().iterator();
+ assertEquals("key1", keys.next());
+ assertEquals("key2", keys.next());
+
+ // Update an old key and insert a new one
+ m.put("key1", "new value in old key");
+ m.put("key3", "new value");
+ keys = m.keySet().iterator();
+ assertEquals("key1", keys.next());
+ assertEquals("key2", keys.next());
+ assertEquals("key3", keys.next());
+
+ }
+
+ public void testNamedValuesInInsertionOrder() {
+ ParameterMap map = new ParameterMap();
+ map.put("key1", "value1");
+ map.put("key2", "value2");
+ Iterator values = map.values().iterator();
+ assertEquals("value1", values.next());
+ assertEquals("value2", values.next());
+
+ // Update an old key and insert a new one
+ map.put("key1", "new value in old key");
+ map.put("key3", "new value");
+ values = map.values().iterator();
+ assertEquals("new value in old key", values.next());
+ assertEquals("value2", values.next());
+ assertEquals("new value", values.next());
+ }
+
+ public void testNamedMapInInsertionOrder() {
+ ParameterMap map = new ParameterMap();
+ map.setValueType("key1", int.class);
+ map.setValueType("key2", String.class);
+
+ map.put("key1", 5);
+ map.put("key2", "value2");
+
+ Iterator entries = map.getMap().entrySet().iterator();
+ for (int i = 0; i < 2; i++) {
+ Map.Entry entry = (Map.Entry)entries.next();
+ if (i == 0) assertTrue("key1".equals(entry.getKey()) && entry.getValue().equals(5));
+ if (i == 1) assertTrue("key2".equals(entry.getKey()) && entry.getValue().equals("value2"));
+ }
+
+ map.put("key1", 10);
+ map.put("key3", "value3");
+ entries = map.getMap().entrySet().iterator();
+ for (int i = 0; i < 3; i++) {
+ Map.Entry entry = (Map.Entry)entries.next();
+ if (i == 0) assertTrue("key1".equals(entry.getKey()) && entry.getValue().equals(10));
+ if (i == 1) assertTrue("key2".equals(entry.getKey()) && entry.getValue().equals("value2"));
+ if (i == 2) assertTrue("key3".equals(entry.getKey()) && entry.getValue().equals("value3"));
+ }
+ }
+
+ public void testNamedParameterWithWrongTypeBefore() {
+ ParameterMap map = new ParameterMap();
+ map.setValueType("key1", int.class);
+ map.setValueType("key2", String.class);
+ map.setValueType("key3", Integer.class);
+ map.setValueType("key4", Integer.class);
+
+ map.put("key1", 5);
+ try {
+ map.put("key2", 7);
+ fail();
+ } catch (IllegalArgumentException ex) {
+ // good
+ }
+ map.put("key3", new Integer(7));
+ map.put("key4", 8);
+ map.put("key1", new Integer(9));
+ }
+
+ public void testNamedParameterWithWrongTypeAfter() {
+ ParameterMap map = new ParameterMap();
+ map.put("key1", 5);
+ map.put("key2", 7);
+ map.setValueType("key1", int.class);
+
+ try {
+ map.setValueType("key2", String.class);
+ fail();
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ map.put("key3", new Integer(7));
+ map.put("key4", 8);
+ map.setValueType("key3", Integer.class);
+ map.setValueType("key4", Integer.class);
+ }
+
+ public void testPositionalParameterWithoutSpecificType() {
+ ParameterMap map = new ParameterMap();
+ map.put(1, "value1");
+ map.put(2, "value2");
+ assertFalse(map.isNamed());
+ assertEquals(2, map.size());
+ Iterator values = map.values().iterator();
+ assertEquals("value1", values.next());
+ assertEquals("value2", values.next());
+
+ map.put(1, "new value in old key");
+ values = map.values().iterator();
+ assertEquals("new value in old key", values.next());
+ assertEquals("value2", values.next());
+ }
+
+ public void testPositionalParameterWithSpecificType() {
+ ParameterMap map = new ParameterMap();
+ map.setValueType(1, int.class);
+ map.setValueType(2, String.class);
+
+ map.put(1, 5);
+ map.put(2, "value2");
+ assertFalse(map.isNamed());
+ assertEquals(2, map.size());
+ Iterator values = map.values().iterator();
+ assertEquals(5, values.next());
+ assertEquals("value2", values.next());
+
+ map.put(1, 10);
+ values = map.values().iterator();
+ assertEquals(10, values.next());
+ assertEquals("value2", values.next());
+ }
+
+ public void testPositionalParameterWithWrongTypeBefore() {
+ ParameterMap map = new ParameterMap();
+ map.setValueType(1, int.class);
+ map.setValueType(2, String.class);
+ map.setValueType(3, Integer.class);
+ map.setValueType(4, Integer.class);
+
+ map.put(1, 5);
+ try {
+ map.put(2, 7);
+ fail();
+ } catch (IllegalArgumentException ex) {
+
+ }
+ map.put(3, new Integer(7));
+ map.put(4, 8);
+ map.put(1, new Integer(9));
+ }
+
+ public void testPositionalParameterWithWrongTypeAfter() {
+ ParameterMap map = new ParameterMap();
+ map.put(1, 5);
+ map.put(2, 7);
+ map.setValueType(1, int.class);
+
+ try {
+ map.setValueType(2, String.class);
+ fail();
+ } catch (IllegalArgumentException ex) {
+
+ }
+
+ map.put(3, new Integer(7));
+ map.put(4, 8);
+ map.setValueType(3, Integer.class);
+ map.setValueType(4, Integer.class);
+ }
+
+ public void testPositionalParameterInsertedInRandomOrder() {
+ ParameterMap map = new ParameterMap();
+ map.put(3, 30);
+ map.put(1, 10);
+ map.put(2, 20);
+ map.put(5, 50);
+ map.put(4, 40);
+ Iterator values = map.values().iterator();
+ assertEquals(10, values.next());
+ assertEquals(20, values.next());
+ assertEquals(30, values.next());
+ assertEquals(40, values.next());
+ assertEquals(50, values.next());
+ }
+
+ public void testPositionalParameterInsertedWithGap() {
+ ParameterMap map = new ParameterMap();
+ map.put(3, 30);
+ map.put(2, 20);
+ map.put(5, 50);
+ Iterator values = map.values().iterator();
+ assertEquals(20, values.next());
+ assertEquals(30, values.next());
+ assertEquals(50, values.next());
+ }
+
+}
Modified: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestNativeQueryParameterBinding.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestNativeQueryParameterBinding.java?rev=693341&r1=693340&r2=693341&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestNativeQueryParameterBinding.java (original)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestNativeQueryParameterBinding.java Mon Sep 8 19:03:37 2008
@@ -21,8 +21,6 @@
import javax.persistence.EntityManager;
import javax.persistence.Query;
-import org.apache.openjpa.persistence.jdbc.query.domain.Applicant;
-import org.apache.openjpa.persistence.jdbc.query.domain.Application;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
Added: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java (added)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,86 @@
+/*
+ * 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.jdbc.sqlcache;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Address {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String street;
+
+ private String city;
+
+ private String state;
+
+ private int zip;
+
+ public Address() {
+
+ }
+
+ public Address(String street, String city, String state, int zip) {
+ super();
+ this.street = street;
+ this.city = city;
+ this.state = state;
+ this.zip = zip;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public int getZip() {
+ return zip;
+ }
+
+ public void setZip(int zip) {
+ this.zip = zip;
+ }
+
+ public long getId() {
+ return id;
+ }
+}
Added: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java (added)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,60 @@
+/*
+ * 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.jdbc.sqlcache;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.persistence.*;
+
+@Entity
+public class Company {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ @OneToMany(mappedBy="company", cascade=CascadeType.ALL)
+ private Collection<Department> departments = new HashSet<Department>();
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Collection<Department> getDepartments() {
+ return departments;
+ }
+
+ public void addDepartment(Department dept) {
+ this.departments.add(dept);
+ dept.setCompany(this);
+ }
+
+ public long getId() {
+ return id;
+ }
+
+
+
+}
Added: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java (added)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,70 @@
+/*
+ * 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.jdbc.sqlcache;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.persistence.*;
+
+@Entity
+public class Department {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ @ManyToOne
+ private Company company;
+
+ @OneToMany
+ private Collection<Employee> employees = new HashSet<Employee>();
+
+ public Company getCompany() {
+ return company;
+ }
+
+ public void setCompany(Company company) {
+ this.company = company;
+ }
+
+ public Collection<Employee> getEmployees() {
+ return employees;
+ }
+
+ public void addEmployees(Employee emp) {
+ this.employees.add(emp);
+ emp.setDepartment(this);
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+}
Added: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java (added)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,65 @@
+/*
+ * 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.jdbc.sqlcache;
+
+import javax.persistence.*;
+
+@Entity
+public class Employee {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ @ManyToOne
+ private Department department;
+
+ @OneToOne
+ private Address address;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Department getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(Department department) {
+ this.department = department;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+}
Added: openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java?rev=693341&view=auto
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java (added)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java Mon Sep 8 19:03:37 2008
@@ -0,0 +1,180 @@
+/*
+ * 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.jdbc.sqlcache;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.PreparedQuery;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Performance oriented test to see comparative difference in response time with
+ * or with SQL caching.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class TestPreparedQueryCache extends SingleEMFTestCase {
+ public static final int SAMPLE_SIZE = 100;
+ public static final long NANOS_TO_MILLS = 1000*1000;
+ public static final boolean QUERY_CACHE = true;
+ public static final String[] COMPANY_NAMES = { "acme.org" };
+ public static final String[] DEPARTMENT_NAMES = { "Marketing", "Sales",
+ "Engineering" };
+ public static final String[] EMPLOYEE_NAMES = { "Tom", "Dick", "Harray" };
+
+ public void setUp() throws Exception {
+ super.setUp(Company.class, Department.class, Employee.class,
+ Address.class, "openjpa.Log", "SQL=WARN");
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testPreparedQueryCacheIsActiveByDefault() {
+ OpenJPAConfiguration conf = emf.getConfiguration();
+ assertEquals("true", conf.getPreparedQueryCache());
+ assertNotNull(conf.getPreparedQueryCacheInstance());
+ }
+
+ public void testPreparedQueryCacheCanBeDeactivatedDynamically() {
+ OpenJPAConfiguration conf = emf.getConfiguration();
+ assertNotNull(conf.getPreparedQueryCacheInstance());
+ conf.setPreparedQueryCache("false");
+ assertNull(conf.getPreparedQueryCacheInstance());
+ }
+
+ public void testSimpleQueryCache() {
+ compare("select p from Company p");
+ }
+
+ public void testQueryWithLiteral() {
+ compare("select p from Company p where p.name = 'PObject'");
+ }
+
+ public void testQueryWithParameter() {
+ compare("select p from Company p where p.name = :param",
+ "param", "x");
+ }
+
+ public void testJoins() {
+ compare("select e from Employee e " + "where e.name = :emp "
+ + "and e.department.name = :dept " +
+ "and e.department.company.name = :company " +
+ "and e.address.zip = :zip",
+
+ "emp", "John",
+ "dept", "Engineering",
+ "company", "acme.org",
+ "zip", 12345);
+ }
+
+ /**
+ * Compare the result of execution of the same query with and without
+ * Prepared Query Cache.
+ *
+ */
+ void compare(String jpql, Object... params) {
+ // run the query once for warming up
+ run(jpql, params, !QUERY_CACHE, 1);
+
+ // run N times without cache
+ long without = run(jpql, params, !QUERY_CACHE, SAMPLE_SIZE);
+
+ // run N times with cache
+ long with = run(jpql, params, QUERY_CACHE, SAMPLE_SIZE);
+
+ long delta = (without == 0) ? 0 : (without - with) * 100 / without;
+
+ String sql = getSQL(jpql);
+ System.err.println("Execution time in millis for " + SAMPLE_SIZE
+ + " query execution with and without SQL cache:" + with + " "
+ + without + " (" + delta + "%)");
+ System.err.println("JPQL: " + jpql);
+ System.err.println("SQL : " + sql);
+ assertFalse("change in execution time = " + delta + "%", delta < 0);
+ }
+
+ /**
+ * Create and run a query N times with the given parameters. The time for
+ * each query execution is measured in nanosecond precision) and finally
+ * median time taken in N observation is returned in nanosecond.
+ *
+ * returns median time taken for single execution.
+ */
+ long run(String jpql, Object[] params, boolean cache, int N) {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getConfiguration().setPreparedQueryCache("" + cache);
+
+ List<Long> stats = new ArrayList<Long>();
+ for (int i = 0; i < N; i++) {
+ long start = System.nanoTime();
+ OpenJPAQuery q = em.createQuery(jpql);
+ for (int j = 0; params != null && j < params.length - 1; j += 2) {
+ String key = params[j].toString();
+ Object val = params[j + 1];
+ if (val instanceof String)
+ q.setParameter(key, val + "-" + i);
+ else if (val instanceof Integer)
+ q.setParameter(key, ((Integer) val).intValue() + i);
+ else
+ q.setParameter(key, val);
+ }
+ q.getResultList();
+ q.closeAll();
+ long end = System.nanoTime();
+ stats.add(end - start);
+ }
+ em.close();
+ Collections.sort(stats);
+ return stats.get(N/2);
+ }
+
+ public boolean isCached(String jpql) {
+ Map cache = emf.getConfiguration().getPreparedQueryCacheInstance();
+ return cache != null && cache.get(jpql) != null
+ && cache.get(jpql) != PreparedQuery.NOT_CACHABLE;
+ }
+
+ String getSQL(String jpql) {
+ Map cache = emf.getConfiguration().getPreparedQueryCacheInstance();
+ return cache == null ? null : ((PreparedQuery)cache.get(jpql)).getSQL();
+ }
+
+
+
+ public static void main(String[] args) throws Exception {
+ TestPreparedQueryCache _this = new TestPreparedQueryCache();
+ _this.setUp();
+ String jpql = "select e from Employee e where e.name = :emp and "
+ + "e.department.name = :dept and "
+ + "e.department.company.name = :company and e.address.zip = :zip";
+ Object[] params = { "emp", "John", "dept", "Engineering", "company",
+ "acme.org", "zip", 12345 };
+ _this.run(jpql, params, true, 100);
+ }
+
+}