OPENJPA-2099: Align Slice threading model and Select reuse threading model

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1235594 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2012-01-25 02:01:30 +00:00
parent 49adef7c61
commit 816280b17b
5 changed files with 124 additions and 31 deletions

View File

@ -83,13 +83,14 @@ import org.apache.openjpa.util.UserException;
* @author Abe White
* @nojavadoc
*/
@SuppressWarnings({ "serial", "deprecation" })
public class JDBCStoreQuery
extends ExpressionStoreQuery {
private static final Table INVALID = new Table();
// add all standard filter and aggregate listeners to these maps
private static final Map _listeners = new HashMap();
private static final Map<String,FilterListener> _listeners = new HashMap<String, FilterListener>();
static {
// deprecated extensions
@ -186,8 +187,8 @@ public class JDBCStoreQuery
ExpContext ctx = new ExpContext(_store, params, fetch);
// add selects with populate WHERE conditions to list
List sels = new ArrayList(mappings.length);
List selMappings = new ArrayList(mappings.length);
List<Select> sels = new ArrayList<Select>(mappings.length);
List<ClassMapping> selMappings = new ArrayList<ClassMapping>(mappings.length);
BitSet subclassBits = new BitSet();
BitSet nextBits = new BitSet();
boolean unionable = createWhereSelects(sels, mappings, selMappings,
@ -335,8 +336,8 @@ public class JDBCStoreQuery
* Generate the selects with WHERE conditions needed to execute the query
* for the given mappings.
*/
private boolean createWhereSelects(List sels, ClassMapping[] mappings,
List selMappings, boolean subclasses, BitSet subclassBits,
private boolean createWhereSelects(List<Select> sels, ClassMapping[] mappings,
List<ClassMapping> selMappings, boolean subclasses, BitSet subclassBits,
BitSet nextBits, ExpressionFactory[] facts, QueryExpressions[] exps,
QueryExpressionsState[] states, ExpContext ctx, int subclassMode) {
Number optHint = (Number) ctx.fetch.getHint
@ -364,12 +365,12 @@ public class JDBCStoreQuery
else if (this.ctx.isUnique())
sel.setExpectedResultCount(1, false);
List selectFrom = getJoinedTableMeta(sel);
List<ClassMapping> selectFrom = getJoinedTableMeta(sel);
int size = 0;
if (selectFrom != null) {
size = selectFrom.size();
for (int j = 0; j < size; j++) {
ClassMapping vert = (ClassMapping)selectFrom.get(j);
ClassMapping vert = selectFrom.get(j);
selMappings.add(vert);
if (j == size - 1) {
nextBits.set(sels.size());
@ -388,7 +389,7 @@ public class JDBCStoreQuery
nextBits.set(sels.size());
sels.add(sel);
} else
sels.add(sel.fullClone(1));
sels.add((Select)sel.fullClone(1));
}
}
@ -400,17 +401,17 @@ public class JDBCStoreQuery
return unionable;
}
private List getJoinedTableMeta(Select sel) {
List selectFrom = sel.getJoinedTableClassMeta();
List exSelectFrom = sel.getExcludedJoinedTableClassMeta();
private List<ClassMapping> getJoinedTableMeta(Select sel) {
List<ClassMapping> selectFrom = sel.getJoinedTableClassMeta();
List<ClassMapping> exSelectFrom = sel.getExcludedJoinedTableClassMeta();
if (exSelectFrom == null)
return selectFrom;
if (selectFrom == null)
return null;
int size = selectFrom.size();
List retList = new ArrayList(size);
List<ClassMapping> retList = new ArrayList<ClassMapping>(size);
for (int i = 0; i < size; i++) {
Object obj = selectFrom.get(i);
ClassMapping obj = selectFrom.get(i);
if (!exSelectFrom.contains(obj))
retList.add(obj);
}
@ -430,15 +431,15 @@ public class JDBCStoreQuery
|| !hasVerticalSubclasses(mapping))
return new ClassMapping[] { mapping };
List subs = new ArrayList(4);
List<ClassMapping> subs = new ArrayList<ClassMapping>(4);
addSubclasses(mapping, subs);
return (ClassMapping[]) subs.toArray(new ClassMapping[subs.size()]);
return subs.toArray(new ClassMapping[subs.size()]);
}
/**
* Recursive helper to add mappings for subclasses to the given list.
*/
private void addSubclasses(ClassMapping mapping, Collection subs) {
private void addSubclasses(ClassMapping mapping, Collection<ClassMapping> subs) {
// possible future optimizations:
// - if no fields in meta or its subclasses (and not in an
// already-selected table) are in the current fetch
@ -699,8 +700,8 @@ public class JDBCStoreQuery
ExpContext ctx = new ExpContext(_store, params, fetch);
// add selects with populate WHERE conditions to list
List sels = new ArrayList(mappings.length);
List selMappings = new ArrayList(mappings.length);
List<Select> sels = new ArrayList<Select>(mappings.length);
List<ClassMapping> selMappings = new ArrayList<ClassMapping>(mappings.length);
BitSet subclassBits = new BitSet();
BitSet nextBits = new BitSet();
boolean unionable = createWhereSelects(sels, mappings, selMappings,
@ -797,11 +798,11 @@ public class JDBCStoreQuery
(org.apache.openjpa.jdbc.kernel.exps.Math) value;
Val value1 = mathVal.getVal1();
Object val1 = getValue(value1, ob, params, sm);
Class c1 = value1.getType();
Class<?> c1 = value1.getType();
Val value2 = mathVal.getVal2();
Object val2 = getValue(value2, ob, params, sm);
Class c2 = value2.getType();
Class<?> c2 = value2.getType();
String op = mathVal.getOperation();
if (op.equals(org.apache.openjpa.jdbc.kernel.exps.Math.ADD))
@ -942,7 +943,7 @@ public class JDBCStoreQuery
org.apache.openjpa.jdbc.kernel.exps.Abs absVal =
(org.apache.openjpa.jdbc.kernel.exps.Abs) value;
Object val = getValue(absVal.getValue(), ob, params, sm);
Class c = val.getClass();
Class<?> c = val.getClass();
if (c == Integer.class)
return Integer.valueOf(java.lang.Math.abs(((Integer) val).intValue()));
else if (c == Float.class)
@ -959,7 +960,7 @@ public class JDBCStoreQuery
org.apache.openjpa.jdbc.kernel.exps.Sqrt sqrtVal =
(org.apache.openjpa.jdbc.kernel.exps.Sqrt) value;
Object val = getValue(sqrtVal.getValue(), ob, params, sm);
Class c = val.getClass();
Class<?> c = val.getClass();
if (c == Integer.class)
return Double.valueOf(java.lang.Math.sqrt(((Integer) val).doubleValue()));
else if (c == Float.class)

View File

@ -18,6 +18,9 @@
*/
package org.apache.openjpa.jdbc.sql;
import java.util.HashMap;
import java.util.Map;
import org.apache.openjpa.jdbc.schema.Column;
@ -31,7 +34,7 @@ import org.apache.openjpa.jdbc.schema.Column;
* select.
* <br><b>NOTE:</b>
* The primary assumption of usage is that the binding parameter values to a cached select and
* executing it are carried out in the same thread.
* executing it are carried out in the same (or <em>equivalent</em>) thread.
* <br>
* The parameter is further qualified by whether it is user specified (e.g. from a query parameter)
* or internally generated (e.g. a discriminator value for inheritance join). A database column can
@ -51,7 +54,7 @@ public class BindParameter {
private final Column _column;
// key of this parameter
private final Object _key;
private ThreadLocal<Object> _value = new ThreadLocal<Object>();
private Map<Thread, Object> _value = new HashMap<Thread, Object>();
/**
* Constructs a parameter with given key, column and user flag.
@ -67,21 +70,34 @@ public class BindParameter {
_key = key;
_user = user;
_column = column;
_value.set(value);
_value.put(Thread.currentThread(), value);
}
/**
* Gets the value bound to this parameter. Can be null.
* Gets the value bound to this parameter for the calling thread. Can be null.
*
* @exception if the current thread or its equivalent never bound a value
* to this parameter.
*/
public Object getValue() {
return _value.get();
Thread current = Thread.currentThread();
if (!_value.containsKey(current)) {
// calling thread may be a child of the thread that inserted the value.
// This is for SliceThread
for (Map.Entry<Thread, Object> e : _value.entrySet()) {
if (isEquivalent(e.getKey(), current))
return e.getValue();
}
throw new IllegalStateException("No value for " + current + ". Known threads " + _value.keySet());
}
return _value.get(current);
}
/**
* Binds the given value to this parameter. Can be null.
*/
public void setValue(Object value) {
_value.set(value);
_value.put(Thread.currentThread(),value);
}
/**
@ -105,4 +121,9 @@ public class BindParameter {
public Object getKey() {
return _key;
}
boolean isEquivalent(Thread a, Thread b) {
if (a == b) return true;
return a.equals(b) || b.equals(a);
}
}

View File

@ -63,11 +63,39 @@ import org.apache.openjpa.util.Id;
import org.apache.openjpa.util.InternalException;
/**
* Standard {@link Select} implementation. Usage note: though this class
* implements {@link Joins}, it should not be used for joining directly.
* Instead, use the return value of {@link #newJoins}.
* Standard {@link Select} implementation.
* <br>
* This critical construct holds information to form a SQL <tt>SELECT</tt> statement.
* The structure imitates SQL components such a {@link Selects projection terms},
* {@link SelectImpl#_where <tt>WHERE</tt>} clause, {@link SelectImpl#_ordering
* <tt>ORDER BY</tt>} clause, {@link SelectImpl#_subsels sub-queries} etc.
* It also maintains tables and columns involved in the selection along with
* their join information.
* <br>
* This structure is capable of holding other independent instances for fetching
* data for related fields by either eager or parallel execution.
* <br>
* This instance is {@link #execute(JDBCStore, JDBCFetchConfiguration) executable}.
* After execution, this instance becomes {@link #isReadOnly() immutable} i.e. any
* structural modification is {@link #assertMutable() prohibited}. The only modification
* possible is by {@link #bindParameter(SQLBuffer, Object[], Column[], PathJoins) binding}
* different values to existing {@link BindParameter parameters}.
* <br>
* The {@link SelectResult result} from executing this instance of a <tt>JDBC</tt>-enabled
* database is available for field values to be populated in in-memory persistent entities.
* The access mechanics is critical to performance, and hence an {@link Selects#isOptimizable()
* heuristics} is applied for optimization.
* <br>
* Usage note:
* <LI>a) though this class implements {@link Joins}, it should not be used for
* joining directly. Instead, use the return value of {@link #newJoins}.
* <LI>b) if this instance is concurrently shared based on {@link JDBCConfiguration#getSelectCacheEnabled()
* <tt>openjpa.jdbc.CachesSelect</tt>} setting, then the caller must ensure that the
* parameters are bound in the same or {@link BindParameter#isEquivalent(Thread, Thread) equivalent}
* thread in which this instance is executed.
*
* @author Abe White
* @author Pinaki Poddar
* @nojavadoc
*/
public class SelectImpl

View File

@ -50,6 +50,36 @@ public class SliceThread extends Thread {
return _parent;
}
/**
* This thread equals itself (of course), its parent and any of its siblings.
* Essentially all slice threads are equal.
* <br>
* Note: this definition of equality breaks the core definition i.e. if
* <tt>P</tt> is parent thread of a slice child <tt>S</tt>, then
* <tt>S.equals(P)</tt>, but <em>not</em> <tt>P.equals(S)</tt>.
*/
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (other instanceof SliceThread) {
return ((SliceThread)other)._parent == this._parent;
}
return this._parent == other;
}
/**
* Hash code of this thread is same as its parent.
*/
@Override
public int hashCode() {
return _parent.hashCode();
}
@Override
public String toString() {
return '[' + getClass().getSimpleName() + '-'+ getName() + " child of " + _parent + ']';
}
/**
* Create a cached pool of <em>slice</em> threads.
* The thread factory creates specialized threads for preferential locking treatment.

View File

@ -357,4 +357,17 @@ public class TestBasic extends SliceTestCase {
assertEquals("newslice", SlicePersistence.getSlice(newP));
}
public void testSliceThreadEqualsParentAndSiblings() {
Thread parent = new Thread();
SliceThread s1 = new SliceThread(parent, null);
SliceThread s2 = new SliceThread(parent, null);
assertEquals(s1, parent);
assertEquals(s2, parent);
assertEquals(s1, s2);
assertNotSame(parent, s1);
assertNotSame(parent, s2);
assertNotSame(s1, s2);
}
}