Patch for OPENJPA-168

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@522581 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David J. Wisneski 2007-03-26 17:59:03 +00:00
parent 91cc432676
commit 442c1cee48
13 changed files with 218 additions and 52 deletions

View File

@ -43,6 +43,7 @@ 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;
@ -368,9 +369,12 @@ public class JDBCStoreManager
if (!select(sel, mapping, subs, sm, null, fetch, if (!select(sel, mapping, subs, sm, null, fetch,
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);
return sel.execute(this, fetch); //Set the expectedResultCount for the select as 1 as a single
//object is being loaded. force = true is an indicator that it is
//internally generated value
sel.setExpectedResultCount(1,true);
return sel.execute(this, fetch);
} }
/** /**
@ -385,7 +389,7 @@ public class JDBCStoreManager
JDBCFetchConfiguration.EAGER_JOIN); JDBCFetchConfiguration.EAGER_JOIN);
Union union = _sql.newUnion(mappings.length); Union union = _sql.newUnion(mappings.length);
union.setSingleResult(true); union.setExpectedResultCount(1,true);
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

@ -312,6 +312,7 @@ public class JDBCStoreQuery
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],
@ -322,6 +323,19 @@ public class JDBCStoreQuery
// 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
//expectedResultCount to 1.force = true indicates that this is
//internally generated value
if(this.ctx.isUnique())
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
(this.optimizeHint))!= 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

@ -238,7 +238,7 @@ class LRSProxyMap
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.setSingleResult(true); 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.setSingleResult(true); union.setExpectedResultCount(1,true);
if (fetch.getSubclassFetchMode(field.getTypeMapping()) if (fetch.getSubclassFetchMode(field.getTypeMapping())
!= JDBCFetchConfiguration.EAGER_JOIN) != JDBCFetchConfiguration.EAGER_JOIN)
union.abortUnion(); union.abortUnion();

View File

@ -20,6 +20,7 @@ import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Sequence;
/** /**
@ -27,7 +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 rowClause = "row";
public DB2Dictionary() { public DB2Dictionary() {
platform = "DB2"; platform = "DB2";
validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM " validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM "
@ -193,4 +197,65 @@ 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);
//return 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,
JDBCFetchConfiguration fetch) {
sel.addJoinClassConditions();
boolean update = forUpdate && sel.getFromSelect() == null;
SQLBuffer select = getSelects(sel, false, update);
SQLBuffer ordering = null;
if (!sel.isAggregate() || sel.getGrouping() != null)
ordering = sel.getOrdering();
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,13 +47,18 @@ public class LogicalUnion
private static final Localizer _loc = Localizer.forPackage private static final Localizer _loc = Localizer.forPackage
(LogicalUnion.class); (LogicalUnion.class);
//expected number of results for this select to be used in
// optimize for clause
protected int expectedResultCount = 0;
//indicate if this is internally generated result count
//or not
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;
protected final BitSet desc = new BitSet(); protected final BitSet desc = new BitSet();
private boolean _distinct = true; private boolean _distinct = true;
private boolean _single = false;
/** /**
* Constructor. * Constructor.
@ -102,15 +107,7 @@ public class LogicalUnion
public Select[] getSelects() { public Select[] getSelects() {
return sels; return sels;
} }
public boolean isSingleResult() {
return _single;
}
public void setSingleResult(boolean single) {
_single = single;
}
public boolean isUnion() { public boolean isUnion() {
return false; return false;
} }
@ -215,9 +212,12 @@ public class LogicalUnion
return res; return res;
} }
if (_single) { if (this.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]);
@ -305,7 +305,12 @@ 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;
// expected number of results for this select to be used in
// optimize for clause
protected int expectedResultCount = 0;
//force indicates it is internally generated result count or not
protected boolean force = false;
public UnionSelect(SelectImpl sel, int pos) { public UnionSelect(SelectImpl sel, int pos) {
this.sel = sel; this.sel = sel;
this.pos = pos; this.pos = pos;
@ -838,6 +843,18 @@ public class LogicalUnion
public String toString() { public String toString() {
return sel.toString(); return sel.toString();
} }
public int getExpectedResultCount() {
return expectedResultCount;
}
public void setExpectedResultCount(int expectedResultCount, boolean force) {
this.expectedResultCount = expectedResultCount;
this.force = force;
}
public boolean isExpRsltCntForced() {
return force;
}
} }
/** /**
@ -918,4 +935,16 @@ 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

@ -119,4 +119,23 @@ 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

@ -153,7 +153,14 @@ public class SelectImpl
// from select if this select selects from a tmp table created by another // from select if this select selects from a tmp table created by another
private SelectImpl _from = null; private SelectImpl _from = null;
private SelectImpl _outer = null; private SelectImpl _outer = null;
//expected number of results for this select to be used in
// optimize for clause
private int expectedResultCount = 0;
//true if the expectedResultCount is internally set false if
//it is set by the user
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.
*/ */
@ -304,6 +311,20 @@ 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)
@ -2799,6 +2820,19 @@ 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

