mirror of https://github.com/apache/openjpa.git
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
This commit is contained in:
parent
16e7a11f81
commit
e2388e9b3c
|
@ -18,12 +18,16 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.openjpa.datacache;
|
package org.apache.openjpa.datacache;
|
||||||
|
|
||||||
|
import java.security.AccessController;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||||
import org.apache.openjpa.event.RemoteCommitEvent;
|
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.Configurable;
|
||||||
import org.apache.openjpa.lib.conf.Configuration;
|
import org.apache.openjpa.lib.conf.Configuration;
|
||||||
import org.apache.openjpa.lib.log.Log;
|
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.Localizer;
|
||||||
import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
|
import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
|
||||||
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet;
|
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet;
|
||||||
|
@ -63,34 +68,65 @@ public abstract class AbstractQueryCache
|
||||||
*/
|
*/
|
||||||
protected Log log;
|
protected Log log;
|
||||||
|
|
||||||
|
protected ConcurrentHashMap<String,Long> entityTimestampMap = null;
|
||||||
private boolean _closed = false;
|
private boolean _closed = false;
|
||||||
|
|
||||||
|
protected String evictPolicy = "default";
|
||||||
|
|
||||||
public void initialize(DataCacheManager manager) {
|
public void initialize(DataCacheManager manager) {
|
||||||
|
if(evictPolicy.equalsIgnoreCase("timestamp")) {
|
||||||
|
entityTimestampMap = new ConcurrentHashMap<String,Long>();
|
||||||
|
|
||||||
|
// 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) {
|
public void onTypesChanged(TypesChangedEvent ev) {
|
||||||
writeLock();
|
writeLock();
|
||||||
Collection keys = null;
|
Collection keys = null;
|
||||||
try {
|
if (!evictPolicy.equalsIgnoreCase("timestamp")) {
|
||||||
if (hasListeners())
|
try {
|
||||||
fireEvent(ev);
|
if (hasListeners())
|
||||||
keys = keySet();
|
fireEvent(ev);
|
||||||
} finally {
|
keys = keySet();
|
||||||
writeUnlock();
|
} 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryKey qk;
|
||||||
|
List<QueryKey> removes = null;
|
||||||
|
for (Object o: keys) {
|
||||||
|
qk = (QueryKey) o;
|
||||||
|
if (qk.changeInvalidatesQuery(ev.getTypes())) {
|
||||||
|
if (removes == null)
|
||||||
|
removes = new ArrayList<QueryKey>();
|
||||||
|
removes.add(qk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removes != null)
|
||||||
|
removeAllInternal(removes);
|
||||||
|
} else {
|
||||||
|
Collection changedTypes = ev.getTypes();
|
||||||
|
HashMap<String,Long> changedClasses =
|
||||||
|
new HashMap<String,Long>();
|
||||||
|
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) {
|
public QueryResult get(QueryKey key) {
|
||||||
|
@ -319,4 +355,50 @@ public abstract class AbstractQueryCache
|
||||||
protected Collection newListenerCollection() {
|
protected Collection newListenerCollection() {
|
||||||
return new ConcurrentReferenceHashSet (ConcurrentReferenceHashSet.WEAK);
|
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<String,Long> 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<Long> getAllEntityTimestampFromMap(List<String> keyList) {
|
||||||
|
ArrayList<Long> tmval = null;
|
||||||
|
if (entityTimestampMap != null) {
|
||||||
|
for (String s: keyList) {
|
||||||
|
if (entityTimestampMap.containsKey(s)) {
|
||||||
|
if(tmval == null)
|
||||||
|
tmval = new ArrayList<Long>();
|
||||||
|
tmval.add(entityTimestampMap.get(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,4 +137,11 @@ public class ConcurrentQueryCache
|
||||||
protected Collection keySet() {
|
protected Collection keySet() {
|
||||||
return _cache.keySet ();
|
return _cache.keySet ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the eviction policy of the query cache
|
||||||
|
*/
|
||||||
|
public String getEvictPolicy() {
|
||||||
|
return super.evictPolicy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.apache.commons.collections.map.LinkedMap;
|
import org.apache.commons.collections.map.LinkedMap;
|
||||||
|
@ -123,6 +124,30 @@ public class QueryCacheStoreQuery
|
||||||
if (res.isEmpty())
|
if (res.isEmpty())
|
||||||
return Collections.EMPTY_LIST;
|
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<String> classNames = qk.getAcessPathClassNames();
|
||||||
|
List<String> keyList = new ArrayList<String>();
|
||||||
|
keyList.addAll(classNames);
|
||||||
|
|
||||||
|
List<Long> 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;
|
int projs = getContext().getProjectionAliases().length;
|
||||||
if (projs == 0) {
|
if (projs == 0) {
|
||||||
// make sure the data cache contains the oids for the query result;
|
// make sure the data cache contains the oids for the query result;
|
||||||
|
@ -572,6 +597,7 @@ public class QueryCacheStoreQuery
|
||||||
QueryResult res = null;
|
QueryResult res = null;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
res = new QueryResult(_qk, _data.values());
|
res = new QueryResult(_qk, _data.values());
|
||||||
|
res.setTimestamp(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
_cache.put(_qk, res);
|
_cache.put(_qk, res);
|
||||||
abortCaching();
|
abortCaching();
|
||||||
|
|
|
@ -200,6 +200,8 @@ public class QueryKey
|
||||||
// since the class change framework deals with least-derived types,
|
// since the class change framework deals with least-derived types,
|
||||||
// record the least-derived access path types
|
// record the least-derived access path types
|
||||||
meta = metas[i];
|
meta = metas[i];
|
||||||
|
if (meta.getDataCache() != null)
|
||||||
|
accessPathClassNames.add(meta.getDescribedType().getName());
|
||||||
while (meta.getPCSuperclass() != null)
|
while (meta.getPCSuperclass() != null)
|
||||||
meta = meta.getPCSuperclassMetaData();
|
meta = meta.getPCSuperclassMetaData();
|
||||||
|
|
||||||
|
@ -232,6 +234,7 @@ public class QueryKey
|
||||||
if (metas[i].getDataCache() == null)
|
if (metas[i].getDataCache() == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
accessPathClassNames.add(metas[i].getDescribedType().getName());
|
||||||
subTimeout = metas[i].getDataCacheTimeout();
|
subTimeout = metas[i].getDataCacheTimeout();
|
||||||
if (subTimeout != -1 && subTimeout < timeout)
|
if (subTimeout != -1 && subTimeout < timeout)
|
||||||
timeout = subTimeout;
|
timeout = subTimeout;
|
||||||
|
@ -466,4 +469,12 @@ public class QueryKey
|
||||||
_rangeEnd = in.readLong ();
|
_rangeEnd = in.readLong ();
|
||||||
_timeout = in.readInt ();
|
_timeout = in.readInt ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set of the accessPathClassnames that exists in the query
|
||||||
|
* @return -- Returns a set of accesspath classnames.
|
||||||
|
*/
|
||||||
|
public Set<String> getAcessPathClassNames() {
|
||||||
|
return this._accessPathClassNames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class QueryResult
|
||||||
|
|
||||||
private final long _ex;
|
private final long _ex;
|
||||||
|
|
||||||
|
private long _timestamp = 0L;
|
||||||
/**
|
/**
|
||||||
* Constructor; supply corresponding query key and result data.
|
* Constructor; supply corresponding query key and result data.
|
||||||
*/
|
*/
|
||||||
|
@ -64,4 +65,20 @@ public class QueryResult
|
||||||
public boolean isTimedOut() {
|
public boolean isTimedOut() {
|
||||||
return _ex != -1 && _ex < System.currentTimeMillis();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
81
openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java
vendored
Normal file
81
openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/cache/Part.java
vendored
Normal file
|
@ -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<Usage> usedIn = new ArrayList<Usage>();
|
||||||
|
|
||||||
|
@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<Usage> getUsedIn() {
|
||||||
|
return usedIn;
|
||||||
|
}
|
||||||
|
public void setUsedIn(Collection<Usage> usedIn) {
|
||||||
|
this.usedIn = usedIn;
|
||||||
|
}
|
||||||
|
public int getInventory() {
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
public void setInventory(int inventory) {
|
||||||
|
this.inventory = inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Supplier> suppliers = new ArrayList<Supplier>();
|
||||||
|
|
||||||
|
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<Supplier> getSuppliers() {
|
||||||
|
return suppliers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuppliers(List<Supplier> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Usage> partsUsed = new ArrayList<Usage>();
|
||||||
|
|
||||||
|
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<Usage> getPartsUsed() {
|
||||||
|
return partsUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartsUsed(Collection<Usage> partsUsed) {
|
||||||
|
this.partsUsed = partsUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAssemblyTime() {
|
||||||
|
return assemblyTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssemblyTime(double assemblyTime) {
|
||||||
|
this.assemblyTime = assemblyTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<PartBase> supplies = new ArrayList<PartBase>();
|
||||||
|
|
||||||
|
@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<PartBase> getSupplies() {
|
||||||
|
return supplies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupplies(List<PartBase> supplies) {
|
||||||
|
this.supplies = supplies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
return "Supplier:"+sid+" name:+"+name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue