Cleanup and fixes to changes for OPENJPA-168.

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@523425 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
A. Abram White 2007-03-28 17:43:35 +00:00
parent 3a5d06436c
commit 1679c05c89
13 changed files with 123 additions and 213 deletions

View File

@ -43,7 +43,6 @@ import org.apache.openjpa.jdbc.sql.SQLExceptions;
import org.apache.openjpa.jdbc.sql.SQLFactory; import org.apache.openjpa.jdbc.sql.SQLFactory;
import org.apache.openjpa.jdbc.sql.Select; import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.jdbc.sql.SelectExecutor; import org.apache.openjpa.jdbc.sql.SelectExecutor;
import org.apache.openjpa.jdbc.sql.SelectImpl;
import org.apache.openjpa.jdbc.sql.Union; import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.FetchConfiguration; import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.LockManager; import org.apache.openjpa.kernel.LockManager;
@ -370,10 +369,7 @@ public class JDBCStoreManager
JDBCFetchConfiguration.EAGER_JOIN, true, false)) JDBCFetchConfiguration.EAGER_JOIN, true, false))
return null; return null;
sel.wherePrimaryKey(sm.getObjectId(), mapping, this); sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
// Set the expectedResultCount for the select as 1 as a single sel.setExpectedResultCount(1, false);
// object is being loaded. force = true is an indicator that it is
// internally generated value
sel.setExpectedResultCount(1,true);
return sel.execute(this, fetch); return sel.execute(this, fetch);
} }
@ -389,7 +385,7 @@ public class JDBCStoreManager
JDBCFetchConfiguration.EAGER_JOIN); JDBCFetchConfiguration.EAGER_JOIN);
Union union = _sql.newUnion(mappings.length); Union union = _sql.newUnion(mappings.length);
union.setExpectedResultCount(1,true); union.setExpectedResultCount(1, false);
if (fetch.getSubclassFetchMode(mapping) != fetch.EAGER_JOIN) if (fetch.getSubclassFetchMode(mapping) != fetch.EAGER_JOIN)
union.abortUnion(); union.abortUnion();
union.select(new Union.Selector() { union.select(new Union.Selector() {

View File

@ -310,32 +310,25 @@ public class JDBCStoreQuery
List selMappings, boolean subclasses, BitSet subclassBits, List selMappings, boolean subclasses, BitSet subclassBits,
BitSet nextBits, ExpressionFactory[] facts, QueryExpressions[] exps, BitSet nextBits, ExpressionFactory[] facts, QueryExpressions[] exps,
QueryExpressionsState[] states, ExpContext ctx, int subclassMode) { QueryExpressionsState[] states, ExpContext ctx, int subclassMode) {
Number optHint = (Number) ctx.fetch.getHint
(QueryHints.HINT_RESULT_COUNT);
ClassMapping[] verts; ClassMapping[] verts;
boolean unionable = true; boolean unionable = true;
Select sel; Select sel;
Object optHint = null;
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
// determine vertical mappings to select separately // determine vertical mappings to select separately
verts = getVerticalMappings(mappings[i], subclasses, exps[i], verts = getVerticalMappings(mappings[i], subclasses, exps[i],
subclassMode); subclassMode);
if (verts.length == 1 && subclasses) if (verts.length == 1 && subclasses)
subclassBits.set(sels.size()); subclassBits.set(sels.size());
// create criteria select and clone for each vert mapping // create criteria select and clone for each vert mapping
sel = ((JDBCExpressionFactory) facts[i]).getSelectConstructor(). sel = ((JDBCExpressionFactory) facts[i]).getSelectConstructor().
evaluate(ctx, null, null, exps[i], states[i]); evaluate(ctx, null, null, exps[i], states[i]);
// It means it is coming from getSingleResult so set the if (optHint != null)
// expectedResultCount to 1.force = true indicates that this is sel.setExpectedResultCount(optHint.intValue(), true);
// internally generated value else if (this.ctx.isUnique())
if (this.ctx.isUnique()) sel.setExpectedResultCount(1, false);
sel.setExpectedResultCount(1,true);
// It means this is coming from getResultList so set the
// expectedResultCount based on any optimize hint if provided
else {
if ((optHint = ctx.fetch.getHint
(QueryHints.HINT_RESULT_COUNT))!= null)
sel.setExpectedResultCount
(((Integer)optHint).intValue(),false);
}
for (int j = 0; j < verts.length; j++) { for (int j = 0; j < verts.length; j++) {
selMappings.add(verts[j]); selMappings.add(verts[j]);
if (j == verts.length - 1) { if (j == verts.length - 1) {

View File

@ -236,9 +236,8 @@ class LRSProxyMap
final JDBCFetchConfiguration fetch = store.getFetchConfiguration(); final JDBCFetchConfiguration fetch = store.getFetchConfiguration();
final ClassMapping[] clss = _strat.getIndependentValueMappings(true); final ClassMapping[] clss = _strat.getIndependentValueMappings(true);
final Joins[] resJoins = new Joins[Math.max(1, clss.length)]; final Joins[] resJoins = new Joins[Math.max(1, clss.length)];
Union union = store.getSQLFactory().newUnion Union union = store.getSQLFactory().newUnion(Math.max(1, clss.length));
(Math.max(1, clss.length)); union.setExpectedResultCount(1, false);
union.setExpectedResultCount(1,true);
if (fetch.getSubclassFetchMode(_strat.getFieldMapping(). if (fetch.getSubclassFetchMode(_strat.getFieldMapping().
getElementMapping().getTypeMapping()) getElementMapping().getTypeMapping())
!= JDBCFetchConfiguration.EAGER_JOIN) != JDBCFetchConfiguration.EAGER_JOIN)

View File

@ -578,7 +578,7 @@ public class RelationFieldStrategy
// back to our fk table if not an inverse mapping (in which case we // back to our fk table if not an inverse mapping (in which case we
// can just make sure the inverse cols == our pk values) // can just make sure the inverse cols == our pk values)
Union union = store.getSQLFactory().newUnion(rels.length); Union union = store.getSQLFactory().newUnion(rels.length);
union.setExpectedResultCount(1,true); union.setExpectedResultCount(1, false);
if (fetch.getSubclassFetchMode(field.getTypeMapping()) if (fetch.getSubclassFetchMode(field.getTypeMapping())
!= JDBCFetchConfiguration.EAGER_JOIN) != JDBCFetchConfiguration.EAGER_JOIN)
union.abortUnion(); union.abortUnion();

View File

@ -28,9 +28,10 @@ import org.apache.openjpa.jdbc.schema.Sequence;
*/ */
public class DB2Dictionary public class DB2Dictionary
extends AbstractDB2Dictionary { extends AbstractDB2Dictionary {
// variables to support optimize clause
public String optimizeClause = "optimize for"; public String optimizeClause = "optimize for";
public String rowClause = "row"; public String rowClause = "row";
public DB2Dictionary() { public DB2Dictionary() {
platform = "DB2"; platform = "DB2";
validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM " validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM "
@ -197,60 +198,13 @@ public class DB2Dictionary
} }
} }
/** Based on the expectedResultCount of the select create the optimize
* for clause
*/
public String getOptimizeClause(JDBCFetchConfiguration fetch,
int expectedResultCount) {
Integer rows = null;
StringBuffer optimizeString = new StringBuffer();
if (expectedResultCount != 0)
optimizeString.append(" ").append(optimizeClause).append(" ")
.append(expectedResultCount).append(" ")
.append(rowClause).append(" ");
return optimizeString.toString();
}
/** Override the DBDictionary toSelect to call getOptimizeClause and append
* to the select string
*/
public SQLBuffer toSelect(SQLBuffer selects, JDBCFetchConfiguration fetch,
SQLBuffer from, SQLBuffer where, SQLBuffer group,
SQLBuffer having, SQLBuffer order,
boolean distinct, boolean forUpdate, long start, long end,
int expectedResultCount) {
String optimizeString = null;
SQLBuffer selString = toOperation(getSelectOperation(fetch),
selects, from, where,
group, having, order, distinct,
forUpdate, start, end);
if (fetch != null)
optimizeString = getOptimizeClause(fetch, expectedResultCount);
if (optimizeString != null && optimizeString.length() > 0)
selString.append(optimizeString);
return selString;
}
/** Override the DBDictionary toSelect to pass expectedResultcount to the
* other toSelect method
*/
public SQLBuffer toSelect(Select sel, boolean forUpdate, public SQLBuffer toSelect(Select sel, boolean forUpdate,
JDBCFetchConfiguration fetch) { JDBCFetchConfiguration fetch) {
sel.addJoinClassConditions(); SQLBuffer buf = super.toSelect(sel, forUpdate, fetch);
boolean update = forUpdate && sel.getFromSelect() == null; if (sel.getExpectedResultCount() > 0)
SQLBuffer select = getSelects(sel, false, update); buf.append(" ").append(optimizeClause).append(" ").
SQLBuffer ordering = null; append(String.valueOf(sel.getExpectedResultCount())).
if (!sel.isAggregate() || sel.getGrouping() != null) append(" ").append(rowClause);
ordering = sel.getOrdering(); return buf;
SQLBuffer from;
if (sel.getFromSelect() != null)
from = getFromSelect(sel, forUpdate);
else
from = getFrom(sel, update);
SQLBuffer where = getWhere(sel, update);
return toSelect(select, fetch, from, where, sel.getGrouping(),
sel.getHaving(), ordering, sel.isDistinct(), forUpdate,
sel.getStartIndex(),
sel.getEndIndex(),sel.getExpectedResultCount());
} }
} }

View File

@ -47,8 +47,7 @@ public class LogicalUnion
private static final Localizer _loc = Localizer.forPackage private static final Localizer _loc = Localizer.forPackage
(LogicalUnion.class); (LogicalUnion.class);
protected int expectedResultCount = 0;
protected boolean force = false;
protected final UnionSelect[] sels; protected final UnionSelect[] sels;
protected final DBDictionary dict; protected final DBDictionary dict;
protected final ClassMapping[] mappings; protected final ClassMapping[] mappings;
@ -157,6 +156,16 @@ public class LogicalUnion
sels[i].setLRS(lrs); sels[i].setLRS(lrs);
} }
public int getExpectedResultCount() {
return sels[0].getExpectedResultCount();
}
public void setExpectedResultCount(int expectedResultCount,
boolean force) {
for (int i = 0; i < sels.length; i++)
sels[i].setExpectedResultCount(expectedResultCount, force);
}
public int getJoinSyntax() { public int getJoinSyntax() {
return sels[0].getJoinSyntax(); return sels[0].getJoinSyntax();
} }
@ -208,13 +217,9 @@ public class LogicalUnion
return res; return res;
} }
if (this.getExpectedResultCount()== 1) { if (getExpectedResultCount() == 1) {
AbstractResult res; AbstractResult res;
for (int i = 0; i < sels.length; i++) { for (int i = 0; i < sels.length; i++) {
// For each select set the expected result count to 1
// and force true indicating that this internally generated
// value
sels[i].sel.setExpectedResultCount(1,true);
res = (AbstractResult) sels[i].execute(store, fetch, res = (AbstractResult) sels[i].execute(store, fetch,
lockLevel); lockLevel);
res.setBaseMapping(mappings[i]); res.setBaseMapping(mappings[i]);
@ -302,8 +307,6 @@ public class LogicalUnion
protected final int pos; protected final int pos;
protected int orders = 0; protected int orders = 0;
protected List orderIdxs = null; protected List orderIdxs = null;
protected int expectedResultCount = 0;
protected boolean force = false;
public UnionSelect(SelectImpl sel, int pos) { public UnionSelect(SelectImpl sel, int pos) {
this.sel = sel; this.sel = sel;
@ -839,17 +842,12 @@ public class LogicalUnion
} }
public int getExpectedResultCount() { public int getExpectedResultCount() {
return expectedResultCount; return sel.getExpectedResultCount();
} }
public void setExpectedResultCount(int expectedResultCount, public void setExpectedResultCount(int expectedResultCount,
boolean force) { boolean force) {
this.expectedResultCount = expectedResultCount; sel.setExpectedResultCount(expectedResultCount, force);
this.force = force;
}
public boolean isExpRsltCntForced() {
return force;
} }
} }
@ -931,18 +929,4 @@ public class LogicalUnion
return a1.length - a2.length; return a1.length - a2.length;
} }
} }
public int getExpectedResultCount() {
return expectedResultCount;
}
public void setExpectedResultCount(int expectedResultCount,
boolean force) {
this.expectedResultCount = expectedResultCount;
this.force = force;
}
public boolean isExpRsltCntForced() {
return force;
}
} }

