mirror of https://github.com/apache/openjpa.git
OPENJPA-924: Support cache of Finder queries.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@745293 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
556e09b69f
commit
3fc7f54a01
|
@ -40,6 +40,8 @@ import org.apache.openjpa.jdbc.sql.DBDictionary;
|
|||
import org.apache.openjpa.jdbc.sql.DBDictionaryFactory;
|
||||
import org.apache.openjpa.jdbc.sql.SQLFactory;
|
||||
import org.apache.openjpa.kernel.BrokerImpl;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.kernel.PreparedQueryCache;
|
||||
import org.apache.openjpa.kernel.StoreContext;
|
||||
import org.apache.openjpa.lib.conf.IntValue;
|
||||
import org.apache.openjpa.lib.conf.ObjectValue;
|
||||
|
@ -317,6 +319,17 @@ public class JDBCConfigurationImpl
|
|||
preparedQueryCachePlugin.setDynamic(true);
|
||||
preparedQueryCachePlugin.setInstantiatingGetter("getQuerySQLCacheInstance");
|
||||
|
||||
finderCachePlugin = addPlugin("jdbc.FinderCache", true);
|
||||
aliases = new String[] {
|
||||
"true", "org.apache.openjpa.jdbc.kernel.FinderCacheImpl",
|
||||
"false", null
|
||||
};
|
||||
finderCachePlugin.setAliases(aliases);
|
||||
finderCachePlugin.setAliasListComprehensive(true);
|
||||
finderCachePlugin.setDefault(aliases[0]);
|
||||
finderCachePlugin.setClassName(aliases[1]);
|
||||
finderCachePlugin.setDynamic(true);
|
||||
finderCachePlugin.setInstantiatingGetter("getFinderCacheInstance");
|
||||
|
||||
// this static initializer is to get past a weird
|
||||
// ClassCircularityError that happens only under IBM's
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.kernel.FinderQuery;
|
||||
import org.apache.openjpa.kernel.QueryHints;
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
import org.apache.openjpa.lib.conf.Configuration;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
|
||||
/**
|
||||
* Implementation of FinderCache for JDBC.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
public class FinderCacheImpl
|
||||
implements FinderCache<ClassMapping, SelectExecutor, Result> {
|
||||
private static final String PATTERN_SEPARATOR = "\\;";
|
||||
private static final String EXLUDED_BY_USER = "Excluded by user";
|
||||
|
||||
private final Map<ClassMapping,
|
||||
FinderQuery<ClassMapping, SelectExecutor, Result>> _delegate;
|
||||
// Key: class name Value: Reason why excluded
|
||||
private final Map<String, String> _uncachables;
|
||||
private List<String> _exclusionPatterns;
|
||||
private QueryStatistics<FinderQuery<ClassMapping,SelectExecutor,Result>>
|
||||
_stats;
|
||||
private ReentrantLock _lock = new ReentrantLock();
|
||||
private Log _log;
|
||||
private Localizer _loc = Localizer.forPackage(FinderCacheImpl.class);
|
||||
|
||||
|
||||
public FinderCacheImpl() {
|
||||
_delegate = new HashMap<ClassMapping,
|
||||
FinderQuery<ClassMapping, SelectExecutor, Result>>();
|
||||
_uncachables = new HashMap<String, String>();
|
||||
_stats = new QueryStatistics.Default<FinderQuery<ClassMapping,
|
||||
SelectExecutor,Result>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map-oriented view of the cache.
|
||||
*
|
||||
* @return a map of the query string with class names as key.
|
||||
*/
|
||||
public Map<String, String> getMapView() {
|
||||
lock();
|
||||
try {
|
||||
Map<String, String> view = new TreeMap<String, String>();
|
||||
for (ClassMapping mapping : _delegate.keySet())
|
||||
view.put(mapping.getDescribedType().getName(),
|
||||
_delegate.get(mapping).getQueryString());
|
||||
return view;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets basic statistics of execution and hit count of finder queries.
|
||||
*/
|
||||
public QueryStatistics<FinderQuery<ClassMapping,SelectExecutor,Result>>
|
||||
getStatistics() {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the finder query for the given mapping. The get operation can be
|
||||
* controlled by FetchConfiguration hints.
|
||||
* {@link QueryHints#HINT_IGNORE_FINDER HINT_IGNORE_FINDER} will ignore
|
||||
* any cached finder that may exist in this cache and will return null.
|
||||
* {@link QueryHints#HINT_INVALIDATE_FINDER HINT_INVALIDATE_FINDER} will
|
||||
* invalidate any cached finder that may exist in this cache and will return
|
||||
* null.
|
||||
*
|
||||
*/
|
||||
public FinderQuery<ClassMapping,SelectExecutor,Result>
|
||||
get(ClassMapping mapping, FetchConfiguration fetch) {
|
||||
boolean ignore = isHinted(fetch, QueryHints.HINT_IGNORE_FINDER);
|
||||
boolean invalidate = isHinted(fetch, QueryHints.HINT_INVALIDATE_FINDER);
|
||||
if (invalidate)
|
||||
invalidate(mapping);
|
||||
if (ignore)
|
||||
return null;
|
||||
FinderQuery<ClassMapping, SelectExecutor, Result> result =
|
||||
_delegate.get(mapping);
|
||||
_stats.recordExecution(result, result != null);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache a Finder Query for the given mapping and select. The put operation
|
||||
* can be controlled by FetchConfiguration hints.
|
||||
* If no entry exists for the given mapping then an attempt is made to
|
||||
* create a new FinderQuery. The attempt, however, may not be successful
|
||||
* because all Selects can not be cached.
|
||||
* @see FinderQueryImpl#newFinder(ClassMapping, Select).
|
||||
*
|
||||
* If a entry for the given mapping exists then the value of
|
||||
* {@link QueryHints#HINT_RECACHE_FINDER HINT_RECACHE_FINDER} hint
|
||||
* determines whether the existing entry is returned or a new FinderQuery
|
||||
* with the given argument overwrites the existing one.
|
||||
*
|
||||
* @param mapping the class for which the finder is to be cached
|
||||
* @param select the finder query
|
||||
* @param fetch may contain hints to control cache operation
|
||||
*/
|
||||
public FinderQuery<ClassMapping, SelectExecutor, Result> cache
|
||||
(ClassMapping mapping, SelectExecutor select, FetchConfiguration fetch) {
|
||||
lock();
|
||||
try {
|
||||
boolean recache = isHinted(fetch, QueryHints.HINT_RECACHE_FINDER);
|
||||
if (isExcluded(mapping)) {
|
||||
return recache ? put(mapping, select) : null;
|
||||
}
|
||||
if (_delegate.containsKey(mapping)) {
|
||||
return recache ? put(mapping, select) : _delegate.get(mapping);
|
||||
}
|
||||
return put(mapping, select);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and puts a FinderQuery in the internal map indexed by the
|
||||
* given ClassMapping.
|
||||
* If a new FinderQuery can not be created for the given Select (because
|
||||
* some Select are not cached), then the mapping is marked invalid.
|
||||
*
|
||||
*/
|
||||
private FinderQuery<ClassMapping, SelectExecutor, Result> put
|
||||
(ClassMapping mapping, SelectExecutor select) {
|
||||
FinderQuery<ClassMapping, SelectExecutor, Result> finder =
|
||||
FinderQueryImpl.newFinder(mapping, select);
|
||||
if (finder != null) {
|
||||
_delegate.put(mapping, finder);
|
||||
if (_log != null && _log.isTraceEnabled())
|
||||
_log.trace(_loc.get("finder-cached", finder));
|
||||
} else {
|
||||
if (_log != null && _log.isWarnEnabled())
|
||||
_log.warn(_loc.get("finder-not-cachable", mapping));
|
||||
invalidate(mapping);
|
||||
}
|
||||
return finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given mapping is excluded from being cached.
|
||||
*/
|
||||
public boolean isExcluded(ClassMapping mapping) {
|
||||
return mapping != null &&
|
||||
isExcluded(mapping.getDescribedType().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the exclusion patterns to find out if the given string matches
|
||||
* any element.
|
||||
*/
|
||||
private boolean isExcluded(String target) {
|
||||
if (_exclusionPatterns != null && _exclusionPatterns.contains(target))
|
||||
return true;
|
||||
return getMatchedExclusionPattern(target) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pattern for exclusion. Any cached finder whose class name
|
||||
* matches the given pattern will be marked invalidated as a side-effect.
|
||||
*/
|
||||
public void addExclusionPattern(String pattern) {
|
||||
lock();
|
||||
try {
|
||||
if (_exclusionPatterns == null)
|
||||
_exclusionPatterns = new ArrayList<String>();
|
||||
_exclusionPatterns.add(pattern);
|
||||
Collection<ClassMapping> invalidMappings = getMatchedKeys(pattern,
|
||||
_delegate.keySet());
|
||||
if (!invalidMappings.isEmpty()
|
||||
&& _log != null && _log.isInfoEnabled())
|
||||
_log.info(_loc.get("finder-add-pattern", pattern,
|
||||
invalidMappings.size(), invalidMappings));
|
||||
for (ClassMapping invalidMapping : invalidMappings)
|
||||
markUncachable(invalidMapping, pattern);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes a pattern for exclusion. Any query identifier marked as not
|
||||
* cachable due to the given pattern will now be removed from the list of
|
||||
* uncachables as a side-effect.
|
||||
*/
|
||||
public void removeExclusionPattern(String pattern) {
|
||||
lock();
|
||||
try {
|
||||
if (_exclusionPatterns == null)
|
||||
return;
|
||||
_exclusionPatterns.remove(pattern);
|
||||
Collection<String> reborns = getMatchedKeys(pattern,
|
||||
_uncachables.keySet());
|
||||
if (!reborns.isEmpty() && _log != null && _log.isInfoEnabled())
|
||||
_log.info(_loc.get("finder-remove-pattern", pattern,
|
||||
reborns.size(), reborns));
|
||||
for (String rebornKey : reborns)
|
||||
_uncachables.remove(rebornKey);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pattern that matches the given identifier.
|
||||
*/
|
||||
private String getMatchedExclusionPattern(String id) {
|
||||
if (_exclusionPatterns == null || _exclusionPatterns.isEmpty())
|
||||
return null;
|
||||
for (String pattern : _exclusionPatterns)
|
||||
if (matches(pattern, id))
|
||||
return pattern;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the elements of the given set that match the given pattern.
|
||||
*/
|
||||
private Collection<ClassMapping> getMatchedKeys(String pattern,
|
||||
Set<ClassMapping> set) {
|
||||
List<ClassMapping> result = new ArrayList<ClassMapping>();
|
||||
for (ClassMapping entry : set) {
|
||||
if (matches(pattern, entry)) {
|
||||
result.add(entry);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the elements of the given list which match the given pattern.
|
||||
*/
|
||||
private Collection<String> getMatchedKeys(String pattern,
|
||||
Collection<String> coll) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (String key : coll) {
|
||||
if (matches(pattern, key)) {
|
||||
result.add(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean matches(String pattern, ClassMapping mapping) {
|
||||
return matches(pattern, mapping.getDescribedType().getName());
|
||||
}
|
||||
|
||||
boolean matches(String pattern, String target) {
|
||||
return target != null && (target.equals(pattern)
|
||||
|| target.matches(pattern));
|
||||
}
|
||||
|
||||
public boolean invalidate(ClassMapping mapping) {
|
||||
lock();
|
||||
try {
|
||||
if (_log.isTraceEnabled())
|
||||
_log.trace(_loc.get("finder-invalidate", mapping));
|
||||
return _delegate.remove(mapping) != null;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
|
||||
ClassMapping mapping) {
|
||||
return markUncachable(mapping.getDescribedType().getName());
|
||||
}
|
||||
|
||||
public FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
|
||||
String id) {
|
||||
return markUncachable(id, EXLUDED_BY_USER);
|
||||
}
|
||||
|
||||
private FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
|
||||
String cls, String reason) {
|
||||
lock();
|
||||
try {
|
||||
boolean excludedByUser = _uncachables.get(cls) == EXLUDED_BY_USER;
|
||||
if (!excludedByUser)
|
||||
_uncachables.put(cls, reason);
|
||||
if (_log != null && _log.isInfoEnabled()) {
|
||||
if (excludedByUser)
|
||||
_log.info(_loc.get("finder-uncache-strong", cls));
|
||||
else
|
||||
_log.info(_loc.get("finder-uncache-weak", cls,
|
||||
reason));
|
||||
}
|
||||
return _delegate.remove(searchMappingByName(cls));
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
|
||||
ClassMapping mapping, String reason) {
|
||||
lock();
|
||||
try {
|
||||
String cls = mapping.getDescribedType().getName();
|
||||
boolean excludedByUser = _uncachables.get(cls) == EXLUDED_BY_USER;
|
||||
if (!excludedByUser)
|
||||
_uncachables.put(cls, reason);
|
||||
if (_log != null && _log.isInfoEnabled()) {
|
||||
if (excludedByUser)
|
||||
_log.info(_loc.get("finder-uncache-strong", cls));
|
||||
else
|
||||
_log.info(_loc.get("finder-uncache-weak", cls, reason));
|
||||
}
|
||||
return _delegate.remove(mapping);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
ClassMapping searchMappingByName(String cls) {
|
||||
for (ClassMapping mapping : _delegate.keySet())
|
||||
if (matches(cls, mapping))
|
||||
return mapping;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setExcludes(String excludes) {
|
||||
lock();
|
||||
try {
|
||||
if (StringUtils.isEmpty(excludes))
|
||||
return;
|
||||
if (_exclusionPatterns == null)
|
||||
_exclusionPatterns = new ArrayList<String>();
|
||||
String[] patterns = excludes.split(PATTERN_SEPARATOR);
|
||||
for (String pattern : patterns)
|
||||
addExclusionPattern(pattern);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getExcludes() {
|
||||
return (List<String>)_exclusionPatterns == null
|
||||
? Collections.EMPTY_LIST
|
||||
: Collections.unmodifiableList(_exclusionPatterns);
|
||||
}
|
||||
|
||||
boolean isHinted(FetchConfiguration fetch, String hint) {
|
||||
if (fetch == null)
|
||||
return false;
|
||||
Object result = fetch.getHint(hint);
|
||||
return result != null && "true".equalsIgnoreCase(result.toString());
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if (_lock != null)
|
||||
_lock.lock();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (_lock != null && _lock.isLocked())
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Configuration contract
|
||||
// ----------------------------------------------------
|
||||
public void startConfiguration() {
|
||||
}
|
||||
|
||||
public void setConfiguration(Configuration conf) {
|
||||
_log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
||||
}
|
||||
|
||||
public void endConfiguration() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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 java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||
import org.apache.openjpa.jdbc.meta.Joinable;
|
||||
import org.apache.openjpa.jdbc.schema.Column;
|
||||
import org.apache.openjpa.jdbc.sql.LogicalUnion;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
||||
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||
import org.apache.openjpa.jdbc.sql.SelectImpl;
|
||||
import org.apache.openjpa.jdbc.sql.Union;
|
||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||
import org.apache.openjpa.kernel.FinderQuery;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreManager;
|
||||
import org.apache.openjpa.util.ApplicationIds;
|
||||
import org.apache.openjpa.util.Id;
|
||||
|
||||
import serp.util.Numbers;
|
||||
|
||||
/**
|
||||
* Implements Finder Query identified by ClassMappping for SelectExecutor that
|
||||
* can be executed to generate Result.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class FinderQueryImpl
|
||||
implements FinderQuery<ClassMapping, SelectExecutor, Result> {
|
||||
private final ClassMapping _mapping;
|
||||
private final SelectImpl _select;
|
||||
private final Column[] _pkCols;
|
||||
private final Joinable[] _joins;
|
||||
private final SQLBuffer _buffer;
|
||||
private final String _sql;
|
||||
|
||||
/**
|
||||
* Attempts to construct a FinderQuery from the given Select for the given
|
||||
* mapping. The given Select may not be amenable for caching and then a null
|
||||
* value is returned.
|
||||
*/
|
||||
static FinderQueryImpl newFinder(ClassMapping mapping,
|
||||
SelectExecutor select) {
|
||||
SelectImpl impl = extractImplementation(select);
|
||||
if (impl == null)
|
||||
return null;
|
||||
SQLBuffer buffer = impl.getSQL();
|
||||
Column[] pkCols = mapping.getPrimaryKeyColumns();
|
||||
boolean canCache = pkCols.length == buffer.getParameters().size();
|
||||
return (canCache)
|
||||
? new FinderQueryImpl(mapping, impl, buffer) : null;
|
||||
}
|
||||
|
||||
private FinderQueryImpl(ClassMapping mapping, SelectImpl select,
|
||||
SQLBuffer buffer) {
|
||||
super();
|
||||
_mapping = mapping;
|
||||
_select = select;
|
||||
_buffer = buffer;
|
||||
_sql = _buffer.getSQL();
|
||||
_pkCols = _mapping.getPrimaryKeyColumns();
|
||||
_joins = new Joinable[_pkCols.length];
|
||||
for (int i = 0; i < _pkCols.length; i++)
|
||||
_joins[i] = _mapping.assertJoinable(_pkCols[i]);
|
||||
}
|
||||
|
||||
public ClassMapping getIdentifier() {
|
||||
return _mapping;
|
||||
}
|
||||
|
||||
public SelectExecutor getDelegate() {
|
||||
return _select;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return _sql;
|
||||
}
|
||||
|
||||
private Object[] getPKValues(OpenJPAStateManager sm, JDBCStore store) {
|
||||
Object[] pks = null;
|
||||
Object oid = sm.getObjectId();
|
||||
if (_mapping.getIdentityType() == ClassMapping.ID_APPLICATION)
|
||||
pks = ApplicationIds.toPKValues(oid, _mapping);
|
||||
|
||||
Object[] val = new Object[_pkCols.length];
|
||||
int count = 0;
|
||||
for (int i = 0; i < _pkCols.length; i++, count++) {
|
||||
if (pks == null)
|
||||
val[0] = (oid == null)
|
||||
? null
|
||||
: Numbers.valueOf(((Id) oid).getId());
|
||||
else {
|
||||
val[i] = pks[_mapping.getField(_joins[i].getFieldIndex()).
|
||||
getPrimaryKeyIndex()];
|
||||
val[i] = _joins[i].getJoinValue(val[i], _pkCols[i], store);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public Result execute(OpenJPAStateManager sm, StoreManager store,
|
||||
FetchConfiguration fetch) {
|
||||
boolean forUpdate = false;
|
||||
Connection conn = ((JDBCStore)store).getConnection();
|
||||
PreparedStatement stmnt = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
stmnt = conn.prepareStatement(_sql);
|
||||
Object[] params = getPKValues(sm, (JDBCStore)store);
|
||||
int i = 0;
|
||||
for (Object o : params) {
|
||||
stmnt.setObject(++i, o);
|
||||
}
|
||||
rs = stmnt.executeQuery();
|
||||
return _select.getEagerResult(conn, stmnt, rs, (JDBCStore)store,
|
||||
(JDBCFetchConfiguration)fetch, forUpdate, _buffer);
|
||||
} catch (SQLException se) {
|
||||
if (stmnt != null)
|
||||
try { stmnt.close(); } catch (SQLException se2) {}
|
||||
try { conn.close(); } catch (SQLException se2) {}
|
||||
throw new RuntimeException(se);
|
||||
}
|
||||
}
|
||||
|
||||
private static SelectImpl extractImplementation(SelectExecutor selector) {
|
||||
if (selector == null || selector.hasMultipleSelects())
|
||||
return null;
|
||||
if (selector instanceof SelectImpl)
|
||||
return (SelectImpl)selector;
|
||||
if (selector instanceof LogicalUnion.UnionSelect)
|
||||
return ((LogicalUnion.UnionSelect)selector).getDelegate();
|
||||
if (selector instanceof Union)
|
||||
return extractImplementation(((Union)selector).getSelects()[0]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return _mapping + ": [" + getQueryString() + "]";
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.openjpa.enhance.PersistenceCapable;
|
||||
|
@ -48,7 +49,10 @@ import org.apache.openjpa.jdbc.sql.SQLFactory;
|
|||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||
import org.apache.openjpa.jdbc.sql.Union;
|
||||
import org.apache.openjpa.kernel.BrokerImpl;
|
||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.kernel.FinderQuery;
|
||||
import org.apache.openjpa.kernel.LockManager;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.PCState;
|
||||
|
@ -483,13 +487,18 @@ public class JDBCStoreManager
|
|||
private Result getInitializeStateResult(OpenJPAStateManager sm,
|
||||
ClassMapping mapping, JDBCFetchConfiguration fetch, int subs)
|
||||
throws SQLException {
|
||||
FinderQueryImpl fq = getFinder(mapping, fetch);
|
||||
if (fq != null)
|
||||
return fq.execute(sm, this, fetch);
|
||||
Select sel = _sql.newSelect();
|
||||
if (!select(sel, mapping, subs, sm, null, fetch,
|
||||
JDBCFetchConfiguration.EAGER_JOIN, true, false))
|
||||
return null;
|
||||
sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
|
||||
sel.setExpectedResultCount(1, false);
|
||||
return sel.execute(this, fetch);
|
||||
Result result = sel.execute(this, fetch);
|
||||
cacheFinder(mapping, sel, fetch);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -499,6 +508,9 @@ public class JDBCStoreManager
|
|||
private Result getInitializeStateUnionResult(final OpenJPAStateManager sm,
|
||||
ClassMapping mapping, final ClassMapping[] mappings,
|
||||
final JDBCFetchConfiguration fetch) throws SQLException {
|
||||
FinderQueryImpl fq = getFinder(mapping, fetch);
|
||||
if (fq != null)
|
||||
return fq.execute(sm, this, fetch);
|
||||
final JDBCStoreManager store = this;
|
||||
final int eager = Math.min(fetch.getEagerFetchMode(),
|
||||
JDBCFetchConfiguration.EAGER_JOIN);
|
||||
|
@ -514,7 +526,9 @@ public class JDBCStoreManager
|
|||
sel.wherePrimaryKey(sm.getObjectId(), mappings[i], store);
|
||||
}
|
||||
});
|
||||
return union.execute(this, fetch);
|
||||
Result result = union.execute(this, fetch);
|
||||
cacheFinder(mapping, union, fetch);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1349,6 +1363,24 @@ public class JDBCStoreManager
|
|||
private void afterExecuteStatement(Statement stmnt) {
|
||||
_stmnts.remove(stmnt);
|
||||
}
|
||||
|
||||
FinderQueryImpl getFinder(ClassMapping mapping, FetchConfiguration fetch) {
|
||||
FinderCache cache = getFinderCache();
|
||||
return cache == null
|
||||
? null : (FinderQueryImpl)cache.get(mapping, fetch);
|
||||
}
|
||||
|
||||
boolean cacheFinder(ClassMapping mapping, SelectExecutor select,
|
||||
FetchConfiguration fetch) {
|
||||
FinderCache cache = getFinderCache();
|
||||
return cache != null && cache.cache(mapping, select, fetch) != null;
|
||||
}
|
||||
|
||||
FinderCache getFinderCache() {
|
||||
return (((BrokerImpl)getContext()).getCacheFinderQuery())
|
||||
? getConfiguration().getFinderCacheInstance() : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connection returned to client code. Makes sure its wrapped connection
|
||||
|
|
|
@ -199,10 +199,9 @@ public class LogicalUnion
|
|||
}
|
||||
|
||||
public boolean hasMultipleSelects() {
|
||||
for (UnionSelect sel : sels)
|
||||
if (sel.hasMultipleSelects())
|
||||
return true;
|
||||
return false;
|
||||
if (sels != null && sels.length > 1)
|
||||
return true;
|
||||
return sels[0].hasMultipleSelects();
|
||||
}
|
||||
|
||||
public int getCount(JDBCStore store)
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.apache.openjpa.kernel.BrokerFactory;
|
|||
import org.apache.openjpa.kernel.BrokerImpl;
|
||||
import org.apache.openjpa.kernel.ConnectionRetainModes;
|
||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.kernel.InverseManager;
|
||||
import org.apache.openjpa.kernel.LockManager;
|
||||
import org.apache.openjpa.kernel.PreparedQueryCache;
|
||||
|
@ -1603,6 +1604,26 @@ public interface OpenJPAConfiguration
|
|||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public void setQuerySQLCache(String config);
|
||||
|
||||
public void setQuerySQLCache(String config);
|
||||
|
||||
/**
|
||||
* Get the cache of finder queries.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public FinderCache getFinderCacheInstance();
|
||||
|
||||
/**
|
||||
* Get the string configuration of the finder cache.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public String getFinderCache();
|
||||
|
||||
/**
|
||||
* Set the finder cache from a string configuration.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public void setFinderCache(String cache);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.openjpa.event.RemoteCommitProvider;
|
|||
import org.apache.openjpa.kernel.AutoClear;
|
||||
import org.apache.openjpa.kernel.BrokerImpl;
|
||||
import org.apache.openjpa.kernel.ConnectionRetainModes;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.kernel.InverseManager;
|
||||
import org.apache.openjpa.kernel.LockLevels;
|
||||
import org.apache.openjpa.kernel.LockManager;
|
||||
|
@ -148,6 +149,7 @@ public class OpenJPAConfigurationImpl
|
|||
public CacheMarshallersValue cacheMarshallerPlugins;
|
||||
public BooleanValue eagerInitialization;
|
||||
public PluginValue preparedQueryCachePlugin;
|
||||
public PluginValue finderCachePlugin;
|
||||
public ObjectValue specification;
|
||||
public IntValue queryTimeout;
|
||||
|
||||
|
@ -1550,5 +1552,18 @@ public class OpenJPAConfigurationImpl
|
|||
return (PreparedQueryCache)preparedQueryCachePlugin.get();
|
||||
}
|
||||
|
||||
|
||||
public void setFinderCache(String finderCache) {
|
||||
finderCachePlugin.setString(finderCache);
|
||||
}
|
||||
|
||||
public String getFinderCache() {
|
||||
return finderCachePlugin.getString();
|
||||
}
|
||||
|
||||
public FinderCache getFinderCacheInstance() {
|
||||
if (finderCachePlugin.get() == null) {
|
||||
finderCachePlugin.instantiate(FinderCache.class, this);
|
||||
}
|
||||
return (FinderCache)finderCachePlugin.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,6 +225,7 @@ public class BrokerImpl
|
|||
private boolean _detachedNew = true;
|
||||
private boolean _orderDirty = false;
|
||||
private boolean _cachePreparedQuery = true;
|
||||
private boolean _cacheFinderQuery = true;
|
||||
|
||||
|
||||
// Map of properties whose values have been changed
|
||||
|
@ -4890,4 +4891,23 @@ public class BrokerImpl
|
|||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getCacheFinderQuery() {
|
||||
lock();
|
||||
try {
|
||||
return _cacheFinderQuery
|
||||
&& _conf.getFinderCacheInstance() != null;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheFinderQuery(boolean flag) {
|
||||
lock();
|
||||
try {
|
||||
_cachePreparedQuery = flag;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openjpa.lib.conf.Configurable;
|
||||
|
||||
/**
|
||||
* A cache to create and maintain {@link FinderQuery finder queries}.
|
||||
*
|
||||
* A finder query is a query to find instance of a given class by its primary
|
||||
* identity. This cache maintains finder queries by generic identifier of
|
||||
* parameterized type K.
|
||||
*
|
||||
* A cached query by an identifier of parameterized type K.
|
||||
*
|
||||
* A query can be cached by an identifier and value represented by parameterized
|
||||
* type V. Caching results in creating a new instance of FinderQuery FQ from the
|
||||
* value V such that FQ can be executed to return a result of parameterized type
|
||||
* R. A request to cache may not be successful if this cache determines the
|
||||
* value V to be not cachable.
|
||||
*
|
||||
* Both get() and put() operations can be controlled by the hints associated
|
||||
* with FetchConfiguration.
|
||||
*
|
||||
* The target database query FQ associated to a cached finder query F
|
||||
* <em>may</em> depend upon query execution context such as fetch plan or
|
||||
* lock group. This cache, by design, does not monitor the context or
|
||||
* automatically invalidate an entry when the original query F is executed
|
||||
* with context parameters that affect the target query FQ.
|
||||
*
|
||||
* The user must notify this receiver to invalidate a cached entry when
|
||||
* execution context changes in a way that will modify the resultant database
|
||||
* language query FQ.
|
||||
*
|
||||
* One of the built-in mechanism (available in JPA facade) is to set query hints
|
||||
* to either invalidate the query entirely or ignore the cached version for the
|
||||
* current execution.
|
||||
*
|
||||
* @see QueryHints#HINT_IGNORE_FINDER
|
||||
* @see QueryHints#HINT_INVALIDATE_FINDER
|
||||
* @see QueryHints#HINT_RECACHE_FINDER
|
||||
*
|
||||
* This cache allows customization of whether a query can be cached or not
|
||||
* via either explicit marking of certain classes as non-cachable (which is
|
||||
* irreversible) or addition/removal of exclusion patterns (which is reversible)
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface FinderCache<K,V,R> extends Configurable {
|
||||
/**
|
||||
* Get the FinderQuery for the given key.
|
||||
*
|
||||
* @param key for which the finder is looked up
|
||||
* @param fecth may contain hints to control lookup operation
|
||||
*
|
||||
* @return FinderQuery for the given mapping.
|
||||
*/
|
||||
public FinderQuery<K,V,R> get(K key, FetchConfiguration fetch);
|
||||
|
||||
/**
|
||||
* Cache a FinderQuery for the given key and value.
|
||||
*
|
||||
* @param key for which the finder is cached.
|
||||
* @param value used to construct the finder query
|
||||
* @param fetch may contain hints to control cache operation.
|
||||
*
|
||||
* @return the finder query that has been cached. It may be newly
|
||||
* constructed or an existing query. If the given key-value can not be
|
||||
* cached, then return null.
|
||||
*/
|
||||
public FinderQuery<K,V,R> cache(K key, V value, FetchConfiguration fetch);
|
||||
|
||||
/**
|
||||
* Get a map view of the cached entries as strings.
|
||||
*/
|
||||
public Map<String, String> getMapView();
|
||||
|
||||
/**
|
||||
* Remove the FinderQuery for the given key from this cache.
|
||||
*/
|
||||
public boolean invalidate(K key);
|
||||
|
||||
/**
|
||||
* Marks the given key as not amenable to caching.
|
||||
* Explicit marking helps to avoid repeated computational cost of
|
||||
* determining whether finder for a key can be cached or not.
|
||||
*
|
||||
* Explicit marking can not be reversed by removal of exclusion patterns.
|
||||
*
|
||||
* @return finder query for the given class if it had been cached before.
|
||||
* null otherwise.
|
||||
*/
|
||||
public FinderQuery<K,V,R> markUncachable(K key);
|
||||
|
||||
/**
|
||||
* Affirms if the given key matches any of the exclusion patterns.
|
||||
*/
|
||||
public boolean isExcluded(K key);
|
||||
|
||||
/**
|
||||
* Gets the excluded stringified keys.
|
||||
*/
|
||||
public List<String> getExcludes();
|
||||
|
||||
/**
|
||||
* Adds the given pattern to the list of excluded patterns. Any existing
|
||||
* cache entry whose key matches the given pattern will be marked
|
||||
* non-cachable in a reversible manner.
|
||||
*/
|
||||
public void addExclusionPattern(String pattern);
|
||||
|
||||
/**
|
||||
* Removes the given pattern from the list of excluded patterns.
|
||||
* Any excluded entry that matches the given pattern can now be cached
|
||||
* again, unless it has been marked non-cachable explicitly.
|
||||
*/
|
||||
public void removeExclusionPattern(String pattern);
|
||||
|
||||
/**
|
||||
* Gets the simple statistics for executed finder queries.
|
||||
*/
|
||||
public QueryStatistics<FinderQuery<K,V,R>> getStatistics();
|
||||
}
|
|
@ -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.kernel;
|
||||
|
||||
/**
|
||||
* A finder query is a query for an instance of a class by its primary key.
|
||||
* A finder query is parameterized by the type of key K, type of value V and
|
||||
* type of result R.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
public interface FinderQuery<K,V,R> {
|
||||
|
||||
/**
|
||||
* Gets the identifier of this receiver.
|
||||
*
|
||||
*/
|
||||
public K getIdentifier();
|
||||
|
||||
/**
|
||||
* Gets the value to which this receiver delegates its execution.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public V getDelegate();
|
||||
|
||||
/**
|
||||
* Execute the query for a given instance.
|
||||
*
|
||||
* @param sm the StateManager for a given instance carrying the primary key
|
||||
* values.
|
||||
* @param store the data store against which the query is to be executed.
|
||||
* @param fetch fetch parameters
|
||||
*
|
||||
* @return the result of execution.
|
||||
*
|
||||
*/
|
||||
public R execute(OpenJPAStateManager sm, StoreManager store,
|
||||
FetchConfiguration fetch);
|
||||
|
||||
/**
|
||||
* Gets the query string.
|
||||
*
|
||||
*/
|
||||
public String getQueryString();
|
||||
}
|
|
@ -75,4 +75,21 @@ public interface QueryHints {
|
|||
public static final String HINT_IGNORE_PREPARED_QUERY =
|
||||
"openjpa.hint.IgnorePreparedQuery";
|
||||
|
||||
/**
|
||||
* A directive to ignore any cached finder query for find() operation.
|
||||
* The cached entry, if any, remains in the cache.
|
||||
*/
|
||||
public static final String HINT_IGNORE_FINDER = "openjpa.hint.IgnoreFinder";
|
||||
|
||||
/**
|
||||
* A directive to invalidate any cached finder query.
|
||||
*/
|
||||
public static final String HINT_INVALIDATE_FINDER =
|
||||
"openjpa.hint.InvalidateFinder";
|
||||
|
||||
/**
|
||||
* A directive to overwrite a cached finder query by a new query.
|
||||
*/
|
||||
public static final String HINT_RECACHE_FINDER =
|
||||
"openjpa.hint.RecacheFinder";
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ public class TxRollbackEntity
|
|||
@Basic
|
||||
private String name;
|
||||
|
||||
protected TxRollbackEntity() {
|
||||
this("?");
|
||||
}
|
||||
|
||||
public TxRollbackEntity(String name)
|
||||
{
|
||||
this.name = name;
|
||||
|
|
|
@ -77,7 +77,9 @@ public class TestDefaultInheritanceStrategy
|
|||
BaseClass4.class, SubclassG.class,
|
||||
BaseClass5.class, MidClass2.class, SubclassH.class,
|
||||
AbstractClass.class, SubclassI.class, SubclassJ.class,
|
||||
BaseClass6.class, SubclassK.class);
|
||||
BaseClass6.class, SubclassK.class,
|
||||
"openjpa.jdbc.FinderCache", "true",
|
||||
CLEAR_TABLES);
|
||||
}
|
||||
|
||||
private Class[] classArray(Class... classes) {
|
||||
|
@ -550,6 +552,43 @@ public class TestDefaultInheritanceStrategy
|
|||
|
||||
em.close();
|
||||
}
|
||||
|
||||
public void testFinder() {
|
||||
EntityManager em = emf.createEntityManager();
|
||||
SubclassK sck = new SubclassK();
|
||||
sck.setId(479);
|
||||
sck.setClassKName("SubclassKName");
|
||||
sck.setMidClass3Name("SubclassKMidClass3Name");
|
||||
sck.setName("SubclassKBaseClass6Name");
|
||||
|
||||
BaseClass6 bk6 = new BaseClass6();
|
||||
bk6.setId(302);
|
||||
bk6.setName("BaseClass6Name");
|
||||
|
||||
SubclassI sci = new SubclassI();
|
||||
sci.setId(109);
|
||||
sci.setClassIName("SubclassIName");
|
||||
sci.setName("SubclassIBaseClassName");
|
||||
|
||||
SubclassJ scj = new SubclassJ();
|
||||
scj.setId(238);
|
||||
scj.setClassJName("SubclassJName");
|
||||
scj.setName("SubclassJBaseClassName");
|
||||
|
||||
em.getTransaction().begin();
|
||||
em.persist(sck);
|
||||
em.persist(bk6);
|
||||
em.persist(sci);
|
||||
em.persist(scj);
|
||||
em.getTransaction().commit();
|
||||
|
||||
em.clear();
|
||||
|
||||
verifyInheritanceFinderResult(em, SubclassK.class, 479);
|
||||
verifyInheritanceFinderResult(em, BaseClass6.class, 479, 302);
|
||||
verifyInheritanceFinderResult(em, SubclassI.class, 109);
|
||||
verifyInheritanceFinderResult(em, SubclassJ.class, 238);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a table contains the specified number of entries
|
||||
|
@ -637,4 +676,16 @@ public class TestDefaultInheritanceStrategy
|
|||
assertTrue("Returned expected entities",
|
||||
count == expectedValues.length);
|
||||
}
|
||||
|
||||
private void verifyInheritanceFinderResult(EntityManager em,
|
||||
Class entityType, int... ids) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
em.clear();
|
||||
for (int id : ids) {
|
||||
Object pc = em.find(entityType, id);
|
||||
assertTrue(entityType.isAssignableFrom(pc.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.apache.openjpa.persistence.jdbc.sqlcache;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
@ -10,7 +9,6 @@ import javax.persistence.InheritanceType;
|
|||
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
|
||||
public class Merchandise {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
public long getId() {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.openjpa.persistence.jdbc.sqlcache;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
@ -32,6 +33,7 @@ import javax.persistence.OneToOne;
|
|||
|
||||
@Entity
|
||||
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
|
||||
@DiscriminatorValue("PERSON")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="JPQLNamedSelectPositionalParameter",
|
||||
query="select p from Person p where p.firstName=?2" +
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 javax.persistence.EntityManager;
|
||||
|
||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||
import org.apache.openjpa.kernel.FinderCache;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
|
||||
/**
|
||||
* Basic test to check FinderQuery caches.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class TestFinderCache extends SingleEMFTestCase {
|
||||
public static final long[] BOOK_IDS = {1000, 2000, 3000};
|
||||
public static final String[] BOOK_NAMES = {"Argumentative Indian", "Tin Drum", "Blink"};
|
||||
public static final long[] CD_IDS = {1001, 2001, 3001};
|
||||
public static final String[] CD_LABELS = {"Beatles", "Sinatra", "Don't Rock My Boat"};
|
||||
|
||||
void createTestData() {
|
||||
EntityManager em = emf.createEntityManager();
|
||||
em.getTransaction().begin();
|
||||
for (int i = 0; i < BOOK_IDS.length; i++) {
|
||||
Book book = new Book();
|
||||
book.setId(BOOK_IDS[i]);
|
||||
book.setTitle(BOOK_NAMES[i]);
|
||||
em.persist(book);
|
||||
}
|
||||
for (int i = 0; i < CD_IDS.length; i++) {
|
||||
CD cd = new CD();
|
||||
cd.setId(CD_IDS[i]);
|
||||
cd.setLabel(CD_LABELS[i]);
|
||||
em.persist(cd);
|
||||
}
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
public void setUp() {
|
||||
super.setUp(CLEAR_TABLES, Merchandise.class, Book.class, CD.class,
|
||||
Author.class, Person.class, Singer.class, Address.class);
|
||||
createTestData();
|
||||
}
|
||||
|
||||
public void testFinder() {
|
||||
int N = 200;
|
||||
|
||||
emf = createEMF("openjpa.jdbc.FinderCache", "false");
|
||||
run(1, Book.class, BOOK_IDS); // for warmup
|
||||
|
||||
assertNull(getCache());
|
||||
long without = run(N, Book.class, BOOK_IDS);
|
||||
|
||||
emf = createEMF("openjpa.jdbc.FinderCache", "true");
|
||||
assertNotNull(getCache());
|
||||
long with = run(N, Book.class, BOOK_IDS);
|
||||
|
||||
getCache().getStatistics().dump(System.out);
|
||||
|
||||
long pct = (without-with)*100/without;
|
||||
System.err.println(BOOK_IDS.length*N + " find");
|
||||
System.err.println("with " + with);
|
||||
System.err.println("without " + without);
|
||||
System.err.println("delta " + (pct > 0 ? "+" : "") + pct + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a finder query for each identifiers N times and report the median
|
||||
* execution time.
|
||||
*/
|
||||
<T> long run(int N, Class<T> cls, long[] ids) {
|
||||
EntityManager em = emf.createEntityManager();
|
||||
List<Long> stats = new ArrayList<Long>();
|
||||
for (int n = 0; n < N; n++) {
|
||||
em.clear();
|
||||
long start = System.nanoTime();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
T pc = em.find(cls, ids[i]);
|
||||
assertNotNull(pc);
|
||||
assertTrue(cls.isInstance(pc));
|
||||
}
|
||||
long end = System.nanoTime();
|
||||
stats.add(end-start);
|
||||
}
|
||||
Collections.sort(stats);
|
||||
return stats.get(N/2);
|
||||
}
|
||||
|
||||
FinderCache getCache() {
|
||||
return ((JDBCConfiguration)emf.getConfiguration()).getFinderCacheInstance();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue