You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by fa...@apache.org on 2008/04/09 20:09:49 UTC

svn commit: r646455 - in /openjpa: branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/k...

Author: fancy
Date: Wed Apr  9 11:09:42 2008
New Revision: 646455

URL: http://svn.apache.org/viewvc?rev=646455&view=rev
Log:
OPENJPA-547 INNER JOIN FETCH query incorrectly generates LEFT join SQL

Modified:
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
    openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
    openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
    openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
    openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
    openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
    openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -260,4 +260,38 @@
             throw translate(re);
         }
     }
+
+    public Set getFetchInnerJoins() {
+        try {
+            return getJDBCDelegate().getFetchInnerJoins();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        try {
+            return getJDBCDelegate().hasFetchInnerJoin(field);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String field) {
+        try {
+            getJDBCDelegate().addFetchInnerJoin(field);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields) {
+        try {
+            getJDBCDelegate().addFetchInnerJoins(fields);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
 }

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -207,4 +207,38 @@
      * Convenience method to cast traversal to store-specific type.
      */
     public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
+
+    /**
+     * Returns the names of the inner fetch joins that this component will use
+     * when loading objects. Defaults to the empty set.  This set is not
+     * thread safe.
+     *
+     * @since 1.1
+     */
+    public Set getFetchInnerJoins();
+
+    /**
+     * Return true if the given fully-qualified inner fetch join has been added.
+     *
+     * @since 1.1
+     */
+    public boolean hasFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>field</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.1
+     */
+    public JDBCFetchConfiguration addFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>fields</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.1
+     */
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields);
 }

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java Wed Apr  9 11:09:42 2008
@@ -69,6 +69,7 @@
         public int size = 0;
         public int syntax = 0;
         public Set joins = null;
+        public Set fetchInnerJoins = null;
         public int isolationLevel = -1;
     }
 
@@ -344,5 +345,38 @@
         if (!(conf instanceof JDBCConfiguration))
             return null;
         return (JDBCConfiguration) conf;
+    }
+
+    public Set getFetchInnerJoins() {
+        return (_state.fetchInnerJoins == null) ? Collections.EMPTY_SET
+            : _state.fetchInnerJoins;
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        return _state.fetchInnerJoins != null &&
+            _state.fetchInnerJoins.contains(field);
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String join) {
+        if (StringUtils.isEmpty(join))
+            throw new UserException(_loc.get("null-join"));
+        
+        lock();
+        try {
+            if (_state.fetchInnerJoins == null)
+                _state.fetchInnerJoins = new HashSet();
+            _state.fetchInnerJoins.add(join);
+        } finally {
+            unlock();
+        }
+        return this;
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection joins) {
+        if (joins == null || joins.isEmpty())
+            return this;
+        for (Iterator itr = joins.iterator(); itr.hasNext();)
+            addFetchInnerJoin((String) itr.next());
+        return this;
     }
 }

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Wed Apr  9 11:09:42 2008
@@ -150,6 +150,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
@@ -600,6 +602,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         eager = Math.min(eager, JDBCFetchConfiguration.EAGER_JOIN);

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -409,9 +409,11 @@
         // clone it for a to-many eager select can result in a clone that
         // produces invalid SQL
         ClassMapping cls = field.getIndependentTypeMappings()[0];