View File

@ -79,6 +79,19 @@ public interface SelectExecutor {
*/ */
public void setLRS(boolean lrs); public void setLRS(boolean lrs);
/**
* The expected result count for the query.
*/
public int getExpectedResultCount();
/**
* The expected result count for the query.
*
* @param force if false, the count will be discarded if this select has
* any to-many eager joins that would throw off the result count
*/
public void setExpectedResultCount(int expectedResultCount, boolean force);
/** /**
* The join syntax for this select, as one of the syntax constants from * The join syntax for this select, as one of the syntax constants from
* {@link JoinSyntaxes}. * {@link JoinSyntaxes}.
@ -119,21 +132,4 @@ public interface SelectExecutor {
public Result execute(JDBCStore store, JDBCFetchConfiguration fetch, public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
int lockLevel) int lockLevel)
throws SQLException; throws SQLException;
/**
* Return the expected result count for the query
*/
public int getExpectedResultCount() ;
/**
* Set the expected result count for the query
* force indicates whether the count is internally generated
* or given by the user as optimize hint
*/
public void setExpectedResultCount(int expectedResultCount,boolean force) ;
/**
* Indicates whether the expectedResultCount is internally generated
*/
public boolean isExpRsltCntForced();
} }

View File

@ -82,6 +82,7 @@ public class SelectImpl
private static final int EAGER_TO_MANY = 2 << 10; private static final int EAGER_TO_MANY = 2 << 10;
private static final int RECORD_ORDERED = 2 << 11; private static final int RECORD_ORDERED = 2 << 11;
private static final int GROUPING = 2 << 12; private static final int GROUPING = 2 << 12;
private static final int FORCE_COUNT = 2 << 13;
private static final String[] TABLE_ALIASES = new String[16]; private static final String[] TABLE_ALIASES = new String[16];
private static final String[] ORDER_ALIASES = new String[16]; private static final String[] ORDER_ALIASES = new String[16];
@ -128,6 +129,7 @@ public class SelectImpl
private int _nullIds = 0; private int _nullIds = 0;
private int _orders = 0; private int _orders = 0;
private int _placeholders = 0; private int _placeholders = 0;
private int _expectedResultCount = 0;
// query clauses // query clauses
private SQLBuffer _ordering = null; private SQLBuffer _ordering = null;
@ -154,9 +156,6 @@ public class SelectImpl
private SelectImpl _from = null; private SelectImpl _from = null;
private SelectImpl _outer = null; private SelectImpl _outer = null;
private int expectedResultCount = 0;
private boolean force = false;
/** /**
* Helper method to return the proper table alias for the given alias index. * Helper method to return the proper table alias for the given alias index.
*/ */
@ -245,6 +244,22 @@ public class SelectImpl
_flags &= ~LRS; _flags &= ~LRS;
} }
public int getExpectedResultCount() {
// if the count isn't forced and we have to-many eager joins that could
// throw the count off, don't pay attention to it
if ((_flags & FORCE_COUNT) == 0 && hasEagerJoin(true))
return 0;
return _expectedResultCount;
}
public void setExpectedResultCount(int expectedResultCount, boolean force) {
_expectedResultCount = expectedResultCount;
if (force)
_flags |= FORCE_COUNT;
else
_flags &= ~FORCE_COUNT;
}
public int getJoinSyntax() { public int getJoinSyntax() {
return _joinSyntax; return _joinSyntax;
} }
@ -307,20 +322,6 @@ public class SelectImpl
JDBCFetchConfiguration fetch, int lockLevel) JDBCFetchConfiguration fetch, int lockLevel)
throws SQLException { throws SQLException {
boolean forUpdate = false; boolean forUpdate = false;
// ExpectedResultCount = 1 and force means that it is internally
// generated value for getSingleResult,single valued relationship.
// We need to check if there are any eager joins in the select if
// there are then the optimize for 1 row clause is not generated
// else we do. if !force then it is set by the user through hint
// and we do not check the eager joins
if (this.expectedResultCount == 1 && force ) {
if (this.hasEagerJoin(true))
this.setExpectedResultCount(0,false);
else
this.setExpectedResultCount(1,false);
}
if (!isAggregate() && _grouping == null) { if (!isAggregate() && _grouping == null) {
JDBCLockManager lm = store.getLockManager(); JDBCLockManager lm = store.getLockManager();
if (lm != null) if (lm != null)
@ -1503,6 +1504,7 @@ public class SelectImpl
sel._flags &= ~LRS; sel._flags &= ~LRS;
sel._flags &= ~EAGER_TO_ONE; sel._flags &= ~EAGER_TO_ONE;
sel._flags &= ~EAGER_TO_MANY; sel._flags &= ~EAGER_TO_MANY;
sel._flags &= ~FORCE_COUNT;
sel._joinSyntax = _joinSyntax; sel._joinSyntax = _joinSyntax;
if (_aliases != null) if (_aliases != null)
sel._aliases = new HashMap(_aliases); sel._aliases = new HashMap(_aliases);
@ -1548,6 +1550,7 @@ public class SelectImpl
for (int i = 0; i < sels; i++) { for (int i = 0; i < sels; i++) {
sel = (SelectImpl) whereClone(1); sel = (SelectImpl) whereClone(1);
sel._flags = _flags; sel._flags = _flags;
sel._expectedResultCount = _expectedResultCount;
sel._selects.addAll(_selects); sel._selects.addAll(_selects);
if (_ordering != null) if (_ordering != null)
sel._ordering = new SQLBuffer(_ordering); sel._ordering = new SQLBuffer(_ordering);
@ -2816,20 +2819,6 @@ public class SelectImpl
_idents = null; _idents = null;
} }
} }
public int getExpectedResultCount() {
return expectedResultCount;
}
public void setExpectedResultCount(int expectedResultCount,
boolean force) {
this.expectedResultCount = expectedResultCount;
this.force = force;
}
public boolean isExpRsltCntForced() {
return force;
}
} }
/** /**

View File

@ -15,10 +15,13 @@
*/ */
package org.apache.openjpa.kernel; package org.apache.openjpa.kernel;
/**
* Standard query hint keys.
*/
public interface QueryHints { public interface QueryHints {
/** Hint to specify the number of rows for the optimize /**
* clause for DB2 * Hint to specify the number of rows to optimize for.
*/ */
public static final String HINT_RESULT_COUNT = public static final String HINT_RESULT_COUNT =
"openjpa.hint.OptimizeResultCount"; "openjpa.hint.OptimizeResultCount";

View File

@ -1273,14 +1273,17 @@ public class QueryImpl
boolean next = rop.next(); boolean next = rop.next();
// extract single result; throw an exception if multiple results // extract single result; throw an exception if multiple results
// match and not constrainted by range, as per spec // match and not constrainted by range, or if a unique query with
// no results
Object single = null; Object single = null;
if (next) { if (next) {
single = rop.getResultObject(); single = rop.getResultObject();
if (range.end != range.start + 1 && rop.next()) if (range.end != range.start + 1 && rop.next())
throw new InvalidStateException(_loc.get("not-unique", throw new InvalidStateException(_loc.get("not-unique",
_class, _query)); _class, _query));
} } else if (_unique == Boolean.TRUE)
throw new InvalidStateException(_loc.get("no-result",
_class, _query));
// if unique set to false, use collection // if unique set to false, use collection
if (_unique == Boolean.FALSE) { if (_unique == Boolean.FALSE) {
@ -1290,10 +1293,6 @@ public class QueryImpl
return Arrays.asList(new Object[]{ single }); return Arrays.asList(new Object[]{ single });
} }
if (single == null)
throw new InvalidStateException(_loc.get("is-null",
_class, _query));
// return single result // return single result
return single; return single;
} finally { } finally {

View File

@ -259,7 +259,7 @@ executing-query-with-params: Executing query: [{0}] with parameters: {1}
not-unique: The query on candidate type "{0}" with filter "{1}" was \ not-unique: The query on candidate type "{0}" with filter "{1}" was \
configured to have a unique result, but more than one instance matched \ configured to have a unique result, but more than one instance matched \
the query. the query.
is-null: The query on candidate type "{0}" with filter "{1}" was \ no-result: The query on candidate type "{0}" with filter "{1}" was \
configured to have a unique result, but no instance matched \ configured to have a unique result, but no instance matched \
the query. the query.
serialized: Queries that have been serialized do not support this operation. serialized: Queries that have been serialized do not support this operation.

View File

@ -279,12 +279,12 @@ public class QueryImpl
*/ */
public Object getSingleResult() { public Object getSingleResult() {
_em.assertNotCloseInvoked(); _em.assertNotCloseInvoked();
// Indicate that this query returns single result.Later copied into // temporarily set query to unique so that a single result is validated
// select.expectedResultCount // and returned; unset again in case the user executes query again
// via getResultList
_query.setUnique(true); _query.setUnique(true);
try { try {
Object ob = execute(); return execute();
return ob;
} finally { } finally {
_query.setUnique(false); _query.setUnique(false);
} }
@ -367,16 +367,17 @@ public class QueryImpl
Filters.hintToSetter(getFetchPlan(), k, value); Filters.hintToSetter(getFetchPlan(), k, value);
} else if (k.startsWith("hint.")) { } else if (k.startsWith("hint.")) {
if ("hint.OptimizeResultCount".equals(k)) { if ("hint.OptimizeResultCount".equals(k)) {
if ((!(value instanceof String)&&! (value instanceof Integer)) if (value instanceof String) {
|| (value instanceof String &&(Integer.parseInt try {
((String)value)< 0))|| value = new Integer((String) value);
((value instanceof Integer) } catch (NumberFormatException nfe) {
&& (((Integer)value).intValue()<0))) }
}
if (!(value instanceof Number)
|| ((Number) value).intValue() < 0)
throw new ArgumentException(_loc.get throw new ArgumentException(_loc.get
("bad-hint-value", key), ("bad-query-hint-value", key, value), null, null,
null, null, false); false);
if (value instanceof String)
value = new Integer((String)value);
} }
_query.getFetchConfiguration().setHint(key, value); _query.getFetchConfiguration().setHint(key, value);
} }
@ -384,12 +385,7 @@ public class QueryImpl
throw new ArgumentException(_loc.get("bad-query-hint", key), throw new ArgumentException(_loc.get("bad-query-hint", key),
null, null, false); null, null, false);
return this; return this;
} } catch (Exception e) {
catch (NumberFormatException e1) {
throw new ArgumentException(_loc.get("bad-hint-value", key),
null, null, false);
}
catch (Exception e) {
throw PersistenceExceptions.toPersistenceException(e); throw PersistenceExceptions.toPersistenceException(e);
} }
} }

View File

@ -64,6 +64,7 @@ mult-results: Query returned multiple results: "{0}".
no-pos-named-params-mix: Cannot mix named and positional parameters in query \ no-pos-named-params-mix: Cannot mix named and positional parameters in query \
"{0}". "{0}".
bad-query-hint: "{0}" is not a recognized query hint. bad-query-hint: "{0}" is not a recognized query hint.
bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
detached: Cannot perform this operation on detached entity "{0}". detached: Cannot perform this operation on detached entity "{0}".
removed: Cannot perform this operation on removed entity "{0}". removed: Cannot perform this operation on removed entity "{0}".
bad-alias: There is no known entity class for entity name "{0}". It is \ bad-alias: There is no known entity class for entity name "{0}". It is \