From 991cdcae7da3904a932bf9c5c453a6de0685e8c9 Mon Sep 17 00:00:00 2001 From: Michael Dick Date: Fri, 28 Aug 2009 18:25:08 +0000 Subject: [PATCH] OPENJPA-1271: Enable CacheStoreMode and CacheRetrieveMode properties to be set for an EntityManager. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@808981 13f79535-47bb-0310-9956-ffa450edef68 --- .../datacache/DataCacheStoreManager.java | 359 ++++++++-------- .../org/apache/openjpa/kernel/BrokerImpl.java | 43 ++ .../openjpa/kernel/DataCacheRetrieveMode.java | 33 ++ .../openjpa/kernel/DataCacheStoreMode.java | 42 ++ .../openjpa/kernel/DelegatingBroker.java | 35 ++ .../apache/openjpa/kernel/StoreContext.java | 57 +++ .../cache/jpa/AbstractCacheModeTestCase.java | 396 ++++++++++++++++++ .../cache/jpa/AbstractJPACacheTestCase.java | 155 ------- .../cache/jpa/TestCacheModeAll.java | 35 +- .../jpa/TestCacheModeDisableSelective.java | 41 +- .../jpa/TestCacheModeEnableSelective.java | 46 +- .../cache/jpa/TestCacheModeNone.java | 39 +- .../cache/jpa/model/CacheEntity.java | 2 + .../cache/jpa/model/CacheableEntity.java | 10 + .../jpa/model/NegatedCachableEntity.java | 10 + .../jpa/model/NegatedUncacheableEntity.java | 10 + .../cache/jpa/model/UncacheableEntity.java | 10 + .../cache/jpa/model/UnspecifiedEntity.java | 10 + .../cache/jpa/model/XmlCacheableEntity.java | 10 + .../cache/jpa/model/XmlUncacheableEntity.java | 10 + .../persistence/EntityManagerImpl.java | 45 +- 21 files changed, 1058 insertions(+), 340 deletions(-) create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheRetrieveMode.java create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheStoreMode.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractCacheModeTestCase.java delete mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractJPACacheTestCase.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java index 999129ec1..5c462d3de 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java @@ -30,6 +30,8 @@ import java.util.Map; import java.util.Map.Entry; import org.apache.openjpa.enhance.PCDataGenerator; +import org.apache.openjpa.kernel.DataCacheRetrieveMode; +import org.apache.openjpa.kernel.DataCacheStoreMode; import org.apache.openjpa.kernel.DelegatingStoreManager; import org.apache.openjpa.kernel.FetchConfiguration; import org.apache.openjpa.kernel.LockLevels; @@ -54,9 +56,9 @@ public class DataCacheStoreManager extends DelegatingStoreManager { // all the state managers changed in this transaction - private Collection _inserts = null; // statemanagers - private Map _updates = null; // statemanager -> fmd set - private Collection _deletes = null; // statemanagers + private Collection _inserts = null; + private Map _updates = null; + private Collection _deletes = null; // the owning context private StoreContext _ctx = null; @@ -130,127 +132,125 @@ public class DataCacheStoreManager * Update all caches with the committed inserts, updates, and deletes. */ private void updateCaches() { - // map each data cache to the modifications we need to perform - Map modMap = null; - Modifications mods; - OpenJPAStateManager sm; - DataCachePCData data; - DataCache cache; + if(_ctx.getCacheStoreMode() != DataCacheStoreMode.BYPASS ) { + // map each data cache to the modifications we need to perform + Map modMap = null; + Modifications mods; + DataCachePCData data; + DataCache cache; - // create pc datas for inserts - if (_ctx.getPopulateDataCache() && _inserts != null) { - for (Iterator itr = _inserts.iterator(); itr.hasNext();) { - sm = (OpenJPAStateManager) itr.next(); - cache = sm.getMetaData().getDataCache(); - if (cache == null) - continue; + // create pc datas for inserts + if (_ctx.getPopulateDataCache() && _inserts != null) { + for(OpenJPAStateManager sm : _inserts) { + cache = sm.getMetaData().getDataCache(); + if (cache == null) + continue; - if (modMap == null) - modMap = new HashMap(); - mods = getModifications(modMap, cache); - data = newPCData(sm); - data.store(sm); - mods.additions.add(new PCDataHolder(data, sm)); - } - } - - // update pcdatas for updates - Map.Entry entry; - if (_updates != null) { - BitSet fields; - for (Iterator itr = _updates.entrySet().iterator(); - itr.hasNext();) { - entry = (Map.Entry) itr.next(); - sm = (OpenJPAStateManager) entry.getKey(); - fields = (BitSet) entry.getValue(); - - cache = sm.getMetaData().getDataCache(); - if (cache == null) - continue; - - // it's ok not to clone the object that we get from the cache, - // since we're inside the commit() method, so any modifications - // to the underlying cache are valid. If the commit had not - // already succeeded, then we'd want to clone the retrieved - // object. - if (modMap == null) - modMap = new HashMap(); - data = cache.get(sm.getObjectId()); - mods = getModifications(modMap, cache); - - // data should always be non-null, since the object is - // dirty, but maybe it got dropped from the cache in the - // interim - if (data == null) { + if (modMap == null) + modMap = new HashMap(); + mods = getModifications(modMap, cache); data = newPCData(sm); data.store(sm); - mods.newUpdates.add(new PCDataHolder(data, sm)); - } else { - data.store(sm, fields); - mods.existingUpdates.add(new PCDataHolder(data, sm)); + mods.additions.add(new PCDataHolder(data, sm)); } } - } - // remove pcdatas for deletes - if (_deletes != null) { - for (Iterator itr = _deletes.iterator(); itr.hasNext();) { - sm = (OpenJPAStateManager) itr.next(); - cache = sm.getMetaData().getDataCache(); - if (cache == null) - continue; + // update pcdatas for updates + if (_updates != null) { + BitSet fields; + OpenJPAStateManager sm; + for(Map.Entry entry : _updates.entrySet()) { + sm = entry.getKey(); + fields = entry.getValue(); - if (modMap == null) - modMap = new HashMap(); - mods = getModifications(modMap, cache); - mods.deletes.add(sm.getObjectId()); - } - } + cache = sm.getMetaData().getDataCache(); + if (cache == null) { + continue; + } - // notify the caches of the changes - if (modMap != null) { - for (Iterator itr = modMap.entrySet().iterator(); itr.hasNext();) { - entry = (Map.Entry) itr.next(); - cache = (DataCache) entry.getKey(); - mods = (Modifications) entry.getValue(); + // it's ok not to clone the object that we get from the cache, + // since we're inside the commit() method, so any modifications + // to the underlying cache are valid. If the commit had not + // already succeeded, then we'd want to clone the retrieved + // object. + if (modMap == null) + modMap = new HashMap(); + data = cache.get(sm.getObjectId()); + mods = getModifications(modMap, cache); - // make sure we're not caching old versions - cache.writeLock(); - try { - transformToVersionSafePCDatas(cache, mods.additions); - transformToVersionSafePCDatas(cache, mods.newUpdates); - transformToVersionSafePCDatas(cache, mods.existingUpdates); - cache.commit(mods.additions, mods.newUpdates, - mods.existingUpdates, mods.deletes); - } finally { - cache.writeUnlock(); + // data should always be non-null, since the object is + // dirty, but maybe it got dropped from the cache in the + // interim + if (data == null) { + data = newPCData(sm); + data.store(sm); + mods.newUpdates.add(new PCDataHolder(data, sm)); + } else { + data.store(sm, fields); + mods.existingUpdates.add(new PCDataHolder(data, sm)); + } } } - } - // if we were in largeTransaction mode, then we have recorded - // the classes of updated/deleted objects and these now need to be - // evicted - if (_ctx.isTrackChangesByType()) { - evictTypes(_ctx.getDeletedTypes()); - evictTypes(_ctx.getUpdatedTypes()); - } + // remove pcdatas for deletes + if (_deletes != null) { + for(OpenJPAStateManager sm : _deletes) { + cache = sm.getMetaData().getDataCache(); + if (cache == null) + continue; - // and notify the query cache. notify in one batch to reduce synch - QueryCache queryCache = _ctx.getConfiguration(). + if (modMap == null) + modMap = new HashMap(); + mods = getModifications(modMap, cache); + mods.deletes.add(sm.getObjectId()); + } + } + + // notify the caches of the changes + if (modMap != null) { + for (Iterator itr = modMap.entrySet().iterator(); itr.hasNext();) { + Map.Entry entry = (Map.Entry) itr.next(); + cache = (DataCache) entry.getKey(); + mods = (Modifications) entry.getValue(); + + // make sure we're not caching old versions + cache.writeLock(); + try { + transformToVersionSafePCDatas(cache, mods.additions); + transformToVersionSafePCDatas(cache, mods.newUpdates); + transformToVersionSafePCDatas(cache, mods.existingUpdates); + cache.commit(mods.additions, mods.newUpdates, + mods.existingUpdates, mods.deletes); + } finally { + cache.writeUnlock(); + } + } + } + + // if we were in largeTransaction mode, then we have recorded + // the classes of updated/deleted objects and these now need to be + // evicted + if (_ctx.isTrackChangesByType()) { + evictTypes(_ctx.getDeletedTypes()); + evictTypes(_ctx.getUpdatedTypes()); + } + + // and notify the query cache. notify in one batch to reduce synch + QueryCache queryCache = _ctx.getConfiguration(). getDataCacheManagerInstance().getSystemQueryCache(); - if (queryCache != null) { - Collection pers = _ctx.getPersistedTypes(); - Collection del = _ctx.getDeletedTypes(); - Collection up = _ctx.getUpdatedTypes(); - int size = pers.size() + del.size() + up.size(); - if (size > 0) { - Collection types = new ArrayList(size); - types.addAll(pers); - types.addAll(del); - types.addAll(up); - queryCache.onTypesChanged(new TypesChangedEvent(this, types)); - } + if (queryCache != null) { + Collection pers = _ctx.getPersistedTypes(); + Collection del = _ctx.getDeletedTypes(); + Collection up = _ctx.getUpdatedTypes(); + int size = pers.size() + del.size() + up.size(); + if (size > 0) { + Collection types = new ArrayList(size); + types.addAll(pers); + types.addAll(del); + types.addAll(up); + queryCache.onTypesChanged(new TypesChangedEvent(this, types)); + } + } } } @@ -333,52 +333,74 @@ public class DataCacheStoreManager return super.syncVersion(sm, edata); } - public boolean initialize(OpenJPAStateManager sm, PCState state, - FetchConfiguration fetch, Object edata) { + public boolean initialize(OpenJPAStateManager sm, PCState state, FetchConfiguration fetch, Object edata) { + boolean rval; DataCache cache = sm.getMetaData().getDataCache(); - if (cache == null || sm.isEmbedded()) - return super.initialize(sm, state, fetch, edata); - - DataCachePCData data = cache.get(sm.getObjectId()); - if (data != null && !isLocking(fetch)) { - //### the 'data.type' access here probably needs to be - //### addressed for bug 511 - sm.initialize(data.getType(), state); - data.load(sm, fetch, edata); - return true; + if (cache == null || sm.isEmbedded() || _ctx.getCacheRetrieveMode() == DataCacheRetrieveMode.BYPASS + || _ctx.getCacheStoreMode() == DataCacheStoreMode.REFRESH) { + // save the return value and return later in case we need to update the cache) + rval = super.initialize(sm, state, fetch, edata); } - // initialize from store manager - if (!super.initialize(sm, state, fetch, edata)) - return false; - if (!_ctx.getPopulateDataCache()) - return true; + else { + DataCachePCData data = cache.get(sm.getObjectId()); + if (data != null && !isLocking(fetch)) { + //### the 'data.type' access here probably needs to be + //### addressed for bug 511 + sm.initialize(data.getType(), state); + data.load(sm, fetch, edata); + return true; + } + // initialize from store manager + if (!super.initialize(sm, state, fetch, edata)) { + return false; + } + rval = true; // same as rval = super.initialize(...) + } + + // update the cache if configured appropriately. + if (_ctx.getCacheStoreMode() == DataCacheStoreMode.REFRESH && _ctx.getPopulateDataCache()) { + cacheStateManager(cache, sm); + } + return rval; + } + + private void cacheStateManager(DataCache cache, OpenJPAStateManager sm) { + if(sm.isFlushed()) { + return; + } // make sure that we're not trying to cache an old version cache.writeLock(); try { - data = cache.get(sm.getObjectId()); - if (data != null && compareVersion(sm, sm.getVersion(), - data.getVersion()) == VERSION_EARLIER) - return true; + DataCachePCData data = cache.get(sm.getObjectId()); + if (data != null && compareVersion(sm, sm.getVersion(), data.getVersion()) == VERSION_EARLIER) { + return; + } // cache newly loaded info. It is safe to cache data frorm // initialize() because this method is only called upon // initial load of the data. - if (data == null) + boolean isNew = data == null; + if (isNew) { data = newPCData(sm); + } data.store(sm); - cache.put(data); + if(isNew) { + cache.put(data); + } + else { + cache.update(data); + } } finally { cache.writeUnlock(); } - return true; } public boolean load(OpenJPAStateManager sm, BitSet fields, FetchConfiguration fetch, int lockLevel, Object edata) { DataCache cache = sm.getMetaData().getDataCache(); - if (cache == null || sm.isEmbedded()) + if (cache == null || sm.isEmbedded() || _ctx.getCacheRetrieveMode() == DataCacheRetrieveMode.BYPASS) return super.load(sm, fields, fetch, lockLevel, edata); DataCachePCData data = cache.get(sm.getObjectId()); @@ -392,33 +414,11 @@ public class DataCacheStoreManager // so that if the store manager decides to modify it it won't affect us if (!super.load(sm, (BitSet) fields.clone(), fetch, lockLevel, edata)) return false; - if (!_ctx.getPopulateDataCache()) - return true; - // Do not load changes into cache if the instance has been flushed - if (sm.isFlushed()) - return true; - - // make sure that we're not trying to cache an old version - cache.writeLock(); - try { - data = cache.get(sm.getObjectId()); - if (data != null && compareVersion(sm, sm.getVersion(), - data.getVersion()) == VERSION_EARLIER) - return true; - - // cache newly loaded info - boolean isNew = data == null; - if (isNew) - data = newPCData(sm); - data.store(sm, fields); - if (isNew) - cache.put(data); - else - cache.update(data); - } finally { - cache.writeUnlock(); + if (_ctx.getPopulateDataCache()) { + cacheStateManager(cache, sm); } return true; + } public Collection loadAll(Collection sms, PCState state, int load, @@ -430,7 +430,7 @@ public class DataCacheStoreManager return super.loadAll(sms, state, load, fetch, edata); } - Map unloaded = null; + Map unloaded = null; List smList = null; Map caches = new HashMap(); OpenJPAStateManager sm; @@ -509,12 +509,12 @@ public class DataCacheStoreManager return failed; // for each loaded instance, merge loaded state into cached data - Map.Entry entry; + boolean isNew; - for (Iterator itr = unloaded.entrySet().iterator(); itr.hasNext();) { - entry = (Map.Entry) itr.next(); - sm = (OpenJPAStateManager) entry.getKey(); - fields = (BitSet) entry.getValue(); + + for(Map.Entry entry : unloaded.entrySet()) { + sm = entry.getKey(); + fields = entry.getValue(); cache = sm.getMetaData().getDataCache(); if (cache == null || sm.isEmbedded() || (failed != null @@ -550,10 +550,10 @@ public class DataCacheStoreManager /** * Helper method to add an unloaded instance to the given map. */ - private static Map addUnloaded(OpenJPAStateManager sm, BitSet fields, - Map unloaded) { + private static Map addUnloaded(OpenJPAStateManager sm, BitSet fields, + Map unloaded) { if (unloaded == null) - unloaded = new HashMap(); + unloaded = new HashMap(); unloaded.put(sm, fields); return unloaded; } @@ -580,24 +580,29 @@ public class DataCacheStoreManager sm = (OpenJPAStateManager) itr.next(); if (sm.getPCState() == PCState.PNEW && !sm.isFlushed()) { - if (_inserts == null) - _inserts = new ArrayList(); + if (_inserts == null) { + _inserts = new ArrayList(); + } _inserts.add(sm); // may have been re-persisted - if (_deletes != null) - _deletes.remove(sm); + if (_deletes != null) { + _deletes.remove(sm); + } } else if (_inserts != null && (sm.getPCState() == PCState.PNEWDELETED - || sm.getPCState() == PCState.PNEWFLUSHEDDELETED)) + || sm.getPCState() == PCState.PNEWFLUSHEDDELETED)) { _inserts.remove(sm); + } else if (sm.getPCState() == PCState.PDIRTY) { - if (_updates == null) - _updates = new HashMap(); + if (_updates == null) { + _updates = new HashMap(); + } _updates.put(sm, sm.getDirty()); } else if (sm.getPCState() == PCState.PDELETED) { - if (_deletes == null) - _deletes = new HashSet(); + if (_deletes == null) { + _deletes = new HashSet(); + } _deletes.add(sm); } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index aec010f69..33989c7ac 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -38,6 +38,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; @@ -229,6 +230,12 @@ public class BrokerImpl private boolean _cachePreparedQuery = true; private boolean _cacheFinderQuery = true; + private DataCacheStoreMode _cacheStoreMode; + private DataCacheRetrieveMode _cacheRetrieveMode; + + // Store and Retrieve mode may be suspended for a given operation. Stack may be overkill here. + private Stack _cacheStoreModeStack = new Stack(); + private Stack _cacheRetrieveModeStack = new Stack(); // Map of properties whose values have been changed // private Map _changedProperties = @@ -4894,4 +4901,40 @@ public class BrokerImpl unlock(); } } + + @Override + public DataCacheRetrieveMode getCacheRetrieveMode() { + return _cacheRetrieveMode; + } + + @Override + public DataCacheStoreMode getCacheStoreMode() { + return _cacheStoreMode; + } + + @Override + public void setCacheRetrieveMode(DataCacheRetrieveMode mode) { + _cacheRetrieveMode = mode; + } + + @Override + public void setCacheStoreMode(DataCacheStoreMode mode) { + _cacheStoreMode = mode; + } + + public void popCacheRetrieveMode() { + _cacheRetrieveMode = _cacheRetrieveModeStack.pop(); + } + + public void popCacheStoreMode() { + _cacheStoreMode = _cacheStoreModeStack.pop(); + } + + public void pushCacheRetrieveMode() { + _cacheRetrieveModeStack.push(_cacheRetrieveMode); + } + + public void pushCacheStoreMode() { + _cacheStoreModeStack.push(_cacheStoreMode); + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheRetrieveMode.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheRetrieveMode.java new file mode 100644 index 000000000..0a4cea35c --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheRetrieveMode.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * DataCache Retrieve Modes. + */ +public enum DataCacheRetrieveMode { + /** + * Retrieve objects from the DataCache if a DataCache is enabled. + */ + USE, + /** + * Ignore the DataCache and fetch data directly from the database. + */ + BYPASS, +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheStoreMode.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheStoreMode.java new file mode 100644 index 000000000..d12a13973 --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DataCacheStoreMode.java @@ -0,0 +1,42 @@ +/* + * 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; + +/** + * DataCache Store modes + */ +public enum DataCacheStoreMode { + + /** + * Store updates, inserts and deletes in the DataCache. The DataCache will + * not be refreshed when data is read from the database. + */ + USE, + /** + * Write updates, inserts and deletes directly to the database. The + * DataCache will not be aware of these changes and may need to be + * refreshed. + */ + BYPASS, + /** + * Store updates, inserts and deletes in the DataCache. Entities which are + * read from the database will be refreshed in the DataCache. + */ + REFRESH +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java index dfb7b43d3..5aa480d19 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java @@ -1411,4 +1411,39 @@ public class DelegatingBroker _broker.setCachePreparedQuery(flag); } + @Override + public DataCacheRetrieveMode getCacheRetrieveMode() { + return _broker.getCacheRetrieveMode(); + } + + @Override + public DataCacheStoreMode getCacheStoreMode() { + return _broker.getCacheStoreMode(); + } + + @Override + public void setCacheRetrieveMode(DataCacheRetrieveMode mode) { + _broker.setCacheRetrieveMode(mode); + } + + @Override + public void setCacheStoreMode(DataCacheStoreMode mode) { + _broker.setCacheStoreMode(mode); + } + + public void popCacheRetrieveMode() { + _broker.popCacheRetrieveMode(); + } + + public void popCacheStoreMode() { + _broker.popCacheStoreMode(); + } + + public void pushCacheRetrieveMode() { + _broker.pushCacheRetrieveMode(); + } + + public void pushCacheStoreMode() { + _broker.pushCacheStoreMode(); + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java index 1d8d9d500..70863e4da 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java @@ -458,4 +458,61 @@ public interface StoreContext { * Releases the internal lock. */ public void unlock (); + + /** + * Return the current DataCacheStoreMode for this context. The + * DataCacheStoreMode controls when entities are added / updated in the + * DataCache. + * + * @return DataCacheStore mode in use + * @since 2.0.0 + */ + public DataCacheStoreMode getCacheStoreMode(); + + /** + * Set DataCacheStoreMode + * @param mode The new DataCacheStoreMode + * @since 2.0.0 + */ + public void setCacheStoreMode(DataCacheStoreMode mode); + + /** + * Return the current DataCacheRetrieveMode (controls whether objects will + * be loaded from the DataCache or direct from the database). + * + * @return DataCacheRetrieveMode in use. + * @since 2.0.0 + */ + public DataCacheRetrieveMode getCacheRetrieveMode(); + + /** + * Set DataCacheRetrieveMode + * @param mode new mode for obtaining data from the cache + * @since 2.0.0 + */ + public void setCacheRetrieveMode(DataCacheRetrieveMode mode); + + /** + * Pop the DataCacheRetrieveMode stack + * @since 2.0.0 + */ + public void popCacheRetrieveMode(); + + /** + * Pop the DataCacheStoreMode stack. + * @since 2.0.0 + */ + public void popCacheStoreMode() ; + + /** + * Push the current DataCacheRetrieveMode onto a saved stack. + * @since 2.0.0 + */ + public void pushCacheRetrieveMode() ; + + /** + * Push the current DataCacheStoreMode onto a saved stack. + * @since 2.0.0 + */ + public void pushCacheStoreMode(); } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractCacheModeTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractCacheModeTestCase.java new file mode 100644 index 000000000..07c7b7ff6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractCacheModeTestCase.java @@ -0,0 +1,396 @@ +/* + * 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.cache.jpa; + +import java.util.List; + +import javax.persistence.Cache; +import javax.persistence.CacheRetrieveMode; +import javax.persistence.CacheStoreMode; +import javax.persistence.EntityManager; + +import org.apache.openjpa.lib.jdbc.AbstractJDBCListener; +import org.apache.openjpa.lib.jdbc.JDBCEvent; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.cache.jpa.model.CacheEntity; +import org.apache.openjpa.persistence.cache.jpa.model.CacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.NegatedCachableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.NegatedUncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UnspecifiedEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlCacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlUncacheableEntity; + +public abstract class AbstractCacheModeTestCase extends AbstractCacheTestCase { + public abstract OpenJPAEntityManagerFactorySPI getEntityManagerFactory(); + + public abstract List getSql(); + + protected abstract Class[] getExpectedNotInCache(); + + protected abstract Class[] getExpectedInCache(); + + // ======================================================================= + // Asserts + // ======================================================================= + /** + * Assert whether the cache contains the expected results. + * + * @param cache + * The JPA Cache to verify + * @param expectCacheables + * Whether entities with @Cacheable(true) should be in the cache + * (almost always true) + * @param expectUncacheables + * Whether entities with @Cacheable(false) should be in the cache + * (almost always false) + * @param expectUnspecified + * Whether entities with no @Cacheable annotation should be in + * the cache (varies per config). + */ + protected void assertCacheContents(Cache cache, boolean expectCacheables, boolean expectUncacheables, + boolean expectUnspecified) { + assertCacheables(cache, expectCacheables); + assertUncacheables(cache, expectUncacheables); + assertUnspecified(cache, expectUnspecified); + } + + /** + * Assert whether the cacheable types are in the cache. This method exits on + * the first cache 'miss'. + * + * @param cache + * JPA Cache to verify + * @param expected + * If true the cacheable types should be in the cache, if false + * they should not be. + */ + protected void assertCacheables(Cache cache, boolean expected) { + assertCached(cache, CacheableEntity.class, 1, expected); + assertCached(cache, NegatedUncacheableEntity.class, 1, expected); + assertCached(cache, XmlCacheableEntity.class, 1, expected); + } + + /** + * Assert whether the uncacheable types are in the cache. This method exits + * on the first cache 'miss'. + * + * @param cache + * JPA Cache to verify + * @param expected + * If true the uncacheable types should be in the cache, if false + * they should not be. + */ + protected void assertUncacheables(Cache cache, boolean expected) { + assertCached(cache, UncacheableEntity.class, 1, expected); + assertCached(cache, XmlUncacheableEntity.class, 1, expected); + assertCached(cache, NegatedCachableEntity.class, 1, expected); + } + + /** + * Assert whether the unspecified types are in the cache. This method exits + * on the first cache 'miss'. + * + * @param cache + * JPA Cache to verify + * @param expected + * If true the unspecified types should be in the cache, if false + * they should not be. + */ + protected void assertUnspecified(Cache cache, boolean expected) { + assertCached(cache, UnspecifiedEntity.class, 1, expected); + } + + /** + * Assert that no sql is executed when running the supplied Action. + * + * @param act + * Action to execute. + */ + public void assertNoSql(Action act) { + assertSqlInc(act, 0); + } + + /** + * Assert that expectedSqls SQL statements are executed + * when running act + * + * @param act + * Action to run. + * @param expectedSqls + * Number of SQL statements that should be executed. + */ + public void assertSqlInc(Action act, int expectedSqls) { + int before = getSql().size(); + act.run(); + assertEquals(before + expectedSqls, getSql().size()); + } + + // ======================================================================= + // Utility classes + // ======================================================================= + /** + * Basic 'runnable' interface used to run a set of commands, then analyze + * the number of SQL statements that result. + */ + public interface Action { + public void run(); + } + + /** + * Simple JDBCListener which stores the executed sql in a List. The List is + * provided by the getSql() method so that subclasses may use separate + * lists. + * + * @author mikedd + * + */ + public class Listener extends AbstractJDBCListener { + @Override + public void beforeExecuteStatement(JDBCEvent event) { + if (event.getSQL() != null && getSql() != null) { + getSql().add(event.getSQL()); + } + } + } + + // ======================================================================= + // Test utilities + // ======================================================================= + public boolean getCacheEnabled() { + return true; + } + + // ======================================================================= + // Common test methods. + // ======================================================================= + /** + * Ensure that each call the em.find generates an SQL statement when + * CacheRetrieveMode.BYPASS is used. + */ + public void testReadModeByass() { + assertSqlInc(new Action() { + public void run() { + EntityManager em = getEntityManagerFactory().createEntityManager(); + em.setProperty(RETRIEVE_MODE_PROP, CacheRetrieveMode.BYPASS); + for (Class cls : persistentTypes) { + em.find(cls, 1); + } + em.close(); + } + }, persistentTypes.length); + } + + /** + *

+ * Ensure that each entity in getExpectedInCache(): + *

    + *
  • is in the cache
  • + *
  • does not go to the database for a find operation
  • + *
  • is not null
  • + *
+ *

+ *

+ * and + *

+ *

+ * Ensure that each entity in getExpectedNotInCache() : + *

    + *
  • is not in the cache
  • + *
  • results in a single SQL statement when em.find() is called
  • + *
  • is not null
  • + *
+ *

+ * + */ + public void testRetrieveModeUse() { + assertNoSql(new Action() { + public void run() { + EntityManager em = getEntityManagerFactory().createEntityManager(); + em.setProperty(RETRIEVE_MODE_PROP, CacheRetrieveMode.USE); + for (Class cls : getExpectedInCache()) { + assertCached(getEntityManagerFactory().getCache(), cls, 1, true); + assertNotNull(em.find(cls, 1)); + } + em.close(); + } + }); + assertSqlInc(new Action() { + public void run() { + EntityManager em = getEntityManagerFactory().createEntityManager(); + em.setProperty(RETRIEVE_MODE_PROP, CacheRetrieveMode.USE); + for (Class cls : getExpectedNotInCache()) { + assertCached(getEntityManagerFactory().getCache(), cls, 1, false); + assertNotNull(em.find(cls, 1)); + } + em.close(); + } + }, getExpectedNotInCache().length); + } + + public void updateAndFind(Class classToUpdate, int idToUpdate, + Class classToFind, int idToFind, + CacheStoreMode storeMode, CacheRetrieveMode retrieveMode) { + EntityManager em = getEntityManagerFactory().createEntityManager(); + + if (storeMode != null) { + em.setProperty(STORE_MODE_PROP, storeMode); + } + if (retrieveMode != null) { + em.setProperty(RETRIEVE_MODE_PROP, retrieveMode); + } + + em.getTransaction().begin(); + CacheEntity ce1 = em.find(classToUpdate, idToUpdate); + CacheEntity ce2 = em.find(classToFind, idToFind); + assertNotNull(ce1); + assertNotNull(ce2); + ce1.setName(ce1.getName() + "UPD"); + em.getTransaction().commit(); + em.close(); + } + + /** + *

+ * Test logic to validate different CacheStoreModes. It should behave + * identically for all shared-cache-modes except NONE which never caches + * anything. + *

+ *

+ * This method only tests setting the store mode on the EntityManager + * itself. + *

+ *

+ * The first transaction updates CacheableEntity::1 with CacheStoreMode + * tran1StoreMode, calls find for CacheableEntity::1 and + * XmlCacheableEntity::1. This will never trigger a cache refresh since the + * data is up to date - but it could trigger additional SQL + *

+ *

+ * The second transaction updates XmlCacheableEntity::1 with CacheStoreMode + * tran2StoreMode, calls find for CacheableEntity::1 and + * XmlCacheableEntity::1. In this case if tran2StoreMode == + * CacheStoreMode.REFRESH we may update the cache with the state of + * CacheableEntity::1. + *

+ * + * @param tran1StoreMode + * CacheStoreMode to use in transaction 1. + * @param tran2StoreMode + * cacheStoreMode to use in transaction 2. + * @param cacheUpdatedForTran1 + * Whether the cache will contain an updated version of + * CacheableEntity::1 + * @param cacheUpdatedForTran2 + * Whether the cache will contain an updated version of + * XmlCacheableEntity::1 + * @param version + * Expected starting version of for both entities + */ + public void entityManagerStoreModeTest(CacheStoreMode tran1StoreMode, CacheStoreMode tran2StoreMode, + boolean cacheUpdatedForTran1, boolean cacheUpdatedForTran2, int version) { + updateAndFind(CacheableEntity.class, 1, XmlCacheableEntity.class, 1, tran1StoreMode, null); + updateAndFind(XmlCacheableEntity.class, 1, CacheableEntity.class, 1, tran2StoreMode, null); + + // get entities from the cache and ensure their versions are as + // expected. + EntityManager em = getEntityManagerFactory().createEntityManager(); + em = getEntityManagerFactory().createEntityManager(); + CacheableEntity ceFromEM = em.find(CacheableEntity.class, 1); + XmlCacheableEntity xceFromEM = em.find(XmlCacheableEntity.class, 1); + em.close(); + assertEquals(cacheUpdatedForTran1 ? version + 1 : version, ceFromEM.getVersion()); + assertEquals(cacheUpdatedForTran2 ? version + 1 : version, xceFromEM.getVersion()); + + // get the data from the database. Version should always have been + // updated in this case. + em = getEntityManagerFactory().createEntityManager(); + em.setProperty(RETRIEVE_MODE_PROP, CacheRetrieveMode.BYPASS); + CacheableEntity ceFromDB = + (CacheableEntity) em.createNativeQuery("Select * from CacheableEntity where id = 1", CacheableEntity.class) + .getSingleResult(); + + XmlCacheableEntity xceFromDB = + (XmlCacheableEntity) em.createNativeQuery("Select * from XmlCacheableEntity where id = 1", + XmlCacheableEntity.class).getSingleResult(); + + assertEquals(version + 1, ceFromDB.getVersion()); + assertEquals(version + 1, xceFromDB.getVersion()); + em.close(); + } + + /** + * Execute the defaultStoreModeTest with + */ + public void testStoreModeUseBypass() throws Exception { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.USE, CacheStoreMode.BYPASS, true, false, 1); + } + } + + public void testStoreModeUseUse() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.USE, CacheStoreMode.USE, true, true, 1); + } + } + + public void testStoreModeUseRefresh() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.USE, CacheStoreMode.REFRESH, true, true, 1); + } + } + + public void entityManagerStoreModeTest() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.BYPASS, CacheStoreMode.BYPASS, false, false, 1); + } + } + + public void testStoreModeBypassUse() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.BYPASS, CacheStoreMode.USE, false, true, 1); + } + } + + public void testStoreModeBypassRefresh() { + if (getCacheEnabled()) { + // REFRESH picks up the changes from the database, even though the + // first update was done with BYPASS + entityManagerStoreModeTest(CacheStoreMode.BYPASS, CacheStoreMode.REFRESH, true, true, 1); + } + } + + public void testStoreModeRefreshUse() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.REFRESH, CacheStoreMode.USE, true, true, 1); + } + } + + public void testStoreModeRefreshBypass() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.REFRESH, CacheStoreMode.BYPASS, true, false, 1); + } + } + + public void testStoreModeRefreshRefresh() { + if (getCacheEnabled()) { + entityManagerStoreModeTest(CacheStoreMode.REFRESH, CacheStoreMode.REFRESH, true, true, 1); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractJPACacheTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractJPACacheTestCase.java deleted file mode 100644 index b7aecf88e..000000000 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/AbstractJPACacheTestCase.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.cache.jpa; - -import java.lang.reflect.Modifier; - -import javax.persistence.Cache; -import javax.persistence.EntityManager; - -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.persistence.cache.jpa.model.CacheEntity; -import org.apache.openjpa.persistence.cache.jpa.model.CacheableEntity; -import org.apache.openjpa.persistence.cache.jpa.model.NegatedCachableEntity; -import org.apache.openjpa.persistence.cache.jpa.model.NegatedUncacheableEntity; -import org.apache.openjpa.persistence.cache.jpa.model.UncacheableEntity; -import org.apache.openjpa.persistence.cache.jpa.model.UnspecifiedEntity; -import org.apache.openjpa.persistence.cache.jpa.model.XmlCacheableEntity; -import org.apache.openjpa.persistence.cache.jpa.model.XmlUncacheableEntity; -import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase; - -public abstract class AbstractJPACacheTestCase extends AbstractPersistenceTestCase { - public abstract OpenJPAEntityManagerFactorySPI getEntityManagerFactory(); - - private static Class[] persistentTypes = - { CacheableEntity.class, UncacheableEntity.class, UnspecifiedEntity.class, - NegatedCachableEntity.class, NegatedUncacheableEntity.class, XmlCacheableEntity.class, - XmlUncacheableEntity.class }; - - public void populate() throws IllegalAccessException, InstantiationException { - EntityManager em = getEntityManagerFactory().createEntityManager(); - em.getTransaction().begin(); - for (Class clss : persistentTypes) { - if (!Modifier.isAbstract(clss.getModifiers())) { - CacheEntity ce = (CacheEntity) clss.newInstance(); - ce.setId(1); - em.persist(ce); - } - } - em.getTransaction().commit(); - em.close(); - } - - public OpenJPAEntityManagerFactorySPI createEntityManagerFactory(String puName) { - OpenJPAEntityManagerFactorySPI emf = - (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(puName, - "META-INF/caching-persistence.xml", getPropertiesMap("openjpa.DataCache", "true", - "openjpa.RemoteCommitProvider", "sjvm", persistentTypes)); - return emf; - } - - @Override - public void setUp() throws Exception { - super.setUp(); - // populate once per test method in case we add more methods - cleanDatabase(); - populate(); - } - - public void cleanDatabase() throws Exception { - EntityManager em = getEntityManagerFactory().createEntityManager(); - em.getTransaction().begin(); - for (Class clss : persistentTypes) { - if (!Modifier.isAbstract(clss.getModifiers())) { - em.createQuery("Delete from " + clss.getSimpleName()).executeUpdate(); - } - } - em.getTransaction().commit(); - em.close(); - } - - /** - * Assert whether the cache contains the expected results. - * - * @param cache - * The JPA Cache to verify - * @param expectCacheables - * Whether entities with @Cacheable(true) should be in the cache - * (almost always true) - * @param expectUncacheables - * Whether entities with @Cacheable(false) should be in the cache - * (almost always false) - * @param expectUnspecified - * Whether entities with no @Cacheable annotation should be in - * the cache (varies per config). - */ - protected void assertCacheContents(Cache cache, boolean expectCacheables, boolean expectUncacheables, - boolean expectUnspecified) { - assertCacheables(cache, expectCacheables); - assertUncacheables(cache, expectUncacheables); - assertUnspecified(cache, expectUnspecified); - } - - /** - * Assert whether the cacheable types are in the cache. This method exits on - * the first cache 'miss'. - * - * @param cache - * JPA Cache to verify - * @param expected - * If true the cacheable types should be in the cache, if false - * they should not be. - */ - protected void assertCacheables(Cache cache, boolean expected) { - assertCached(cache, CacheableEntity.class, 1, expected); - assertCached(cache, NegatedUncacheableEntity.class, 1, expected); - assertCached(cache, XmlCacheableEntity.class, 1, expected); - } - - /** - * Assert whether the uncacheable types are in the cache. This method exits - * on the first cache 'miss'. - * - * @param cache - * JPA Cache to verify - * @param expected - * If true the uncacheable types should be in the cache, if false - * they should not be. - */ - protected void assertUncacheables(Cache cache, boolean expected) { - assertCached(cache, UncacheableEntity.class, 1, expected); - assertCached(cache, XmlUncacheableEntity.class, 1, expected); - assertCached(cache, NegatedCachableEntity.class, 1, expected); - } - - /** - * Assert whether the unspecified types are in the cache. This method exits - * on the first cache 'miss'. - * - * @param cache - * JPA Cache to verify - * @param expected - * If true the unspecified types should be in the cache, if false - * they should not be. - */ - protected void assertUnspecified(Cache cache, boolean expected) { - assertCached(cache, UnspecifiedEntity.class, 1, expected); - } -} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeAll.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeAll.java index dd54c7786..ff97cb880 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeAll.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeAll.java @@ -18,14 +18,24 @@ */ package org.apache.openjpa.persistence.cache.jpa; -import javax.persistence.Cache; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Cache; +import javax.persistence.CacheStoreMode; + +import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -public class TestCacheModeAll extends AbstractJPACacheTestCase { +public class TestCacheModeAll extends AbstractCacheModeTestCase { private static OpenJPAEntityManagerFactorySPI emf = null; private static Cache cache = null; + private static List sql = new ArrayList(); + private static JDBCListener listener; + + private static Class[] expectedInCache = persistentTypes; + private static Class[] expectedNotInCache = {}; @Override public OpenJPAEntityManagerFactorySPI getEntityManagerFactory() { @@ -38,6 +48,17 @@ public class TestCacheModeAll extends AbstractJPACacheTestCase { return emf; } + public JDBCListener getListener() { + if (listener == null) { + listener = new Listener(); + } + return listener; + } + + public List getSql() { + return sql; + } + public void testCacheables() { assertCacheables(cache, true); } @@ -49,4 +70,14 @@ public class TestCacheModeAll extends AbstractJPACacheTestCase { public void testUnspecified() { assertUnspecified(cache, true); } + + @Override + protected Class[] getExpectedInCache() { + return expectedInCache; + } + + @Override + protected Class[] getExpectedNotInCache() { + return expectedNotInCache; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeDisableSelective.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeDisableSelective.java index 552603567..015c99bae 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeDisableSelective.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeDisableSelective.java @@ -18,14 +18,32 @@ */ package org.apache.openjpa.persistence.cache.jpa; +import java.util.ArrayList; +import java.util.List; + import javax.persistence.Cache; +import javax.persistence.CacheStoreMode; +import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.cache.jpa.model.CacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.NegatedUncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UnspecifiedEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlCacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlUncacheableEntity; -public class TestCacheModeDisableSelective extends AbstractJPACacheTestCase { +public class TestCacheModeDisableSelective extends AbstractCacheModeTestCase { private static OpenJPAEntityManagerFactorySPI emf = null; private static Cache cache = null; + private static List sql = new ArrayList(); + private static JDBCListener listener; + + private static Class[] expectedInCache = + { CacheableEntity.class, XmlCacheableEntity.class, NegatedUncacheableEntity.class, UnspecifiedEntity.class, }; + private static Class[] expectedNotInCache = + { UncacheableEntity.class, XmlUncacheableEntity.class, }; @Override public OpenJPAEntityManagerFactorySPI getEntityManagerFactory() { @@ -38,6 +56,17 @@ public class TestCacheModeDisableSelective extends AbstractJPACacheTestCase { return emf; } + public JDBCListener getListener() { + if (listener == null) { + listener = new Listener(); + } + return listener; + } + + public List getSql() { + return sql; + } + public void testCacheables() { assertCacheables(cache, true); } @@ -49,4 +78,14 @@ public class TestCacheModeDisableSelective extends AbstractJPACacheTestCase { public void testUnspecified() { assertUnspecified(cache, true); } + + @Override + protected Class[] getExpectedInCache() { + return expectedInCache; + } + + @Override + protected Class[] getExpectedNotInCache() { + return expectedNotInCache; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeEnableSelective.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeEnableSelective.java index 1ff9bdcc5..2ed9effe5 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeEnableSelective.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeEnableSelective.java @@ -18,14 +18,31 @@ */ package org.apache.openjpa.persistence.cache.jpa; +import java.util.ArrayList; +import java.util.List; + import javax.persistence.Cache; +import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.cache.jpa.model.CacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.NegatedUncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UncacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.UnspecifiedEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlCacheableEntity; +import org.apache.openjpa.persistence.cache.jpa.model.XmlUncacheableEntity; -public class TestCacheModeEnableSelective extends AbstractJPACacheTestCase { +public class TestCacheModeEnableSelective extends AbstractCacheModeTestCase { private static OpenJPAEntityManagerFactorySPI emf = null; private static Cache cache = null; + private static List sql = new ArrayList(); + private static JDBCListener listener; + + private static Class[] expectedInCache = + { CacheableEntity.class, XmlCacheableEntity.class, NegatedUncacheableEntity.class, }; + private static Class[] expectedNotInCache = + { UncacheableEntity.class, XmlUncacheableEntity.class, UnspecifiedEntity.class, }; @Override public OpenJPAEntityManagerFactorySPI getEntityManagerFactory() { @@ -38,6 +55,31 @@ public class TestCacheModeEnableSelective extends AbstractJPACacheTestCase { return emf; } + public JDBCListener getListener() { + if (listener == null) { + listener = new Listener(); + } + return listener; + } + + public List getSql() { + return sql; + } + + @Override + protected Class[] getExpectedInCache() { + return expectedInCache; + } + + @Override + protected Class[] getExpectedNotInCache() { + return expectedNotInCache; + } + + // ======================================================================= + // Tests + // ======================================================================= + public void testCacheables() { assertCacheables(cache, true); } @@ -49,5 +91,5 @@ public class TestCacheModeEnableSelective extends AbstractJPACacheTestCase { public void testUnspecified() { assertUnspecified(cache, false); } - + } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeNone.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeNone.java index 55ca94af1..7aee383c4 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeNone.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/TestCacheModeNone.java @@ -18,14 +18,24 @@ */ package org.apache.openjpa.persistence.cache.jpa; +import java.util.ArrayList; +import java.util.List; + import javax.persistence.Cache; +import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; +import org.apache.openjpa.persistence.StoreCache; -public class TestCacheModeNone extends AbstractJPACacheTestCase { +public class TestCacheModeNone extends AbstractCacheModeTestCase { private static OpenJPAEntityManagerFactorySPI emf = null; private static Cache cache = null; + private static List sql = new ArrayList(); + private static JDBCListener listener; + + private static Class[] expectedInCache = {}; + private static Class[] expectedNotInCache = persistentTypes; @Override public OpenJPAEntityManagerFactorySPI getEntityManagerFactory() { @@ -38,6 +48,22 @@ public class TestCacheModeNone extends AbstractJPACacheTestCase { return emf; } + public JDBCListener getListener() { + if (listener == null) { + listener = new Listener(); + } + return listener; + } + + public List getSql() { + return sql; + } + + @Override + public boolean getCacheEnabled() { + return false; + } + public void testCacheables() { assertCacheables(cache, false); } @@ -49,4 +75,15 @@ public class TestCacheModeNone extends AbstractJPACacheTestCase { public void testUnspecified() { assertUnspecified(cache, false); } + + @Override + protected Class[] getExpectedInCache() { + return expectedInCache; + } + + @Override + protected Class[] getExpectedNotInCache() { + return expectedNotInCache; + } + } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheEntity.java index cfeaffe7e..5c68b856a 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheEntity.java @@ -21,4 +21,6 @@ package org.apache.openjpa.persistence.cache.jpa.model; public interface CacheEntity { public void setId(int id); + public void setName(String name); + public String getName(); } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheableEntity.java index 9c826e485..507e74464 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/CacheableEntity.java @@ -31,6 +31,8 @@ public class CacheableEntity implements CacheEntity { @Version private int version; + + private String name; public int getId() { return id; @@ -47,4 +49,12 @@ public class CacheableEntity implements CacheEntity { public void setVersion(int version) { this.version = version; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedCachableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedCachableEntity.java index 9a95cab09..41d005005 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedCachableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedCachableEntity.java @@ -33,6 +33,16 @@ public class NegatedCachableEntity implements CacheEntity { @Version private int version; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } public int getId() { return id; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedUncacheableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedUncacheableEntity.java index 8ce5df03a..954750583 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedUncacheableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/NegatedUncacheableEntity.java @@ -34,6 +34,16 @@ public class NegatedUncacheableEntity implements CacheEntity { @Version private int version; + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public int getId() { return id; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UncacheableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UncacheableEntity.java index 82cdce726..8d9bdd929 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UncacheableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UncacheableEntity.java @@ -31,6 +31,16 @@ public class UncacheableEntity implements CacheEntity { @Version private int version; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } public int getId() { return id; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UnspecifiedEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UnspecifiedEntity.java index 7ada8e19e..3d4e796bc 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UnspecifiedEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/UnspecifiedEntity.java @@ -29,6 +29,16 @@ public class UnspecifiedEntity implements CacheEntity { @Version private int version; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } public int getId() { return id; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlCacheableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlCacheableEntity.java index 1134ca1f2..c8db48df3 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlCacheableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlCacheableEntity.java @@ -32,6 +32,8 @@ public class XmlCacheableEntity implements CacheEntity { @Version private int version; + + private String name; public int getId() { return id; @@ -48,4 +50,12 @@ public class XmlCacheableEntity implements CacheEntity { public void setVersion(int version) { this.version = version; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlUncacheableEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlUncacheableEntity.java index 34cc5a504..c8d1da6a0 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlUncacheableEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/jpa/model/XmlUncacheableEntity.java @@ -33,6 +33,16 @@ public class XmlUncacheableEntity implements CacheEntity { @Version private int version; + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public int getId() { return id; } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java index a68ac2ea0..ff21202e6 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java @@ -38,6 +38,8 @@ import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; +import javax.persistence.CacheRetrieveMode; +import javax.persistence.CacheStoreMode; import javax.persistence.EntityManager; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; @@ -57,6 +59,8 @@ import org.apache.openjpa.enhance.PCRegistry; import org.apache.openjpa.enhance.Reflection; import org.apache.openjpa.kernel.AbstractBrokerFactory; import org.apache.openjpa.kernel.Broker; +import org.apache.openjpa.kernel.DataCacheRetrieveMode; +import org.apache.openjpa.kernel.DataCacheStoreMode; import org.apache.openjpa.kernel.DelegatingBroker; import org.apache.openjpa.kernel.FetchConfiguration; import org.apache.openjpa.kernel.FindCallbacks; @@ -105,6 +109,9 @@ public class EntityManagerImpl private Map _plans = new IdentityHashMap(1); private RuntimeExceptionTranslator _ret = PersistenceExceptions.getRollbackTranslator(this); + + protected final String RETRIEVE_MODE_PROP = "javax.persistence.cache.retrieveMode"; + protected final String STORE_MODE_PROP = "javax.persistence.cache.storeMode"; public EntityManagerImpl() { // for Externalizable @@ -482,9 +489,9 @@ public class EntityManagerImpl public T find(Class cls, Object oid, LockModeType mode, Map properties) { assertNotCloseInvoked(); - if (mode != null && mode != LockModeType.NONE) + if (mode != null && mode != LockModeType.NONE) { _broker.assertActiveTransaction(); - + } processLockProperties(pushFetchPlan(), mode, properties); try { oid = _broker.newObjectId(cls, oid); @@ -1638,4 +1645,38 @@ public class EntityManagerImpl int dot = s.lastIndexOf('.'); return dot == -1 ? s : s.substring(dot+1); } + + public void setRetrieveMode(CacheRetrieveMode retrieveMode) { + _broker.setCacheRetrieveMode(toDataCacheRetrieveMode(retrieveMode)); + } + + public CacheRetrieveMode getRetrieveMode() { + return fromDataCacheRetrieveMode(_broker.getCacheRetrieveMode()); + } + + public void setStoreMode(CacheStoreMode storeMode) { + _broker.setCacheStoreMode(toDataCacheStoreMode(storeMode)); + } + + public CacheStoreMode getStoreMode() { + return fromDataCacheStoreMode(_broker.getCacheStoreMode()); + } + + private final DataCacheRetrieveMode toDataCacheRetrieveMode(CacheRetrieveMode mode ) { + // relies on the CacheRetrieveMode enums being nearly identical + return DataCacheRetrieveMode.valueOf(mode.toString()); + } + + private final DataCacheStoreMode toDataCacheStoreMode(CacheStoreMode mode ) { + // relies on the CacheStoreMode enums being nearly identical + return DataCacheStoreMode.valueOf(mode.toString()); + } + + private final CacheRetrieveMode fromDataCacheRetrieveMode(DataCacheRetrieveMode mode) { + return CacheRetrieveMode.valueOf(mode.toString()); + } + + private final CacheStoreMode fromDataCacheStoreMode(DataCacheStoreMode mode) { + return CacheStoreMode.valueOf(mode.toString()); + } }