+        boolean forceInner = fetch.hasFetchInnerJoin(field.getFullName(false)) ?
+                true : false;
         sel.select(cls, field.getSelectSubclasses(), store, fetch,
             JDBCFetchConfiguration.EAGER_JOIN,
-            eagerJoin(sel.newJoins(), cls, false));
+            eagerJoin(sel.newJoins(), cls, forceInner));
     }
 
     /**

Modified: openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java (original)
+++ openjpa/branches/1.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -24,7 +24,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -168,10 +167,13 @@
         // we limit further eager fetches to joins, because after this point
         // the select has been modified such that parallel clones may produce
         // invalid sql
+        boolean outer = field.getNullValue() != FieldMapping.NULL_EXCEPTION;
+        // force inner join for inner join fetch 
+        if (fetch.hasFetchInnerJoin(field.getFullName(false)))
+            outer = false;
         selectEager(sel, getDefaultElementMapping(true), sm, store, fetch, 
             JDBCFetchConfiguration.EAGER_JOIN, false,
-            field.getNullValue()
-                != FieldMapping.NULL_EXCEPTION);
+            outer);
     }
 
     public boolean isEagerSelectToMany() {

Modified: openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java (original)
+++ openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java Wed Apr  9 11:09:42 2008
@@ -63,6 +63,7 @@
     public int operation = QueryOperations.OP_SELECT;
     public ClassMetaData[] accessPath = StoreQuery.EMPTY_METAS;
     public String[] fetchPaths = StoreQuery.EMPTY_STRINGS;
+    public String[] fetchInnerPaths = StoreQuery.EMPTY_STRINGS;
     public Value[] range = EMPTY_VALUES;
     private Boolean _aggregate = null;
 

Modified: openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java (original)
+++ openjpa/branches/1.0.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java Wed Apr  9 11:09:42 2008
@@ -440,6 +440,7 @@
 
         // handle JOIN FETCH
         Set joins = null;
+        Set innerJoins = null;
 
         JPQLNode[] outers = root().findChildrenByID(JJTOUTERFETCHJOIN);
         for (int i = 0; outers != null && i < outers.length; i++)
@@ -447,13 +448,20 @@
                 add(getPath(onlyChild(outers[i])).last().getFullName(false));
 
         JPQLNode[] inners = root().findChildrenByID(JJTINNERFETCHJOIN);
-        for (int i = 0; inners != null && i < inners.length; i++)
-            (joins == null ? joins = new TreeSet() : joins).
-                add(getPath(onlyChild(inners[i])).last().getFullName(false));
+        for (int i = 0; inners != null && i < inners.length; i++) {
+            String path = getPath(onlyChild(inners[i])).last()
+                .getFullName(false);
+            (joins == null ? joins = new TreeSet() : joins).add(path);
+            (innerJoins == null ? innerJoins = new TreeSet() : innerJoins).
+                add(path);
+        }
 
         if (joins != null)
             exps.fetchPaths = (String[]) joins.
                 toArray(new String[joins.size()]);
+        if (innerJoins != null)
+            exps.fetchInnerPaths = (String[]) innerJoins.
+                toArray(new String[innerJoins.size()]);
 
         return filter;
     }
@@ -494,7 +502,7 @@
             else if (node.id == JJTINNERJOIN)
                 exp = addJoin(node, true, exp);
             else if (node.id == JJTINNERFETCHJOIN)
-                exp = addJoin(node, true, exp);
+                ; // we handle inner fetch joins in the evalFetchJoins() method
             else if (node.id == JJTOUTERFETCHJOIN)
                 ; // we handle outer fetch joins in the evalFetchJoins() method
             else

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -260,4 +260,38 @@
             throw translate(re);
         }
     }
+
+    public Set getFetchInnerJoins() {
+        try {
+            return getJDBCDelegate().getFetchInnerJoins();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        try {
+            return getJDBCDelegate().hasFetchInnerJoin(field);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String field) {
+        try {
+            getJDBCDelegate().addFetchInnerJoin(field);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields) {
+        try {
+            getJDBCDelegate().addFetchInnerJoins(fields);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
 }

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -207,4 +207,38 @@
      * Convenience method to cast traversal to store-specific type.
      */
     public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
+
+    /**
+     * Returns the names of the inner fetch joins that this component will use
+     * when loading objects. Defaults to the empty set.  This set is not
+     * thread safe.
+     *
+     * @since 1.0.3
+     */
+    public Set getFetchInnerJoins();
+
+    /**
+     * Return true if the given fully-qualified inner fetch join has been added.
+     *
+     * @since 1.0.3
+     */
+    public boolean hasFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>field</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.0.3
+     */
+    public JDBCFetchConfiguration addFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>fields</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.0.3
+     */
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields);
 }

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java Wed Apr  9 11:09:42 2008
@@ -69,6 +69,7 @@
         public int size = 0;
         public int syntax = 0;
         public Set joins = null;
