From e2388e9b3cb5f983623c968459d36a4d8c81c59c Mon Sep 17 00:00:00 2001 From: Catalina Wei Date: Tue, 21 Oct 2008 00:23:29 +0000 Subject: [PATCH] OPENJPA-738 QueryCache Improvement Committing patch provided by Sandhya Sturaga git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@706481 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/datacache/AbstractQueryCache.java | 118 ++++++++-- .../datacache/ConcurrentQueryCache.java | 7 + .../datacache/QueryCacheStoreQuery.java | 28 ++- .../apache/openjpa/datacache/QueryKey.java | 11 + .../apache/openjpa/datacache/QueryResult.java | 17 ++ .../persistence/jdbc/query/cache/Part.java | 81 +++++++ .../jdbc/query/cache/PartBase.java | 93 ++++++++ .../jdbc/query/cache/PartComposite.java | 96 ++++++++ .../jdbc/query/cache/Supplier.java | 87 ++++++++ .../cache/TestQueryTimestampEviction.java | 207 ++++++++++++++++++ .../persistence/jdbc/query/cache/Usage.java | 95 ++++++++ 11 files changed, 821 insertions(+), 19 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartBase.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartComposite.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Supplier.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/TestQueryTimestampEviction.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Usage.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java index 6adbd8bc5..53af81a0f 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractQueryCache.java @@ -18,12 +18,16 @@ */ package org.apache.openjpa.datacache; +import java.security.AccessController; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.event.RemoteCommitEvent; @@ -31,6 +35,7 @@ import org.apache.openjpa.event.RemoteCommitListener; import org.apache.openjpa.lib.conf.Configurable; import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.log.Log; +import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager; import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet; @@ -63,34 +68,65 @@ public abstract class AbstractQueryCache */ protected Log log; + protected ConcurrentHashMap entityTimestampMap = null; private boolean _closed = false; + protected String evictPolicy = "default"; + public void initialize(DataCacheManager manager) { + if(evictPolicy.equalsIgnoreCase("timestamp")) { + entityTimestampMap = new ConcurrentHashMap(); + + // Get all persistence types to pre-load the entityTimestamp Map + Collection perTypes = conf.getMetaDataRepositoryInstance(). + getPersistentTypeNames(false, + (ClassLoader) AccessController.doPrivileged( + J2DoPrivHelper.getContextClassLoaderAction())); + + // Pre-load all the entity types into the HashMap to handle + // synchronization on the map efficiently + for (Object o : perTypes) + entityTimestampMap.put((String)o, new Long(0)); + } } public void onTypesChanged(TypesChangedEvent ev) { writeLock(); Collection keys = null; - try { - if (hasListeners()) - fireEvent(ev); - keys = keySet(); - } finally { - writeUnlock(); - } - - QueryKey qk; - List removes = null; - for (Iterator iter = keys.iterator(); iter.hasNext();) { - qk = (QueryKey) iter.next(); - if (qk.changeInvalidatesQuery(ev.getTypes())) { - if (removes == null) - removes = new ArrayList(); - removes.add(qk); + if (!evictPolicy.equalsIgnoreCase("timestamp")) { + try { + if (hasListeners()) + fireEvent(ev); + keys = keySet(); + } finally { + writeUnlock(); } + + QueryKey qk; + List removes = null; + for (Object o: keys) { + qk = (QueryKey) o; + if (qk.changeInvalidatesQuery(ev.getTypes())) { + if (removes == null) + removes = new ArrayList(); + removes.add(qk); + } + } + if (removes != null) + removeAllInternal(removes); + } else { + Collection changedTypes = ev.getTypes(); + HashMap changedClasses = + new HashMap(); + for (Object o: changedTypes) { + String name = ((Class) o).getName(); + if(!changedClasses.containsKey(name)) + changedClasses.put(name, + new Long(System.currentTimeMillis())); + } + // Now update entity timestamp map + updateEntityTimestampMap(changedClasses); } - if (removes != null) - removeAllInternal(removes); } public QueryResult get(QueryKey key) { @@ -319,4 +355,50 @@ public abstract class AbstractQueryCache protected Collection newListenerCollection() { return new ConcurrentReferenceHashSet (ConcurrentReferenceHashSet.WEAK); } + + /** + * Sets the eviction policy for the query cache + * @param evictPolicy -- String value that specifies the eviction policy + */ + public void setEvictPolicy(String evictPolicy) { + this.evictPolicy = evictPolicy; + } + + /** + * Returns the evictionPolicy for QueryCache + * @return -- returns a String value of evictPolicy attribute + */ + public String getEvictPolicy() { + return null; + } + + /** + * Updates the entity timestamp map with the current time in milliseconds + * @param timestampMap -- a map that contains entityname and its last updated timestamp + */ + protected void updateEntityTimestampMap(Map timestampMap) { + if (entityTimestampMap != null) + entityTimestampMap.putAll(timestampMap); + } + + /** + * Returns a list of timestamps in the form of Long objects + * which are the last updated time stamps for the given entities in the + * keylist. + * @param keyList -- List of entity names + * @return -- Returns a list that has the timestamp for the given entities + */ + public List getAllEntityTimestampFromMap(List keyList) { + ArrayList tmval = null; + if (entityTimestampMap != null) { + for (String s: keyList) { + if (entityTimestampMap.containsKey(s)) { + if(tmval == null) + tmval = new ArrayList(); + tmval.add(entityTimestampMap.get(s)); + } + } + } + return tmval; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ConcurrentQueryCache.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ConcurrentQueryCache.java index fd1a44f64..ab2ee4d77 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ConcurrentQueryCache.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ConcurrentQueryCache.java @@ -137,4 +137,11 @@ public class ConcurrentQueryCache protected Collection keySet() { return _cache.keySet (); } + + /** + * Returns the eviction policy of the query cache + */ + public String getEvictPolicy() { + return super.evictPolicy; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java index 4b5722ee5..5c273892b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java @@ -28,6 +28,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import org.apache.commons.collections.map.LinkedMap; @@ -119,10 +120,34 @@ public class QueryCacheStoreQuery // get the cached data QueryResult res = _cache.get(qk); if (res == null) - return null; + return null; if (res.isEmpty()) return Collections.EMPTY_LIST; + // this if block is invoked if the evictOnTimestamp is set to true + if (_cache instanceof AbstractQueryCache) { + AbstractQueryCache qcache = (AbstractQueryCache) _cache; + if (qcache.getEvictPolicy().equalsIgnoreCase("timestamp")) { + Set classNames = qk.getAcessPathClassNames(); + List keyList = new ArrayList(); + keyList.addAll(classNames); + + List timestamps = + qcache.getAllEntityTimestampFromMap(keyList); + long queryTS = res.getTimestamp(); + if (timestamps != null) { + for (Long ts: timestamps) { + // if this is true we have to evict the query + // from cache + if (queryTS < ts) { + qcache.remove(qk); + return null; + } + } + } + } + } + int projs = getContext().getProjectionAliases().length; if (projs == 0) { // make sure the data cache contains the oids for the query result; @@ -572,6 +597,7 @@ public class QueryCacheStoreQuery QueryResult res = null; synchronized (this) { res = new QueryResult(_qk, _data.values()); + res.setTimestamp(System.currentTimeMillis()); } _cache.put(_qk, res); abortCaching(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryKey.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryKey.java index 43f962eba..7ae3fe80d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryKey.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryKey.java @@ -200,6 +200,8 @@ public class QueryKey // since the class change framework deals with least-derived types, // record the least-derived access path types meta = metas[i]; + if (meta.getDataCache() != null) + accessPathClassNames.add(meta.getDescribedType().getName()); while (meta.getPCSuperclass() != null) meta = meta.getPCSuperclassMetaData(); @@ -232,6 +234,7 @@ public class QueryKey if (metas[i].getDataCache() == null) return null; + accessPathClassNames.add(metas[i].getDescribedType().getName()); subTimeout = metas[i].getDataCacheTimeout(); if (subTimeout != -1 && subTimeout < timeout) timeout = subTimeout; @@ -466,4 +469,12 @@ public class QueryKey _rangeEnd = in.readLong (); _timeout = in.readInt (); } + + /** + * Returns the set of the accessPathClassnames that exists in the query + * @return -- Returns a set of accesspath classnames. + */ + public Set getAcessPathClassNames() { + return this._accessPathClassNames; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryResult.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryResult.java index 2e4630470..0fefff32b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryResult.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryResult.java @@ -31,6 +31,7 @@ public class QueryResult private final long _ex; + private long _timestamp = 0L; /** * Constructor; supply corresponding query key and result data. */ @@ -64,4 +65,20 @@ public class QueryResult public boolean isTimedOut() { return _ex != -1 && _ex < System.currentTimeMillis(); } + + /** + * Sets the timestamp of the query result. + * @param ts -- Timestamp value in long + */ + public void setTimestamp(long ts) { + this._timestamp = ts; + } + + /** + * Returns the timestamp of the query result. + * @return -- the timestamp value in long + */ + public long getTimestamp() { + return this._timestamp; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java new file mode 100644 index 000000000..1949280eb --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java @@ -0,0 +1,81 @@ +/* + * 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.query.cache; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.OneToMany; +import javax.persistence.Version; + +import org.apache.openjpa.persistence.DataCache; + +@Entity +//@MappedSuperclass +@Inheritance(strategy=InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name="PARTTYPE") + +@DataCache +abstract public class Part { + + @Id int partno; + @Column(length=20) + String name; + int inventory; + + @OneToMany(mappedBy="child",cascade=CascadeType.PERSIST) + protected Collection usedIn = new ArrayList(); + + @Version + long version; + + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public int getPartno() { + return partno; + } + public void setPartno(int partno) { + this.partno = partno; + } + public Collection getUsedIn() { + return usedIn; + } + public void setUsedIn(Collection usedIn) { + this.usedIn = usedIn; + } + public int getInventory() { + return inventory; + } + public void setInventory(int inventory) { + this.inventory = inventory; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartBase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartBase.java new file mode 100644 index 000000000..dfd9bdbc1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartBase.java @@ -0,0 +1,93 @@ +/* + * 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.query.cache; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.ManyToMany; + +import org.apache.openjpa.persistence.DataCache; + + +@Entity +@DataCache +public class PartBase extends Part { + + double cost; + double mass; + int backOrder; + + @ManyToMany(mappedBy="supplies") + protected List suppliers = new ArrayList(); + + public PartBase() {} + + public PartBase(int partno, String name, double cost, double mass){ + this.partno=partno; + this.name = name; + this.cost = cost; + this.mass= mass; + this.backOrder=0; + this.inventory=0; + } + + public double getCost() { + return cost; + } + + public void setCost(double cost) { + this.cost = cost; + } + + public double getMass() { + return mass; + } + + public void setMass(double mass) { + this.mass = mass; + } + + public Collection getSuppliers() { + return suppliers; + } + + public void setSuppliers(List suppliers) { + this.suppliers = suppliers; + } + + public String toString() { + String sup= ""; + if (getSuppliers()!=null) + for (Supplier s : getSuppliers()){ + sup= sup+s.sid+","; + } + return "PartBase:"+partno+" name:+"+name+" cost:"+cost+" mass:"+mass+" supplies=["+sup+"]"; + } + + public int getBackOrder() { + return backOrder; + } + + public void setBackOrder(int backOrder) { + this.backOrder = backOrder; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartComposite.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartComposite.java new file mode 100644 index 000000000..cf462c5c7 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/PartComposite.java @@ -0,0 +1,96 @@ +/* + * 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.query.cache; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.OneToMany; + +import org.apache.openjpa.persistence.DataCache; + + +@Entity +@DataCache +public class PartComposite extends Part { + + double assemblyCost; + double assemblyTime; + double massIncrement; + + @OneToMany( mappedBy="parent") + Collection partsUsed = new ArrayList(); + + public PartComposite() {} + + public PartComposite(int partno, String name, double asmCost, double massInc) { + this.partno=partno; + this.name=name; + assemblyCost=asmCost; + massIncrement=massInc; + inventory=0; + } + + public PartComposite addSubPart(EntityManager em, int quantity, Part subpart) { + Usage use = new Usage( this, quantity, subpart); + em.persist(use); + return this; + } + + public double getAssemblyCost() { + return assemblyCost; + } + + public void setAssemblyCost(double assemblyCost) { + this.assemblyCost = assemblyCost; + } + + + public double getMassIncrement() { + return massIncrement; + } + + public void setMassIncrement(double massIncrement) { + this.massIncrement = massIncrement; + } + + public String toString() { + + return "PartComposite:"+partno+" name:+"+name+" assemblyCost:"+assemblyCost+" massIncrement:"+massIncrement; + } + + public Collection getPartsUsed() { + return partsUsed; + } + + public void setPartsUsed(Collection partsUsed) { + this.partsUsed = partsUsed; + } + + public double getAssemblyTime() { + return assemblyTime; + } + + public void setAssemblyTime(double assemblyTime) { + this.assemblyTime = assemblyTime; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Supplier.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Supplier.java new file mode 100644 index 000000000..2eced65be --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Supplier.java @@ -0,0 +1,87 @@ +/* + * 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.query.cache; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.persistence.*; + +import org.apache.openjpa.persistence.DataCache; + +@Entity +@DataCache + +public class Supplier { + + @Id int sid; + @Column(length=20) + String name; + + @ManyToMany + List supplies = new ArrayList(); + + @Version + long version; + + public Supplier(){} + + public Supplier(int sid, String name){ + this.sid=sid; + this.name=name; + } + + public Supplier addPart( PartBase p ) { + supplies.add(p); + p.getSuppliers().add(this); + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSid() { + return sid; + } + + public void setSid(int sid) { + this.sid = sid; + } + + public Collection getSupplies() { + return supplies; + } + + public void setSupplies(List supplies) { + this.supplies = supplies; + } + + public String toString() { + + return "Supplier:"+sid+" name:+"+name; + } + + + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/TestQueryTimestampEviction.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/TestQueryTimestampEviction.java new file mode 100644 index 000000000..023f318b6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/TestQueryTimestampEviction.java @@ -0,0 +1,207 @@ +/* + * 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.query.cache; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.openjpa.datacache.ConcurrentQueryCache; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.QueryResultCacheImpl; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.util.CacheMap; + +public class TestQueryTimestampEviction extends SingleEMFTestCase { + public void setUp() throws Exception { + super.setUp(Part.class, PartBase.class, PartComposite.class, + Supplier.class, Usage.class, + "openjpa.DataCache", "true", + "openjpa.QueryCache", + "CacheSize=1000, evictPolicy='timestamp'", + "openjpa.RemoteCommitProvider", "sjvm"); + + if (recreateData) { + // deletes any data leftover data in the database due to the failed + // last run of this testcase + deleteAllData(); + reCreateData(); + } + } + + private boolean deleteData = false; + private boolean recreateData = true; + + public void testLoadQueries() { + + loadQueryCache(); + int cacheSizeBeforeUpdate = queryCacheGet(); + updateAnEntity(); + int cacheSizeAfterUpdate = queryCacheGet(); + + // If evictPolicy is timestamp the querycache size should be equal to + // cacheSizeBeforeUpdate value. + String evictPolicy = getQueryCache().getEvictPolicy(); + + if(evictPolicy.equalsIgnoreCase("timestamp")) + assertEquals(cacheSizeBeforeUpdate, cacheSizeAfterUpdate); + + this.recreateData = false; + } + + public void testEviction() { + loadQueryCache(); + try { + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } + updateAnEntity(); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + String insert1 = "insert into part(partno,parttype,name,cost,mass)" + + " values(13,'PartBase','breakes',1000.0,100.0)"; + em.createNativeQuery(insert1).executeUpdate(); + String insert2 = "insert into supplier_part(suppliers_sid," + + "supplies_partno) values(1,13)"; + em.createNativeQuery(insert2).executeUpdate(); + + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + + String sql = "select partno from part where cost > 120 "; + Query nativeq = em.createNativeQuery(sql); + List nativelist = nativeq.getResultList(); + + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + Query q = em.createQuery("select p from PartBase p where p.cost>?1"); + q.setParameter(1, new Double(120)); + List jpalist = q.getResultList(); + + em.getTransaction().commit(); + em.close(); + + // The resultlist of nativelist and jpalist should be the same + // in both eviction policies(dafault/timestamp) + assertEquals(nativelist.size(),jpalist.size()); + + this.deleteData = true; + this.recreateData = true; + } + + private void loadQueryCache() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + String qry = "select p from PartBase p where p.cost > ?1"; + for (int i=120; i<155; i++) { + Query q = em.createQuery(qry); + q.setParameter(1, new Double(i)); + q.getResultList(); + } + em.getTransaction().commit(); + em.close(); + } + + private void updateAnEntity() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + //Update entity + PartBase p = em.find(PartBase.class,11); + double oldcost = p.getCost(); + if (p != null) + p.setCost((oldcost + 10.0)); + + em.getTransaction().commit(); + em.close(); + } + + private ConcurrentQueryCache getQueryCache() { + OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast(emf); + QueryResultCacheImpl scache = (QueryResultCacheImpl) oemf. + getQueryResultCache(); + + return (ConcurrentQueryCache ) scache.getDelegate(); + } + + private int queryCacheGet() { + ConcurrentQueryCache dcache = getQueryCache(); + CacheMap map = dcache.getCacheMap(); + return map.size(); + } + + public void tearDown() throws Exception { + if (deleteData) + deleteAllData(); + } + + private void reCreateData() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Supplier s1 = new Supplier(1, "S1"); + em.persist(s1); + Supplier s2 = new Supplier(2, "S2"); + em.persist(s2); + Supplier s3 = new Supplier(3, "S3"); + em.persist(s3); + + PartBase p1 = new PartBase(10, "Wheel", 150, 15.00); + em.persist(p1); + PartBase p2 = new PartBase(11, "Frame", 550.00, 25.00); + em.persist(p2); + PartBase p3 = new PartBase(12, "HandleBar", 125.00, 80.00); + em.persist(p3); + + s1.addPart(p1).addPart(p2).addPart(p3); + s2.addPart(p1).addPart(p3); + + PartComposite p4 = new PartComposite(20, "Bike", 180, 1.0); + em.persist(p4); + p4.addSubPart(em, 2, p1).addSubPart(em, 1, p2).addSubPart(em, 1, p3); + + em.getTransaction().commit(); + em.close(); + } + + private void deleteAllData() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + em.createNativeQuery("delete from supplier_part").executeUpdate(); + em.createQuery("delete from PartBase s").executeUpdate(); + em.createQuery("delete from Supplier s").executeUpdate(); + em.createQuery("delete from Usage u").executeUpdate(); + em.createQuery("delete from Part p").executeUpdate(); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Usage.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Usage.java new file mode 100644 index 000000000..f17076fc4 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Usage.java @@ -0,0 +1,95 @@ +/* + * 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.query.cache; + +import javax.persistence.*; + +import org.apache.openjpa.persistence.DataCache; + +@Entity +@DataCache(timeout=100000) + +public class Usage { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + int id; + int quantity; + + @ManyToOne + Part child; + @ManyToOne + PartComposite parent ; + + @Version + long version; + + + public Usage(PartComposite p, int quantity, Part subpart) { + parent=p; + this.quantity=quantity; + parent.getPartsUsed().add(this); + setChild(subpart); + subpart.getUsedIn().add(this); + } + + // JPA entity needs a public no-arg constructor ! + public Usage() {} + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Part getParent() { + return parent; + } + + public void setParent(PartComposite parent) { + this.parent = parent; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public Part getChild() { + return child; + } + + public void setChild(Part child) { + this.child = child; + } + + public String toString() { + return "Usage:"+id+" quantity:"+quantity+" child:"+child.getPartno()+" parent"+parent.getPartno(); + } + + + + + +}