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; @@ -368,9 +369,12 @@ if (!select(sel, mapping, subs, sm, null, fetch, JDBCFetchConfiguration.EAGER_JOIN, true, false)) 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. force = true is an indicator that it is + //internally generated value + sel.setExpectedResultCount(1,true); + return sel.execute(this, fetch); } /** @@ -385,7 +389,7 @@ JDBCFetchConfiguration.EAGER_JOIN); Union union = _sql.newUnion(mappings.length); - union.setSingleResult(true); + union.setExpectedResultCount(1,true); if (fetch.getSubclassFetchMode(mapping) != fetch.EAGER_JOIN) union.abortUnion(); union.select(new Union.Selector() { 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 optHint = 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,19 @@ // 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.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++) { selMappings.add(verts[j]); if (j == verts.length - 1) { Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LRSProxyMap.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LRSProxyMap.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LRSProxyMap.java (working copy) @@ -238,7 +238,7 @@ final Joins[] resJoins = new Joins[Math.max(1, clss.length)]; Union union = store.getSQLFactory().newUnion (Math.max(1, clss.length)); - union.setSingleResult(true); + union.setExpectedResultCount(1,true); if (fetch.getSubclassFetchMode(_strat.getFieldMapping(). getElementMapping().getTypeMapping()) != JDBCFetchConfiguration.EAGER_JOIN) Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (working copy) @@ -578,7 +578,7 @@ // back to our fk table if not an inverse mapping (in which case we // can just make sure the inverse cols == our pk values) Union union = store.getSQLFactory().newUnion(rels.length); - union.setSingleResult(true); + union.setExpectedResultCount(1,true); if (fetch.getSubclassFetchMode(field.getTypeMapping()) != JDBCFetchConfiguration.EAGER_JOIN) union.abortUnion(); 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,7 +28,10 @@ */ public class DB2Dictionary extends AbstractDB2Dictionary { - + + //variables to support optimize clause + public String optimizeClause = "optimize for"; + public String rowClause = "row"; public DB2Dictionary() { platform = "DB2"; validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM " @@ -193,4 +197,65 @@ } } } + //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()); + } + } 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) @@ -47,7 +47,12 @@ private static final Localizer _loc = Localizer.forPackage (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 DBDictionary dict; protected final ClassMapping[] mappings; @@ -53,7 +58,7 @@ protected final ClassMapping[] mappings; protected final BitSet desc = new BitSet(); private boolean _distinct = true; - private boolean _single = false; + /** * Constructor. @@ -102,15 +107,7 @@ public Select[] getSelects() { return sels; } - - public boolean isSingleResult() { - return _single; - } - - public void setSingleResult(boolean single) { - _single = single; - } - + public boolean isUnion() { return false; } @@ -215,9 +212,12 @@ return res; } - if (_single) { + if (this.getExpectedResultCount()== 1) { AbstractResult res; 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, lockLevel); res.setBaseMapping(mappings[i]); @@ -305,7 +305,12 @@ 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; + //force indicates it is internally generated result count or not + protected boolean force = false; + public UnionSelect(SelectImpl sel, int pos) { this.sel = sel; this.pos = pos; @@ -838,6 +843,18 @@ public String 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 @@ 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; + } } Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java (working copy) @@ -119,4 +119,23 @@ public Result execute(JDBCStore store, JDBCFetchConfiguration fetch, int lockLevel) 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(); } 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,14 @@ // 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; + //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. */ @@ -304,6 +311,20 @@ JDBCFetchConfiguration fetch, int lockLevel) throws SQLException { 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) { JDBCLockManager lm = store.getLockManager(); if (lm != null) @@ -2799,6 +2820,19 @@ _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; + } } /** Index: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Union.java =================================================================== --- openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Union.java (revision 518262) +++ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Union.java (working copy) @@ -34,20 +34,6 @@ 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 * independent selects. */ Index: openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java =================================================================== --- openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java (revision 518262) +++ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java (working copy) @@ -33,6 +33,7 @@ implements StoreQuery { protected QueryContext ctx = null; + public static final String optimizeHint ="openjpa.hint.OptimizeResultCount"; public QueryContext getContext() { return ctx; Index: openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java =================================================================== --- openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (revision 518262) +++ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (working copy) @@ -1289,7 +1289,11 @@ // Collections.singletonList is JDK 1.3, so... return Arrays.asList(new Object[]{ single }); } - + + if(single == null) + throw new InvalidStateException(_loc.get("not-unique", + _class, _query)); + // return single result return single; } finally { 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,24 +279,15 @@ */ public Object getSingleResult() { _em.assertNotCloseInvoked(); + //Indicate that this query returns single result.Later copied into + //select.expectedResultCount + _query.setUnique(true); + try{ 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 { - OpenJPAPersistence.close(res); + _query.setUnique(false); } } @@ -375,8 +366,20 @@ } 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); + if(value instanceof String) + value = new Integer((String)value); + } _query.getFetchConfiguration().setHint(key, value); + } else throw new ArgumentException(_loc.get("bad-query-hint", key), null, null, false); @@ -381,7 +384,11 @@ throw new ArgumentException(_loc.get("bad-query-hint", key), null, null, false); 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); } }