Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (working copy) @@ -43,6 +43,7 @@ import org.apache.openjpa.jdbc.sql.SQLFactory; import org.apache.openjpa.jdbc.sql.Select; 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.kernel.FetchConfiguration; import org.apache.openjpa.kernel.LockManager; @@ -370,7 +371,10 @@ return null; 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.-1 is an indicator that it is internally generated value + sel.setExpectedResultCount(-1); + return sel.execute(this, fetch); } /** Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (working copy) @@ -312,6 +312,7 @@ ClassMapping[] verts; boolean unionable = true; Select sel; + Object optimizeHint = null; for (int i = 0; i < mappings.length; i++) { // determine vertical mappings to select separately verts = getVerticalMappings(mappings[i], subclasses, exps[i], @@ -322,6 +323,23 @@ // create criteria select and clone for each vert mapping sel = ((JDBCExpressionFactory) facts[i]).getSelectConstructor(). evaluate(ctx, null, null, exps[i], states[i]); + //it means it is coming from getSingleResult so set the expectedResultCount to -1 + //-1 indicates that this is internally generated value + if(this.ctx.isUnique()) + sel.setExpectedResultCount(-1); + //it means this is coming from getResultList so set the expectedResultCount + //based on any optimize hint if provided + else{ + + if((optimizeHint = ctx.fetch.getHint("openjpa.hint.OptimizeResultCount"))!= null){ + if(optimizeHint instanceof Integer) + sel.setExpectedResultCount(((Integer)optimizeHint).intValue()); + //If the hint is set through the NamedQuery it could have a String value + else + sel.setExpectedResultCount((Integer.parseInt((String)optimizeHint))); + + } + } for (int j = 0; j < verts.length; j++) { selMappings.add(verts[j]); if (j == verts.length - 1) { Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java (working copy) @@ -20,6 +20,7 @@ import java.sql.SQLException; import java.util.Arrays; +import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration; import org.apache.openjpa.jdbc.schema.Sequence; /** @@ -27,6 +28,12 @@ */ public class DB2Dictionary extends AbstractDB2Dictionary { + + //variables to support optimize clause + public String optimizeClause = "optimize for"; + public String rowClause = "row"; + public String rowsClause = "rows"; + public DB2Dictionary() { platform = "DB2"; @@ -193,4 +200,62 @@ } } } + //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(" "); + if(expectedResultCount > 1) + optimizeString.append(rowsClause).append(" "); + else + optimizeString.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()); + } + } Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java (working copy) @@ -218,6 +218,9 @@ if (_single) { AbstractResult res; for (int i = 0; i < sels.length; i++) { + //for each select set the expected result count to -1 + //-1 indicates that this internally generated value + sels[i].sel.setExpectedResultCount(-1); res = (AbstractResult) sels[i].execute(store, fetch, lockLevel); res.setBaseMapping(mappings[i]); @@ -305,7 +308,11 @@ protected final int pos; protected int orders = 0; protected List orderIdxs = null; - + // expected number of results for this select to be used in + // optimize for clause + protected int expectedResultCount = 0; + + public UnionSelect(SelectImpl sel, int pos) { this.sel = sel; this.pos = pos; @@ -838,6 +845,16 @@ public String toString() { return sel.toString(); } + + public int getExpectedResultCount() { + return expectedResultCount; + } + + public void setExpectedResultCount(int expectedResultCount) { + this.expectedResultCount = expectedResultCount; + } + + } /** Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java (working copy) @@ -683,4 +683,16 @@ * Implement toString to generate SQL string for profiling/debuggging. */ public String toString(); + + /** + * Return the expected result count for the query + */ + public int getExpectedResultCount() ; + + /** + * Set the expected result count for the query + */ + public void setExpectedResultCount(int expectedResultCount) ; + + } Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (working copy) @@ -153,7 +153,12 @@ // from select if this select selects from a tmp table created by another private SelectImpl _from = null; private SelectImpl _outer = null; - + + //expected number of results for this select to be used in + // optimize for clause + private int expectedResultCount = 0; + + /** * Helper method to return the proper table alias for the given alias index. */ @@ -304,6 +309,22 @@ JDBCFetchConfiguration fetch, int lockLevel) throws SQLException { boolean forUpdate = false; + + //expectedResultCount = -1 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 expectedCount != -1 then it is set by the user through hint and we do not check the eager joins + if(this.expectedResultCount == -1 ){ + + if(this.hasEagerJoin(true)) + this.setExpectedResultCount(0); + else + this.setExpectedResultCount(1); + + } + + + if (!isAggregate() && _grouping == null) { JDBCLockManager lm = store.getLockManager(); if (lm != null) @@ -2799,6 +2820,16 @@ _idents = null; } } + + public int getExpectedResultCount() { + return expectedResultCount; + } + + public void setExpectedResultCount(int expectedResultCount) { + this.expectedResultCount = expectedResultCount; + } + + } /** Index: openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java =================================================================== --- openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java (revision 518262) +++ openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java (working copy) @@ -279,6 +279,8 @@ */ public Object getSingleResult() { _em.assertNotCloseInvoked(); + //Indicate that this query returns single result.Later copied into select.expectedResultCount + _query.setUnique(true); Object ob = execute(); if (!(ob instanceof List)) return ob; @@ -375,8 +377,14 @@ } else if (k.startsWith("FetchPlan.")) { k = k.substring("FetchPlan.".length()); 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); + } _query.getFetchConfiguration().setHint(key, value); + } else throw new ArgumentException(_loc.get("bad-query-hint", key), null, null, false);