+        public Set fetchInnerJoins = null;
         public int isolationLevel = -1;
     }
 
@@ -344,5 +345,38 @@
         if (!(conf instanceof JDBCConfiguration))
             return null;
         return (JDBCConfiguration) conf;
+    }
+
+    public Set getFetchInnerJoins() {
+        return (_state.fetchInnerJoins == null) ? Collections.EMPTY_SET
+            : _state.fetchInnerJoins;
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        return _state.fetchInnerJoins != null &&
+            _state.fetchInnerJoins.contains(field);
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String join) {
+        if (StringUtils.isEmpty(join))
+            throw new UserException(_loc.get("null-join"));
+        
+        lock();
+        try {
+            if (_state.fetchInnerJoins == null)
+                _state.fetchInnerJoins = new HashSet();
+            _state.fetchInnerJoins.add(join);
+        } finally {
+            unlock();
+        }
+        return this;
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection joins) {
+        if (joins == null || joins.isEmpty())
+            return this;
+        for (Iterator itr = joins.iterator(); itr.hasNext();)
+            addFetchInnerJoin((String) itr.next());
+        return this;
     }
 }

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Wed Apr  9 11:09:42 2008
@@ -148,6 +148,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
@@ -599,6 +601,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         eager = Math.min(eager, JDBCFetchConfiguration.EAGER_JOIN);

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -409,9 +409,11 @@
         // clone it for a to-many eager select can result in a clone that
         // produces invalid SQL
         ClassMapping cls = field.getIndependentTypeMappings()[0];
+        boolean forceInner = fetch.hasFetchInnerJoin(field.getFullName(false)) ?
+                true : false;
         sel.select(cls, field.getSelectSubclasses(), store, fetch,
             JDBCFetchConfiguration.EAGER_JOIN,
-            eagerJoin(sel.newJoins(), cls, false));
+            eagerJoin(sel.newJoins(), cls, forceInner));
     }
 
     /**

Modified: openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java (original)
+++ openjpa/branches/1.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -24,7 +24,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -168,10 +167,13 @@
         // we limit further eager fetches to joins, because after this point
         // the select has been modified such that parallel clones may produce
         // invalid sql
+        boolean outer = field.getNullValue() != FieldMapping.NULL_EXCEPTION;
+        // force inner join for inner join fetch 
+        if (fetch.hasFetchInnerJoin(field.getFullName(false)))
+            outer = false;
         selectEager(sel, getDefaultElementMapping(true), sm, store, fetch, 
             JDBCFetchConfiguration.EAGER_JOIN, false,
-            field.getNullValue()
-                != FieldMapping.NULL_EXCEPTION);
+            outer);
     }
 
     public boolean isEagerSelectToMany() {

Modified: openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java (original)
+++ openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java Wed Apr  9 11:09:42 2008
@@ -65,6 +65,7 @@
     public int operation = QueryOperations.OP_SELECT;
     public ClassMetaData[] accessPath = StoreQuery.EMPTY_METAS;
     public String[] fetchPaths = StoreQuery.EMPTY_STRINGS;
+    public String[] fetchInnerPaths = StoreQuery.EMPTY_STRINGS;
     public Value[] range = EMPTY_VALUES;
     private Boolean _aggregate = null;
 

Modified: openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java (original)
+++ openjpa/branches/1.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java Wed Apr  9 11:09:42 2008
@@ -452,6 +452,7 @@
 
         // handle JOIN FETCH
         Set joins = null;
+        Set innerJoins = null;
 
         JPQLNode[] outers = root().findChildrenByID(JJTOUTERFETCHJOIN);
         for (int i = 0; outers != null && i < outers.length; i++)
@@ -459,13 +460,20 @@
                 add(getPath(onlyChild(outers[i])).last().getFullName(false));
 
         JPQLNode[] inners = root().findChildrenByID(JJTINNERFETCHJOIN);
-        for (int i = 0; inners != null && i < inners.length; i++)
-            (joins == null ? joins = new TreeSet() : joins).
-                add(getPath(onlyChild(inners[i])).last().getFullName(false));
+        for (int i = 0; inners != null && i < inners.length; i++) {
+            String path = getPath(onlyChild(inners[i])).last()
+                .getFullName(false);
+            (joins == null ? joins = new TreeSet() : joins).add(path);
+            (innerJoins == null ? innerJoins = new TreeSet() : innerJoins).
+                add(path);
+        }
 
         if (joins != null)
             exps.fetchPaths = (String[]) joins.
                 toArray(new String[joins.size()]);
+        if (innerJoins != null)
+            exps.fetchInnerPaths = (String[]) innerJoins.
+                toArray(new String[innerJoins.size()]);
 
         return filter;
     }
@@ -506,7 +514,7 @@
             else if (node.id == JJTINNERJOIN)
                 exp = addJoin(node, true, exp);
             else if (node.id == JJTINNERFETCHJOIN)
-                exp = addJoin(node, true, exp);
+                ; // we handle inner fetch joins in the evalFetchJoins() method
             else if (node.id == JJTOUTERFETCHJOIN)
                 ; // we handle outer fetch joins in the evalFetchJoins() method
             else

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -260,4 +260,38 @@
             throw translate(re);
         }
     }
+
+    public Set getFetchInnerJoins() {
+        try {
+            return getJDBCDelegate().getFetchInnerJoins();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        try {
+            return getJDBCDelegate().hasFetchInnerJoin(field);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String field) {
+        try {
+            getJDBCDelegate().addFetchInnerJoin(field);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields) {
+        try {
+            getJDBCDelegate().addFetchInnerJoins(fields);
+            return this;
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java Wed Apr  9 11:09:42 2008
@@ -207,4 +207,38 @@
      * Convenience method to cast traversal to store-specific type.
      */
     public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
