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 2009/07/30 01:53:46 UTC

svn commit: r799134 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-kernel/src/main/java/org...

Author: fancy
Date: Wed Jul 29 23:53:46 2009
New Revision: 799134

URL: http://svn.apache.org/viewvc?rev=799134&view=rev
Log:
OPENJPA-1185 JPA2 fix thread-safe problems of subquery restructured code.
Committing patch provided by Fay Wang.

Modified:
    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/kernel/exps/PCPath.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Context.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java

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=799134&r1=799133&r2=799134&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 Jul 29 23:53:46 2009
@@ -102,6 +102,7 @@
     }
 
     private final transient JDBCStore _store;
+    private static ThreadLocalContext localContext = new ThreadLocalContext();
 
     /**
      * Constructor. Supply store manager.
@@ -143,27 +144,14 @@
         return new JDBCExpressionFactory((ClassMapping) meta);
     }
     
-    private void resetSelect(Context ctx) {
-        List<Context> subselCtxs = ctx.getSubselContexts();
-        if (subselCtxs != null) {
-            for (Context subselCtx : subselCtxs) {
-                SelectImpl sel = (SelectImpl)subselCtx.getSelect();
-                sel.reset();
-                resetSelect(subselCtx);
-            }
-        }
-    }
-
     protected ResultObjectProvider executeQuery(Executor ex,
         ClassMetaData base, ClassMetaData[] metas, boolean subclasses,
         ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params,
         Range range) {
-        Context expCtx = exps[0].ctx(); 
-        if (expCtx != null) {
-            expCtx.resetAliasCount();
-            expCtx.setSelect(null);
-            resetSelect(expCtx);
-        }
+        Context[] ctxs = new Context[exps.length];
+        for (int i = 0; i < exps.length; i++)
+            ctxs[i] = exps[i].ctx();
+        localContext.set(clone(ctxs, null));
         if (metas.length > 1 && exps[0].isAggregate())
             throw new UserException(Localizer.forPackage(JDBCStoreQuery.class).
                 get("mult-mapping-aggregate", Arrays.asList(metas)));
@@ -254,6 +242,8 @@
         if ((rops != null && range.end != Long.MAX_VALUE) 
             || start != range.start || end != range.end)
             rop = new RangeResultObjectProvider(rop, range.start, range.end);
+
+        localContext.remove();
         return rop;
     }
 
@@ -465,12 +455,10 @@
     private Number executeBulkOperation(ClassMetaData[] metas,
         boolean subclasses, ExpressionFactory[] facts, QueryExpressions[] exps,
         Object[] params, Map updates) {
-        Context expCtx = exps[0].ctx(); 
-        if (ctx != null) {
-            expCtx.resetAliasCount();
-            expCtx.setSelect(null);
-            resetSelect(expCtx);
-        }
+        Context[] ctxs = new Context[exps.length];
+        for (int i = 0; i < exps.length; i++)
+            ctxs[i] = exps[i].ctx();
+        localContext.set(clone(ctxs, null));
         
         // we cannot execute a bulk delete statement when have mappings in
         // multiple tables, so indicate we want to use in-memory with null
@@ -551,6 +539,8 @@
         } finally {
             try { conn.close(); } catch (SQLException se) {}
         }
+
+        localContext.remove();
         return Numbers.valueOf(count);
     }
 
@@ -634,6 +624,10 @@
     protected String[] getDataStoreActions(ClassMetaData base,
         ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
         QueryExpressions[] exps, Object[] params, Range range) {
+        Context[] ctxs = new Context[exps.length];
+        for (int i = 0; i < exps.length; i++)
+            ctxs[i] = exps[i].ctx();
+        localContext.set(clone(ctxs, null));
         ClassMapping[] mappings = (ClassMapping[]) metas;
         JDBCFetchConfiguration fetch = (JDBCFetchConfiguration) ctx.
             getFetchConfiguration();
@@ -691,6 +685,8 @@
         String[] sql = new String[sels.size()];
         for (int i = 0; i < sels.size(); i++)
             sql[i] = ((Select) sels.get(i)).toSelect(false, fetch).getSQL(true);
+
+        localContext.remove();
         return sql;
     }
     
@@ -986,4 +982,88 @@
             throw new UnsupportedException();
         }
     }
+
+    private static class ThreadLocalContext extends ThreadLocal<Context[]> {
+        public Context[] initialValue() {
+          return null;
+        }
+    }
+
+    public static Context[] getThreadLocalContext() {
+        return localContext.get();
+    }
+
+    public static Context getThreadLocalContext(Context orig) {
+        Context[] root = localContext.get();
+        for (int i = 0; i < root.length; i++) {
+            Context lctx = getThreadLocalContext(root[i], orig);
+            if (lctx != null)
+                return lctx;
+        }
+        return null;
+    }
+
+    public static Select getThreadLocalSelect(Select select) {
+        if (select == null)
+            return null;
+        Context[] lctx = JDBCStoreQuery.getThreadLocalContext();
+        Context cloneFrom = select.ctx();
+        for (int i = 0; i < lctx.length; i++) {
+            Context cloneTo = getThreadLocalContext(lctx[i], cloneFrom);
+            if (cloneTo != null)
+                return (Select)cloneTo.getSelect();
+        }
+        return select;
+    }
+
+    public static Context getThreadLocalContext(Context lctx, Context cloneFrom) {
+        if (lctx.cloneFrom == cloneFrom)
+            return lctx;
+        java.util.List<Context> subselCtxs = lctx.getSubselContexts();
+        if (subselCtxs != null) {
+            for (Context subselCtx : subselCtxs) {
+                Context ctx = getThreadLocalContext(subselCtx, cloneFrom);
+                if (ctx != null)
+                    return ctx;
+            }
+        }
+        return null;
+    }
+
+    private static Context[] clone(Context[] orig, Context parent) {
+        Context[] newCtx = new Context[orig.length];
+        for (int i = 0; i < orig.length; i++) {
+            newCtx[i] = clone(orig[i], parent);
+        }
+        return newCtx;
+    }
+
+    private static Context clone(Context orig, Context parent) {
+        Context myParent = null;
+        if (parent == null) {
+            Context origParent = orig.getParent();
+            if (origParent != null)
+                myParent = clone(orig.getParent(), null);
+        } else
+            myParent = parent;
+
+        Context newCtx = new Context(orig.parsed, null, myParent);
+        newCtx.from = orig.from;
+        newCtx.meta = orig.meta;
+        newCtx.schemaAlias = orig.schemaAlias;
+        newCtx.setSchemas(orig.getSchemas());
+        newCtx.setVariables(orig.getVariables());
+        newCtx.cloneFrom = orig;
+        Object select = orig.getSelect();
+        if (select != null)
+            newCtx.setSelect(((SelectImpl)select).clone(newCtx));
+        newCtx.subquery = orig.subquery;
+        List<Context> subsels = orig.getSubselContexts();
+        if (subsels != null) {
+            for (Context subsel : subsels) 
+                newCtx.addSubselContext(clone(subsel, newCtx));
+        }
+
+        return newCtx;        
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java?rev=799134&r1=799133&r2=799134&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java Wed Jul 29 23:53:46 2009
@@ -26,6 +26,7 @@
 
 import org.apache.commons.lang.ObjectUtils;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCStoreQuery;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.meta.Discriminator;
 import org.apache.openjpa.jdbc.meta.FieldMapping;
@@ -596,8 +597,10 @@
                     break;
             }
             prevaction = action;
-            if (prevaction != null && prevaction.context != null) 
-                pstate.joins = pstate.joins.setJoinContext(prevaction.context);
+            if (prevaction != null && prevaction.context != null) {
+                Context jCtx = JDBCStoreQuery.getThreadLocalContext(prevaction.context);
+                pstate.joins = pstate.joins.setJoinContext(jCtx);
+            }
         }
         if (_varName != null)
             pstate.joins = pstate.joins.setVariable(_varName);
@@ -627,7 +630,7 @@
         return pstate;
     }
     
-    public String findSubqAlias(Select sel) {
+    private String findSubqAlias(Select sel) {
         Select pSel = sel.getParent();
         if (pSel == null)
             return null;

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java?rev=799134&r1=799133&r2=799134&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SelectConstructor.java Wed Jul 29 23:53:46 2009
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.openjpa.jdbc.kernel.JDBCStoreQuery;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.jdbc.sql.Joins;
@@ -122,18 +123,28 @@
      */
     private Select newSelect(ExpContext ctx, Select parent,
         String alias, QueryExpressions exps, QueryExpressionsState state) {
-        Select sel = parent != null ? _subselect
+        Select subselect = JDBCStoreQuery.getThreadLocalSelect(_subselect);
+        Select sel = parent != null ? subselect
             : ctx.store.getSQLFactory().newSelect();
         sel.setAutoDistinct((exps.distinct & exps.DISTINCT_AUTO) != 0);
         sel.setJoinSyntax(ctx.fetch.getJoinSyntax());
         sel.setParent(parent, alias);
 
+        Context[] qryCtx = JDBCStoreQuery.getThreadLocalContext();
+        Context lctx = null;
+        for (int i = 0; i < qryCtx.length; i++) {
+            if (qryCtx[i].cloneFrom == exps.ctx()) {
+                lctx = qryCtx[i];
+                break;
+            }
+        }
+
         if (sel.ctx() == null)
-            sel.setContext(exps.ctx());
+            sel.setContext(lctx);
 
-        if (parent == null && exps.ctx().getSubselContexts() != null) {
+        if (parent == null && lctx.getSubselContexts() != null) {
             // this is the case subselect was created before parent got created
-            List<Context> subselCtxs = exps.ctx().getSubselContexts();
+            List<Context> subselCtxs = lctx.getSubselContexts();
             for (Context subselCtx : subselCtxs) {
                 Select subsel = (Select) subselCtx.getSelect();
                 Subquery subquery = subselCtx.getSubquery();

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java?rev=799134&r1=799133&r2=799134&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java Wed Jul 29 23:53:46 2009
@@ -22,6 +22,7 @@
 
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCStoreQuery;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
 import org.apache.openjpa.jdbc.sql.Result;
@@ -119,9 +120,10 @@
     }
 
     public ExpState initialize(Select sel, ExpContext ctx, int flags) {
-        _select.setParent(sel, null);
+        Select select = JDBCStoreQuery.getThreadLocalSelect(_select);
+        select.setParent(sel, null);
         if (_exps.projections.length == 1) {
-            return ((Val) _exps.projections[0]).initialize(_select, ctx, flags);
+            return ((Val) _exps.projections[0]).initialize(select, ctx, flags);
         }
         return ExpState.NULL;
     }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java?rev=799134&r1=799133&r2=799134&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java Wed Jul 29 23:53:46 2009
@@ -2214,36 +2214,15 @@
     private static class Placeholder {
     }
     
-    public void reset() {
-        _aliases = null;
-        _eager = null;
-        _eagerKeys = null;
-        _expectedResultCount = 0;
-        _flags = 0;
-        _from = null;
-        _grouped = null;
-        _grouping = null;
-        _having = null;
-        _joins = null;
-        _joinSyntax = 0;
-        _nullIds = 0;
-        _ordered = null;
-        _ordering = null;
-        _orders = 0;
-        _outer = null;
-        _parent = null;
-        _placeholders = 0;
-        _preJoins = null;
-        _schemaAlias = null;
-        _selects._aliases = null;
-        _selects._ids = null;
-        _subPath = null;
-        _subsels = null;
-        _tables = null;
-        _where = null;
-        
+    public SelectImpl clone(Context ctx) {
+        SelectImpl sel = (SelectImpl) _conf.getSQLFactoryInstance().newSelect();
+        sel._ctx = ctx;
+        if (_parent != null && _parent.ctx() != null)
+            sel._parent = (SelectImpl)_parent.ctx().getSelect();
+        sel._schemaAlias = _schemaAlias;
+        sel._flags = _flags;
+        return sel;
     }
-    
 
     /**
      * Key type used for aliases.

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Context.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Context.java?rev=799134&r1=799133&r2=799134&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Context.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Context.java Wed Jul 29 23:53:46 2009
@@ -39,10 +39,11 @@
     public String schemaAlias;
     public Subquery subquery;
     public Expression from = null;
-    private Context parent = null;
+    public Context cloneFrom = null;
+    private final Context parent;
     private List<Context> subsels = null;
     private Object select = null;
-    private int aliasCount = -1; 
+    protected int aliasCount = -1; 
     private Map<String,Value> variables = new HashMap<String,Value>();
     private Map<String,ClassMetaData> schemas =
         new HashMap<String,ClassMetaData>();
@@ -120,7 +121,9 @@
      * Register the subquery context in this context.
      * @param sub
      */
-    private void addSubselContext(Context sub) {
+    public void addSubselContext(Context sub) {
+        if (sub == null)
+            return;
         if (subsels == null)
             subsels = new ArrayList<Context>();
         subsels.add(sub);
@@ -146,14 +149,18 @@
         return parent;
     }
 
-    public void setParent(Context parent) {
-        this.parent = parent;
-    }
-
     public void addVariable(String id, Value var) {
         variables.put(id.toLowerCase(), var);
     }
 
+    public Map<String,Value> getVariables() {
+        return variables;
+    }
+
+    public void setVariables(Map<String,Value> variables) {
+        this.variables = variables;
+    }
+
     public void addSchema(String id, ClassMetaData meta) {
         schemas.put(id.toLowerCase(), meta);
     }
@@ -164,14 +171,22 @@
         return null;
     }
 
+    public Map<String,ClassMetaData> getSchemas() {
+        return schemas;
+    }
+
+    public void setSchemas(Map<String,ClassMetaData> schemas) {
+        this.schemas = schemas;
+    }
+
     /**
      * Given an alias and return its associated variable.
-     * @param var
+     * @param alias
      * @return
      */
-    public Value getVariable(String var) {
-        Value variable = var == null ? null 
-            : variables.get(var.toLowerCase());
+    public Value getVariable(String alias) {
+        Value variable = alias == null ? null 
+            : variables.get(alias.toLowerCase());
         return variable;
     }
 

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=799134&r1=799133&r2=799134&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 Jul 29 23:53:46 2009
@@ -1418,10 +1418,8 @@
 
         // parse the subquery
         ParsedJPQL parsed = new ParsedJPQL(node.parser.jpql, node);
-        Context parent = ctx();
         Context subContext = new Context(parsed, null, ctx());
         contexts.push(subContext);
-        subContext.setParent(parent);
 
         ClassMetaData candidate = getCandidateMetaData(node);
         Subquery subq = subContext.getSubquery();