@ -33,20 +33,6 @@ public interface Union
*/ */
public String getOrdering(); public String getOrdering();
/**
* Whether this union will return at most a single result. Setting this
* flag makes it more efficient to execute logical unions that are actually
* made up from multiple selects executed in batch.
*/
public boolean isSingleResult();
/**
* Whether this union will return at most a single result. Setting this
* flag makes it more efficient to execute logical unions that are actually
* made up from multiple selects executed in batch.
*/
public void setSingleResult(boolean single);
/** /**
* Whether this is a true UNION, rather than a logical combination of * Whether this is a true UNION, rather than a logical combination of
* independent selects. * independent selects.

View File

@ -33,6 +33,7 @@ public abstract class AbstractStoreQuery
implements StoreQuery { implements StoreQuery {
protected QueryContext ctx = null; protected QueryContext ctx = null;
public static final String optimizeHint ="openjpa.hint.OptimizeResultCount";
public QueryContext getContext() { public QueryContext getContext() {
return ctx; return ctx;

View File

@ -1289,7 +1289,11 @@ public class QueryImpl
// Collections.singletonList is JDK 1.3, so... // Collections.singletonList is JDK 1.3, so...
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,6 +259,9 @@ 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 \
configured to have a unique result, but no instance matched \
the query.
serialized: Queries that have been serialized do not support this operation. serialized: Queries that have been serialized do not support this operation.
read-only: Attempt to modify a read-only query object. read-only: Attempt to modify a read-only query object.
no-class: A candidate Class must be specified before executing a query. no-class: A candidate Class must be specified before executing a query.

View File

@ -279,24 +279,15 @@ public class QueryImpl
*/ */
public Object getSingleResult() { public Object getSingleResult() {
_em.assertNotCloseInvoked(); _em.assertNotCloseInvoked();
//Indicate that this query returns single result.Later copied into
//select.expectedResultCount
_query.setUnique(true);
try{
Object ob = execute(); Object ob = execute();
if (!(ob instanceof List)) return ob;
return ob;
List res = (List) ob;
try {
// don't use size() b/c can be inefficient under some LRS settings
Iterator itr = res.iterator();
if (!itr.hasNext())
throw new NoResultException(_loc.get("no-results",
_query.getQueryString()).getMessage(), null, null, false);
Object ret = itr.next();
if (itr.hasNext())
throw new NonUniqueResultException(_loc.get("mult-results",
_query.getQueryString()).getMessage(), null, null, false);
return ret;
} finally { } finally {
OpenJPAPersistence.close(res); _query.setUnique(false);
} }
} }
@ -375,13 +366,29 @@ public class QueryImpl
} else if (k.startsWith("FetchPlan.")) { } else if (k.startsWith("FetchPlan.")) {
k = k.substring("FetchPlan.".length()); k = k.substring("FetchPlan.".length());
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((!(value instanceof String)&&!(value instanceof Integer))
|| (value instanceof String &&(Integer.parseInt
((String)value)< 0))||((value instanceof Integer)
&& (((Integer)value).intValue()<0)) )
throw new ArgumentException(_loc.get
("bad-hint-value", key),
null, null, false);
if(value instanceof String)
value = new Integer((String)value);
}
_query.getFetchConfiguration().setHint(key, value); _query.getFetchConfiguration().setHint(key, value);
}
else else
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);
} }
} }