+
+    /**
+     * Returns the names of the inner fetch joins that this component will use
+     * when loading objects. Defaults to the empty set.  This set is not
+     * thread safe.
+     *
+     * @since 1.0.3
+     */
+    public Set getFetchInnerJoins();
+
+    /**
+     * Return true if the given fully-qualified inner fetch join has been added.
+     *
+     * @since 1.0.3
+     */
+    public boolean hasFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>field</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.0.3
+     */
+    public JDBCFetchConfiguration addFetchInnerJoin(String field);
+
+    /**
+     * Adds <code>fields</code> to the set of fully-qualified field names to
+     * eagerly join when loading objects. Each class can have at most
+     * one to-many eagerly joined fields.
+     *
+     * @since 1.0.3
+     */
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection fields);
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java Wed Apr  9 11:09:42 2008
@@ -69,6 +69,7 @@
         public int size = 0;
         public int syntax = 0;
         public Set joins = null;
+        public Set fetchInnerJoins = null;
         public int isolationLevel = -1;
     }
 
@@ -344,5 +345,38 @@
         if (!(conf instanceof JDBCConfiguration))
             return null;
         return (JDBCConfiguration) conf;
+    }
+
+    public Set getFetchInnerJoins() {
+        return (_state.fetchInnerJoins == null) ? Collections.EMPTY_SET
+            : _state.fetchInnerJoins;
+    }
+
+    public boolean hasFetchInnerJoin(String field) {
+        return _state.fetchInnerJoins != null &&
+            _state.fetchInnerJoins.contains(field);
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoin(String join) {
+        if (StringUtils.isEmpty(join))
+            throw new UserException(_loc.get("null-join"));
+        
+        lock();
+        try {
+            if (_state.fetchInnerJoins == null)
+                _state.fetchInnerJoins = new HashSet();
+            _state.fetchInnerJoins.add(join);
+        } finally {
+            unlock();
+        }
+        return this;
+    }
+
+    public JDBCFetchConfiguration addFetchInnerJoins(Collection joins) {
+        if (joins == null || joins.isEmpty())
+            return this;
+        for (Iterator itr = joins.iterator(); itr.hasNext();)
+            addFetchInnerJoin((String) itr.next());
+        return this;
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Wed Apr  9 11:09:42 2008
@@ -148,6 +148,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
@@ -599,6 +601,8 @@
             fetch.addFields(Arrays.asList(exps[0].fetchPaths));
             fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
         }
+        if (exps[0].fetchInnerPaths != null)
+            fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
 
         int eager = calculateEagerMode(exps[0], range.start, range.end);
         eager = Math.min(eager, JDBCFetchConfiguration.EAGER_JOIN);

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -409,9 +409,11 @@
         // clone it for a to-many eager select can result in a clone that
         // produces invalid SQL
         ClassMapping cls = field.getIndependentTypeMappings()[0];
+        boolean forceInner = fetch.hasFetchInnerJoin(field.getFullName(false)) ?
+                true : false;
         sel.select(cls, field.getSelectSubclasses(), store, fetch,
             JDBCFetchConfiguration.EAGER_JOIN,
-            eagerJoin(sel.newJoins(), cls, false));
+            eagerJoin(sel.newJoins(), cls, forceInner));
     }
 
     /**

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java Wed Apr  9 11:09:42 2008
@@ -24,7 +24,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -168,10 +167,13 @@
         // we limit further eager fetches to joins, because after this point
         // the select has been modified such that parallel clones may produce
         // invalid sql
+        boolean outer = field.getNullValue() != FieldMapping.NULL_EXCEPTION;
+        // force inner join for inner join fetch 
+        if (fetch.hasFetchInnerJoin(field.getFullName(false)))
+            outer = false;
         selectEager(sel, getDefaultElementMapping(true), sm, store, fetch, 
             JDBCFetchConfiguration.EAGER_JOIN, false,
-            field.getNullValue()
-                != FieldMapping.NULL_EXCEPTION);
+            outer);
     }
 
     public boolean isEagerSelectToMany() {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/QueryExpressions.java Wed Apr  9 11:09:42 2008
@@ -65,6 +65,7 @@
     public int operation = QueryOperations.OP_SELECT;
     public ClassMetaData[] accessPath = StoreQuery.EMPTY_METAS;
     public String[] fetchPaths = StoreQuery.EMPTY_STRINGS;
+    public String[] fetchInnerPaths = StoreQuery.EMPTY_STRINGS;
     public Value[] range = EMPTY_VALUES;
     private Boolean _aggregate = null;
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=646455&r1=646454&r2=646455&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java Wed Apr  9 11:09:42 2008
@@ -452,6 +452,7 @@
 
         // handle JOIN FETCH
         Set joins = null;
+        Set innerJoins = null;
 
         JPQLNode[] outers = root().findChildrenByID(JJTOUTERFETCHJOIN);
         for (int i = 0; outers != null && i < outers.length; i++)
@@ -459,13 +460,20 @@
                 add(getPath(onlyChild(outers[i])).last().getFullName(false));
 
         JPQLNode[] inners = root().findChildrenByID(JJTINNERFETCHJOIN);
-        for (int i = 0; inners != null && i < inners.length; i++)
-            (joins == null ? joins = new TreeSet() : joins).
-                add(getPath(onlyChild(inners[i])).last().getFullName(false));
+        for (int i = 0; inners != null && i < inners.length; i++) {
+            String path = getPath(onlyChild(inners[i])).last()
+                .getFullName(false);
+            (joins == null ? joins = new TreeSet() : joins).add(path);
+            (innerJoins == null ? innerJoins = new TreeSet() : innerJoins).
+                add(path);
+        }
 
         if (joins != null)
             exps.fetchPaths = (String[]) joins.
                 toArray(new String[joins.size()]);
+        if (innerJoins != null)
+            exps.fetchInnerPaths = (String[]) innerJoins.
+                toArray(new String[innerJoins.size()]);
 
         return filter;
     }
@@ -506,7 +514,7 @@
             else if (node.id == JJTINNERJOIN)
                 exp = addJoin(node, true, exp);
             else if (node.id == JJTINNERFETCHJOIN)
-                exp = addJoin(node, true, exp);
+                ; // we handle inner fetch joins in the evalFetchJoins() method
             else if (node.id == JJTOUTERFETCHJOIN)
                 ; // we handle outer fetch joins in the evalFetchJoins() method
             else