openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Craig L Russell <Craig.Russ...@Sun.COM>
Subject Re: svn commit: r557089 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/ openjpa-lib/src/main/ja
Date Wed, 18 Jul 2007 15:34:45 GMT
I don't suppose you have run this with the Sun JDK (that I've tested  
on).

Craig

On Jul 18, 2007, at 8:00 AM, Kevin Sutter wrote:

> Craig,
> I haven't taken the time to figure out the source of the problem  
> yet, but I
> am experiencing two NPE's in our test bucket when running with the  
> IBM JDK (
> 1.5.0 SR5).  I thought I would post a heads-up for any IBM JDK  
> users...
> Here are the errors that I received:
>
> Tests run: 4, Failures: 0, Errors: 2, Skipped: 0, Time elapsed:  
> 0.031 sec
> <<< FAILURE!
> testBackEdges(org.apache.openjpa.lib.graph.TestDepthFirstAnalysis)   
> Time
> elapsed: 0 sec  <<< ERROR!
> java.lang.NullPointerException
>        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.visit(
> DepthFirstAnalysis.java:97)
>        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.<init>(
> DepthFirstAnalysis.java:74)
>        at  
> org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.setUpGraph2(
> TestDepthFirstAnalysis.java:78)
>        at  
> org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.testBackEdges
> (TestDepthFirstAnalysis.java:105)
>        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>        at sun.reflect.NativeMethodAccessorImpl.invoke(
> NativeMethodAccessorImpl.java:64)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at junit.framework.TestCase.runTest(TestCase.java:154)
>        at junit.framework.TestCase.runBare(TestCase.java:127)
>        at junit.framework.TestResult$1.protect(TestResult.java:106)
>        at junit.framework.TestResult.runProtected(TestResult.java:124)
>        at junit.framework.TestResult.run(TestResult.java:109)
>        at junit.framework.TestCase.run(TestCase.java:118)
>        at org.apache.openjpa.lib.test.AbstractTestCase.run(
> AbstractTestCase.java:178)
>        at junit.framework.TestSuite.runTest(TestSuite.java:208)
>        at junit.framework.TestSuite.run(TestSuite.java:203)
>        at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at org.apache.maven.surefire.junit.JUnitTestSet.execute(
> JUnitTestSet.java:210)
>        at
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTest 
> Set(
> AbstractDirectoryTestSuite.java:135)
>        at
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(
> AbstractDirectoryTestSuite.java:122)
>        at org.apache.maven.surefire.Surefire.run(Surefire.java:129)
>        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>        at sun.reflect.NativeMethodAccessorImpl.invoke(
> NativeMethodAccessorImpl.java:64)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at
> org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(
> SurefireBooter.java:225)
>        at org.apache.maven.surefire.booter.SurefireBooter.main(
> SurefireBooter.java:747)
>
> testForwardEdges 
> (org.apache.openjpa.lib.graph.TestDepthFirstAnalysis)  Time
> elapsed: 0 sec  <<< ERROR!
> java.lang.NullPointerException
>        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.visit(
> DepthFirstAnalysis.java:97)
>        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.<init>(
> DepthFirstAnalysis.java:74)
>        at  
> org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.setUpGraph2(
> TestDepthFirstAnalysis.java:78)
>        at
> org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.testForwardEdges(
> TestDepthFirstAnalysis.java:129)
>        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>        at sun.reflect.NativeMethodAccessorImpl.invoke(
> NativeMethodAccessorImpl.java:64)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at junit.framework.TestCase.runTest(TestCase.java:154)
>        at junit.framework.TestCase.runBare(TestCase.java:127)
>        at junit.framework.TestResult$1.protect(TestResult.java:106)
>        at junit.framework.TestResult.runProtected(TestResult.java:124)
>        at junit.framework.TestResult.run(TestResult.java:109)
>        at junit.framework.TestCase.run(TestCase.java:118)
>        at org.apache.openjpa.lib.test.AbstractTestCase.run(
> AbstractTestCase.java:178)
>        at junit.framework.TestSuite.runTest(TestSuite.java:208)
>        at junit.framework.TestSuite.run(TestSuite.java:203)
>        at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at org.apache.maven.surefire.junit.JUnitTestSet.execute(
> JUnitTestSet.java:210)
>        at
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTest 
> Set(
> AbstractDirectoryTestSuite.java:135)
>        at
> org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(
> AbstractDirectoryTestSuite.java:122)
>        at org.apache.maven.surefire.Surefire.run(Surefire.java:129)
>        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>        at sun.reflect.NativeMethodAccessorImpl.invoke(
> NativeMethodAccessorImpl.java:64)
>        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> DelegatingMethodAccessorImpl.java:43)
>        at java.lang.reflect.Method.invoke(Method.java:615)
>        at
> org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(
> SurefireBooter.java:225)
>        at org.apache.maven.surefire.booter.SurefireBooter.main(
> SurefireBooter.java:747)
>
> I have also verified these problems with Mike (who also uses the  
> IBM JDK).
> Neither of us have taken the time to debug the problem yet, but we are
> willing to accept assistance...  :-)
>
> Later,
> Kevin
>
>
> On 7/17/07, clr@apache.org <clr@apache.org> wrote:
>>
>> Author: clr
>> Date: Tue Jul 17 16:56:45 2007
>> New Revision: 557089
>>
>> URL: http://svn.apache.org/viewvc?view=rev&rev=557089
>> Log:
>> OPENJPA-235 break-nullable-patch contributed by Markus Fuchs
>>
>> Added:
>>
>>     openjpa/trunk/openjpa-lib/src/main/resources/org/apache/ 
>> openjpa/lib/graph/
>>     openjpa/trunk/openjpa-lib/src/main/resources/org/apache/ 
>> openjpa/lib/graph/localizer.properties
>> (with props)
>>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/ 
>> apache/openjpa/jdbc/kernel/EntityE.java
>> (with props)
>> Modified:
>>
>>     openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/ 
>> jdbc/kernel/ConstraintUpdateManager.java
>>
>>     openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/ 
>> jdbc/schema/ForeignKey.java
>>
>>     openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/ 
>> openjpa/jdbc/kernel/localizer.properties
>>
>>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/DepthFirstAnalysis.java
>>
>>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/Edge.java
>>
>>     openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/ 
>> graph/TestDepthFirstAnalysis.java
>>
>>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/ 
>> apache/openjpa/jdbc/kernel/EntityB.java
>>
>>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/ 
>> apache/openjpa/jdbc/kernel/EntityC.java
>>
>>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/ 
>> apache/openjpa/jdbc/kernel/EntityD.java
>>
>>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/ 
>> apache/openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
>>
>> Modified:
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> kernel/ConstraintUpdateManager.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/ 
>> java/org/apache/openjpa/jdbc/kernel/ConstraintUpdateManager.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> kernel/ConstraintUpdateManager.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> kernel/ConstraintUpdateManager.java
>> Tue Jul 17 16:56:45 2007
>> @@ -24,6 +24,7 @@
>> import java.util.HashMap;
>> import java.util.Iterator;
>> import java.util.LinkedList;
>> +import java.util.List;
>> import java.util.Map;
>>
>> import org.apache.openjpa.jdbc.meta.ClassMapping;
>> @@ -43,6 +44,7 @@
>> import org.apache.openjpa.lib.util.Localizer;
>> import org.apache.openjpa.util.InternalException;
>> import org.apache.openjpa.util.OpenJPAException;
>> +import org.apache.openjpa.util.UserException;
>>
>> /**
>>   * <p>Standard update manager, capable of foreign key constraint
>> evaluation.</p>
>> @@ -163,7 +165,7 @@
>>              row2 = getInsertRow(insertMap, rowMgr, row);
>>              if (row2 != null) {
>>                  ignoreUpdates = false;
>> -                graphs[1] = addEdge(graphs[1], row, (PrimaryRow)  
>> row2,
>> null);
>> +                graphs[1] = addEdge(graphs[1], (PrimaryRow) row2,  
>> row,
>> null);
>>              }
>>
>>              // now check this row's fks against other deletes
>> @@ -180,7 +182,7 @@
>>                  row2 = rowMgr.getRow(fks[j].getPrimaryKeyTable(),
>>                      Row.ACTION_DELETE, fkVal, false);
>>                  if (row2 != null && row2.isValid() && row2 != row)
>> -                    graphs[1] = addEdge(graphs[1], row, (PrimaryRow)
>> row2,
>> +                    graphs[1] = addEdge(graphs[1], (PrimaryRow)  
>> row2,
>> row,
>>                          fks[j]);
>>              }
>>          }
>> @@ -249,7 +251,7 @@
>>                      Row.ACTION_INSERT, row.getForeignKeySet(fks[j]),
>> false);
>>                  if (row2 != null && row2.isValid() && (row2 != row
>>                      || fks[j].isDeferred() || fks[j].isLogical()))
>> -                    graph = addEdge(graph, (PrimaryRow) row2, row,
>> fks[j]);
>> +                    graph = addEdge(graph, row, (PrimaryRow) row2,
>> fks[j]);
>>              }
>>
>>              // see if there are any relation id columns dependent on
>> @@ -263,7 +265,7 @@
>>                  row2 = rowMgr.getRow(getBaseTable(sm),  
>> Row.ACTION_INSERT,
>>                      sm, false);
>>                  if (row2 != null && row2.isValid())
>> -                    graph = addEdge(graph, (PrimaryRow) row2, row,
>> cols[j]);
>> +                    graph = addEdge(graph, row, (PrimaryRow) row2,
>> cols[j]);
>>              }
>>          }
>>          return graph;
>> @@ -318,80 +320,205 @@
>>              return;
>>
>>          DepthFirstAnalysis dfa = newDepthFirstAnalysis(graph,
>> autoAssign);
>> +        Collection insertUpdates = new LinkedList();
>> +        Collection deleteUpdates = new LinkedList();
>> +        boolean recalculate;
>> +
>> +        // Handle circular constraints:
>> +        // - if deleted row A has a ciricular fk to deleted row B,
>> +        //   then use an update statement to null A's fk to B before
>> flushing,
>> +        //   and then flush
>> +        // - if inserted row A has a circular fk to updated/ 
>> inserted row
>> B,
>> +        //   then null the fk in the B row object, then flush,
>> +        //   and after flushing, use an update to set the fk back  
>> to A
>> +        // Depending on where circular dependencies are broken, the
>> +        // topological order of the graph nodes has to be re- 
>> calculated.
>> +        recalculate = resolveCycles(graph, dfa.getEdges 
>> (Edge.TYPE_BACK),
>> +                deleteUpdates, insertUpdates);
>> +        recalculate |= resolveCycles(graph, dfa.getEdges(
>> Edge.TYPE_FORWARD),
>> +                deleteUpdates, insertUpdates);
>> +
>> +        if (recalculate) {
>> +            dfa = recalculateDepthFirstAnalysis(graph, autoAssign);
>> +        }
>> +
>> +        // flush delete updates to null fks, then all rows in  
>> order, then
>> +        // the insert updates to set circular fk values
>> +        flush(deleteUpdates, psMgr);
>>          Collection nodes = dfa.getSortedNodes();
>> -        Collection backs = dfa.getEdges(Edge.TYPE_BACK);
>> +        for (Iterator itr = nodes.iterator(); itr.hasNext();)
>> +            psMgr.flush((RowImpl) itr.next());
>> +        flush(insertUpdates, psMgr);
>> +    }
>>
>> -        // handle circular constraints:
>> -        // - if deleted row A has a ciricular fk to deleted row  
>> B, then
>> use an
>> -        //   update statement to null A's fk to B
>> -        // - if inserted row A has a circular fk to updated/ 
>> inserted row
>> B,
>> -        //   then null the fk in the B row object, and after  
>> flushing,
>> use
>> -        //   an update to set the fk to back to A
>> -        Collection insertUpdates = null;
>> -        Collection deleteUpdates = null;
>> +    /**
>> +     * Break a circular dependency caused by delete operations.
>> +     * If deleted row A has a ciricular fk to deleted row B, then  
>> use an
>> update
>> +     * statement to null A's fk to B before deleting B, then  
>> delete A.
>> +     * @param edge Edge in the dependency graph corresponding to a
>> foreign key
>> +     * constraint. This dependency is broken by nullifying the  
>> foreign
>> key.
>> +     * @param deleteUpdates Collection of update statements that are
>> executed
>> +     * before the delete operations are flushed
>> +     */
>> +    private void addDeleteUpdate(Edge edge, Collection  
>> deleteUpdates)
>> +        throws SQLException {
>>          PrimaryRow row;
>>          RowImpl update;
>> -        Edge edge;
>> +        ForeignKey fk;
>> +
>> +        // copy where conditions into new update that nulls the fk
>> +        row = (PrimaryRow) edge.getTo();
>> +        update = new PrimaryRow(row.getTable(),  
>> Row.ACTION_UPDATE, null);
>> +        row.copyInto(update, true);
>> +        if (edge.getUserObject() instanceof ForeignKey) {
>> +            fk = (ForeignKey) edge.getUserObject();
>> +            update.setForeignKey(fk, row.getForeignKeyIO(fk), null);
>> +        } else
>> +            update.setNull((Column) edge.getUserObject());
>> +
>> +        deleteUpdates.add(update);
>> +    }
>> +
>> +    /**
>> +     * Break a circular dependency caused by insert operations.
>> +     * If inserted row A has a circular fk to updated/inserted  
>> row B,
>> +     * then null the fk in the B row object, then flush,
>> +     * and after flushing, use an update to set the fk back to A.
>> +     * @param row Row to be flushed
>> +     * @param edge Edge in the dependency graph corresponding to a
>> foreign key
>> +     * constraint. This dependency is broken by nullifying the  
>> foreign
>> key.
>> +     * @param insertUpdates Collection of update statements that are
>> executed
>> +     * after the insert/update operations are flushed
>> +     */
>> +    private void addInsertUpdate(PrimaryRow row, Edge edge,
>> +        Collection insertUpdates) throws SQLException {
>> +        RowImpl update;
>>          ForeignKey fk;
>>          Column col;
>> -        for (Iterator itr = backs.iterator(); itr.hasNext();) {
>> -            edge = (Edge) itr.next();
>> -            if (edge.getUserObject() == null)
>> -                throw new InternalException(_loc.get("del-ins- 
>> cycle"));
>> -
>> -            // use a primary row update to prevent setting pk and fk
>> values
>> -            // until after flush, to get latest auto-increment  
>> values
>> -            row = (PrimaryRow) edge.getTo();
>> -            if (row.getAction() == Row.ACTION_DELETE) {
>> -                // copy where conditions into new update that  
>> nulls the
>> fk
>> -                row = (PrimaryRow) edge.getFrom();
>> -                update = new PrimaryRow(row.getTable(),  
>> Row.ACTION_UPDATE
>> ,null);
>> -                row.copyInto(update, true);
>> -                if (edge.getUserObject() instanceof ForeignKey) {
>> -                    fk = (ForeignKey) edge.getUserObject();
>> -                    update.setForeignKey(fk, row.getForeignKeyIO 
>> (fk),
>> null);
>> -                } else
>> -                    update.setNull((Column) edge.getUserObject());
>> -
>> -                if (deleteUpdates == null)
>> -                    deleteUpdates = new LinkedList();
>> -                deleteUpdates.add(update);
>> -            } else {
>> -                // copy where conditions into new update that  
>> sets the fk
>> -                update = new PrimaryRow(row.getTable(),  
>> Row.ACTION_UPDATE
>> ,null);
>> -                if (row.getAction() == Row.ACTION_INSERT) {
>> -                    if (row.getPrimaryKey() == null)
>> -                        throw new
>> InternalException(_loc.get("ref-cycle"));
>> -                    update.wherePrimaryKey(row.getPrimaryKey());
>> -                } else
>> -                    row.copyInto(update, true);
>> -                if (edge.getUserObject() instanceof ForeignKey) {
>> -                    fk = (ForeignKey) edge.getUserObject();
>> -                    update.setForeignKey(fk, row.getForeignKeyIO 
>> (fk),
>> -                        row.getForeignKeySet(fk));
>> -                    row.clearForeignKey(fk);
>> -                } else {
>> -                    col = (Column) edge.getUserObject();
>> -                    update.setRelationId(col, row.getRelationIdSet 
>> (col),
>> -                        row.getRelationIdCallback(col));
>> -                    row.clearRelationId(col);
>> -                }
>>
>> -                if (insertUpdates == null)
>> -                    insertUpdates = new LinkedList();
>> -                insertUpdates.add(update);
>> +        // copy where conditions into new update that sets the fk
>> +        update = new PrimaryRow(row.getTable(),  
>> Row.ACTION_UPDATE, null);
>> +        if (row.getAction() == Row.ACTION_INSERT) {
>> +            if (row.getPrimaryKey() == null)
>> +                throw new InternalException(_loc.get("ref-cycle"));
>> +            update.wherePrimaryKey(row.getPrimaryKey());
>> +        } else {
>> +            // Row.ACTION_UPDATE
>> +            row.copyInto(update, true);
>> +        }
>> +        if (edge.getUserObject() instanceof ForeignKey) {
>> +            fk = (ForeignKey) edge.getUserObject();
>> +            update.setForeignKey(fk, row.getForeignKeyIO(fk),
>> +                row.getForeignKeySet(fk));
>> +            row.clearForeignKey(fk);
>> +        } else {
>> +            col = (Column) edge.getUserObject();
>> +            update.setRelationId(col, row.getRelationIdSet(col),
>> +                row.getRelationIdCallback(col));
>> +            row.clearRelationId(col);
>> +        }
>> +
>> +        insertUpdates.add(update);
>> +    }
>> +
>> +    /**
>> +     * Finds a nullable foreign key by walking the dependency cycle.
>> +     * Circular dependencies can be broken at this point.
>> +     * @param cycle Cycle in the dependency graph.
>> +     * @return Edge corresponding to a nullable foreign key.
>> +     */
>> +    private Edge findBreakableLink(List cycle) {
>> +        Edge breakableLink = null;
>> +        for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
>> +            Edge edge = (Edge) iter.next();
>> +            Object userObject = edge.getUserObject();
>> +            if (userObject instanceof ForeignKey) {
>> +                 if (!((ForeignKey) userObject).hasNotNullColumns 
>> ()) {
>> +                     breakableLink = edge;
>> +                     break;
>> +                 }
>> +            } else if (userObject instanceof Column) {
>> +                if (!((Column) userObject).isNotNull()) {
>> +                    breakableLink = edge;
>> +                    break;
>> +                }
>>              }
>>          }
>> +        return breakableLink;
>> +    }
>>
>> -        // flush delete updates to null fks, then all rows in  
>> order, then
>> -        // the insert updates to set circular fk values
>> -        if (deleteUpdates != null)
>> -            flush(deleteUpdates, psMgr);
>> -        for (Iterator itr = nodes.iterator(); itr.hasNext();)
>> -            psMgr.flush((RowImpl) itr.next());
>> -        if (insertUpdates != null)
>> -            flush(insertUpdates, psMgr);
>> -       }
>> +    /**
>> +     * Re-calculates the DepthFirstSearch analysis of the graph
>> +     * after some of the edges have been removed. Ensures
>> +     * that the dependency graph is cycle free.
>> +     * @param graph The graph of statements to be walked
>> +     * @param autoAssign Whether any of the rows in the graph  
>> have any
>> +     * auto-assign constraints
>> +     */
>> +    private DepthFirstAnalysis recalculateDepthFirstAnalysis 
>> (Graph graph,
>> +        boolean autoAssign) {
>> +        DepthFirstAnalysis dfa;
>> +        // clear previous traversal data
>> +        graph.clearTraversal();
>> +        dfa = newDepthFirstAnalysis(graph, autoAssign);
>> +        // make sure that the graph is non-cyclic now
>> +        assert (dfa.hasNoCycles()): _loc.get("graph-not-cycle- 
>> free");
>> +        return dfa;
>> +    }
>> +
>> +    /**
>> +     * Resolve circular dependencies by identifying and breaking
>> +     * a nullable foreign key.
>> +     * @param graph Dependency graph.
>> +     * @param edges Collection of edges. Each edge indicates a  
>> possible
>> +     * circular dependency
>> +     * @param deleteUpdates Collection of update operations  
>> (nullifying
>> +     * foreign keys) to be filled. These updates will be executed  
>> before
>> +     * the rows in the dependency graph are flushed
>> +     * @param insertUpdates CCollection of update operations  
>> (nullifying
>> +     * foreign keys) to be filled. These updates will be executed  
>> after
>> +     * the rows in the dependency graph are flushed
>> +     * @return Depending on where circular dependencies are  
>> broken, the
>> +     * topological order of the graph nodes has to be re-calculated.
>> +     */
>> +    private boolean resolveCycles(Graph graph, Collection edges,
>> +        Collection deleteUpdates, Collection insertUpdates)
>> +        throws SQLException {
>> +        boolean recalculate = false;
>> +        for (Iterator itr = edges.iterator(); itr.hasNext();) {
>> +            Edge edge = (Edge) itr.next();
>> +            List cycle = edge.getCycle();
>> +
>> +            if (cycle != null) {
>> +                // find a nullable foreign key
>> +                Edge breakableLink = findBreakableLink(cycle);
>> +                if (breakableLink == null) {
>> +                    throw new UserException(_loc.get("no-nullable- 
>> fk"));
>> +                }
>> +
>> +                // topologic node order must be re-calculated,   
>> if the
>> +                // breakable link is different from the edge where
>> +                // the circular dependency was originally detected
>> +                if (edge != breakableLink) {
>> +                    recalculate = true;
>> +                }
>> +
>> +                if (!breakableLink.isRemovedFromGraph()) {
>> +
>> +                    // use a primary row update to prevent  
>> setting pk and
>> fk values
>> +                    // until after flush, to get latest auto- 
>> increment
>> values
>> +                    PrimaryRow row = (PrimaryRow)  
>> breakableLink.getFrom
>> ();
>> +                    if (row.getAction() == Row.ACTION_DELETE) {
>> +                        addDeleteUpdate(breakableLink,  
>> deleteUpdates);
>> +                    } else {
>> +                        addInsertUpdate(row, breakableLink,
>> insertUpdates);
>> +                    }
>> +                    graph.removeEdge(breakableLink);
>> +                }
>> +            }
>> +        }
>> +        return recalculate;
>> +    }
>>
>>      /**
>>       * Create a new {@link DepthFirstAnalysis} suitable for the  
>> given
>> graph
>>
>> Modified:
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> schema/ForeignKey.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/ 
>> java/org/apache/openjpa/jdbc/schema/ForeignKey.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> schema/ForeignKey.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/ 
>> schema/ForeignKey.java
>> Tue Jul 17 16:56:45 2007
>> @@ -701,6 +701,19 @@
>>              && match(getPrimaryKeyColumns(), fkPKCols);
>>      }
>>
>> +    /**
>> +     * Checks for non-nullable local columns.
>> +     */
>> +    public boolean hasNotNullColumns() {
>> +      Column[] columns = getColumns();
>> +      for (int j = 0; j < columns.length; j++) {
>> +          if (columns[j].isNotNull()) {
>> +              return true;
>> +          }
>> +      }
>> +      return false;
>> +    }
>> +
>>      private static boolean match(Column[] cols, Column[] fkCols) {
>>          if (cols.length != fkCols.length)
>>              return false;
>>
>> Modified:
>> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/ 
>> jdbc/kernel/localizer.properties
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/ 
>> resources/org/apache/openjpa/jdbc/kernel/localizer.properties? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/ 
>> jdbc/kernel/localizer.properties
>> (original)
>> +++
>> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/ 
>> jdbc/kernel/localizer.properties
>> Tue Jul 17 16:56:45 2007
>> @@ -98,7 +98,15 @@
>>         \t[-properties/-p <properties file or resource>]\n\
>>         \t[-<property name> <property value>]*
>> bad-level: Invalid isolation level. Valid levels are -1, \
>> -    Connection.TRANSACTION_NONE,  
>> Connection.TRANSACTION_READ_UNCOMMITTED,
>> \
>> +    Connection.TRANSACTION_NONE,  
>> Connection.TRANSACTION_READ_UNCOMMITTED,
>> \
>>      Connection.TRANSACTION_READ_COMMITTED, \
>>      Connection.TRANSACTION_REPEATABLE_READ, or \
>>      Connection.TRANSACTION_SERIALIZABLE. Specified value: {0}.
>> +no-nullable-fk: No nullable foreign key found to resolve circular
>> flush\n\
>> +       dependency. During flush processing, changes to instances,  
>> new\n\
>> +       instances, and deleted instances must be processed in a  
>> specific
>> sequence\n\
>> +       to avoid foreign key constraint violations. The changes  
>> required
>> in this\n\
>> +       transaction cannot be reordered because none of the  
>> foreign key
>> constraints\n\
>> +       is nullable (optional).
>> +graph-not-cycle-free: A circular flush dependency has been found  
>> after
>> all \
>> +    circular dependencies should have been resolved.
>>
>> Modified:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/DepthFirstAnalysis.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/ 
>> java/org/apache/openjpa/lib/graph/DepthFirstAnalysis.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/DepthFirstAnalysis.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/DepthFirstAnalysis.java
>> Tue Jul 17 16:56:45 2007
>> @@ -18,6 +18,8 @@
>>   */
>> package org.apache.openjpa.lib.graph;
>>
>> +import org.apache.openjpa.lib.util.Localizer;
>> +
>> import java.util.AbstractList;
>> import java.util.ArrayList;
>> import java.util.Arrays;
>> @@ -26,6 +28,7 @@
>> import java.util.Comparator;
>> import java.util.HashMap;
>> import java.util.Iterator;
>> +import java.util.LinkedList;
>> import java.util.List;
>> import java.util.Map;
>>
>> @@ -42,6 +45,9 @@
>>   */
>> public class DepthFirstAnalysis {
>>
>> +    private static final Localizer _loc = Localizer.forPackage
>> +        (DepthFirstAnalysis.class);
>> +
>>      private final Graph _graph;
>>      private final Map _nodeInfo = new HashMap();
>>      private Comparator _comp;
>> @@ -65,14 +71,15 @@
>>              node = itr.next();
>>              info = (NodeInfo) _nodeInfo.get(node);
>>              if (info.color == NodeInfo.COLOR_WHITE)
>> -                visit(graph, node, info, 0);
>> +                visit(graph, node, info, 0, new LinkedList());
>>          }
>>      }
>>
>>      /**
>>       * Visit a node.  See Introduction to Algorithms book for  
>> details.
>>       */
>> -    private int visit(Graph graph, Object node, NodeInfo info,  
>> int time)
>> {
>> +    private int visit(Graph graph, Object node, NodeInfo info,  
>> int time,
>> +        List path) {
>>          // discover node
>>          info.color = NodeInfo.COLOR_GRAY;
>>
>> @@ -89,14 +96,24 @@
>>              otherInfo = (NodeInfo) _nodeInfo.get(other);
>>              if (otherInfo.color == NodeInfo.COLOR_WHITE) {
>>                  // undiscovered node; recurse into it
>> -                childTime = visit(graph, other, otherInfo, time);
>> +                path.add(edge);
>> +                childTime = visit(graph, other, otherInfo, time,  
>> path);
>> +                path.remove(edge);
>>                  edge.setType(Edge.TYPE_TREE);
>>              } else if (otherInfo.color == NodeInfo.COLOR_GRAY) {
>>                  childTime = -1;
>>                  edge.setType(Edge.TYPE_BACK);
>> +                // calculate the cycle including this edge
>> +                edge.setCycle(cycleForBackEdge(edge, path));
>>              } else {
>>                  childTime = otherInfo.finished;
>>                  edge.setType(Edge.TYPE_FORWARD);
>> +                // find the cycle including this edge
>> +                List cycle = new LinkedList();
>> +                cycle.add(edge);
>> +                if (cycleForForwardEdge(graph, other, node,  
>> cycle)) {
>> +                    edge.setCycle(cycle);
>> +                }
>>              }
>>              maxChildTime = Math.max(maxChildTime, childTime);
>>          }
>> @@ -134,7 +151,7 @@
>>      /**
>>       * Return all edges of the given type.  This method can be  
>> used to
>>       * discover all edges that cause cycles in the graph by  
>> passing it
>> -     * the {@link #EDGE_BACK} edge type.
>> +     * the {@link Edge#TYPE_BACK} or {@link Edge#TYPE_FORWARD}  
>> edge type.
>>       */
>>      public Collection getEdges(int type) {
>>          Collection typed = null;
>> @@ -167,6 +184,132 @@
>>      }
>>
>>      /**
>> +     * Returns a list of graph edges forming a cycle. The cycle  
>> begins
>> +     * with a type {@link Edge#TYPE_BACK} edge.
>> +     * @param backEdge "Starting" edge of the cycle
>> +     * @param path Continuous list of graph edges, may be null
>> +     * @param pos Index of the first edge in path continuing the  
>> cycle
>> +     * @return Cycle starting with a type {@link Edge#TYPE_BACK}  
>> edge
>> +     */
>> +    private List buildCycle(Edge backEdge, List path, int pos) {
>> +        int length = path != null ? path.size() - pos : 0;
>> +        List cycle = new ArrayList(length + 1);
>> +        cycle.add(0, backEdge);
>> +        for (int i = 0; i < length; i++) {
>> +            cycle.add(i + 1, path.get(pos + i));
>> +        }
>> +        return cycle;
>> +    }
>> +
>> +    /**
>> +     * Computes the list of edges forming a cycle. The cycle always
>> exists for
>> +     * a type {@link Edge#TYPE_BACK} edge. This method should  
>> only be
>> called
>> +     * for type {@link Edge#TYPE_BACK} edges.
>> +     * @param edge Edge where the cycle was detected
>> +     * @param path Path consisting of edges to the edge's  
>> starting node
>> +     * @return Cycle starting with a type {@link Edge#TYPE_BACK}  
>> edge
>> +     */
>> +    private List cycleForBackEdge(Edge edge, List path) {
>> +        if (edge.getType() != Edge.TYPE_BACK) {
>> +            return null;
>> +        }
>> +
>> +        List cycle;
>> +        int pos = 0;
>> +        if (path != null && edge.getFrom() != edge.getTo()) {
>> +            // Not a single edge loop
>> +            pos = findNodeInPath(edge.getTo(), path);
>> +            assert (pos >= 0): _loc.get("node-not-on-path", edge,
>> edge.getTo());
>> +        } else {
>> +            assert (edge.getFrom() == edge.getTo()):
>> +                    _loc.get("edge-no-loop", edge).getMessage();
>> +            path = null;
>> +        }
>> +        cycle = buildCycle(edge, path, pos);
>> +        assert (cycle != null): _loc.get("cycle-null",
>> edge).getMessage();
>> +        return cycle;
>> +    }
>> +
>> +    /**
>> +     * Computes the cycle of edges including node cycleTo. The  
>> cycle must
>> not
>> +     * necessarily exist. This method should only be called for type
>> +     * {@link Edge#TYPE_FORWARD} edges.
>> +     * @param graph Graph
>> +     * @param node Current node
>> +     * @param cycleTo End node for loop
>> +     * @param path Path from loop end node to current node
>> +     * @return True if a cycle has been found. The cycle will be
>> contained in
>> +     * the <code>path</code> parameter.
>> +     */
>> +    private boolean cycleForForwardEdge(Graph graph, Object node,
>> +        Object cycleTo, List path) {
>> +        boolean found = false;
>> +        Collection edges = graph.getEdgesFrom(node);
>> +        for (Iterator itr = edges.iterator(); !found &&  
>> itr.hasNext();) {
>> +            Edge edge = (Edge) itr.next();
>> +            Object other = edge.getOther(node);
>> +            // Single edge loops are ignored
>> +            if (node != other) {
>> +                if (other == cycleTo) {
>> +                    // Cycle complete
>> +                    path.add(edge);
>> +                    found = true;
>> +                } else if (!path.contains(edge)){
>> +                    // Walk this edge
>> +                    path.add(edge);
>> +                    found = cycleForForwardEdge(graph, other,  
>> cycleTo,
>> path);
>> +                    if (!found) {
>> +                        // Remove edge again
>> +                        path.remove(edge);
>> +                    }
>> +                }
>> +            }
>> +        }
>> +        return found;
>> +    }
>> +
>> +    /**
>> +     * Finds the position of the edge starting from a particular  
>> node in
>> the
>> +     * continuous list of edges.
>> +     * @param node Node on the cycle.
>> +     * @param path Continuous list of graph edges.
>> +     * @return Edge index if found, -1 otherwise.
>> +     */
>> +    private int findNodeInPath(Object node, List path) {
>> +        int pos = -1;
>> +        if (path != null) {
>> +            for (int i = 0; i < path.size(); i++) {
>> +                if (((Edge)path.get(i)).getFrom() == node) {
>> +                    pos = i;
>> +                }
>> +            }
>> +        }
>> +        return pos;
>> +    }
>> +
>> +    /**
>> +     * Test, if the analysis didn't find cycles.
>> +     */
>> +    public boolean hasNoCycles() {
>> +        // a) there must not be any back edges
>> +        if (!getEdges(Edge.TYPE_BACK).isEmpty()) {
>> +            return false;
>> +        }
>> +        // b) there might be forward edges
>> +        // make sure these don't indicate cycles
>> +        Collection edges = getEdges(Edge.TYPE_FORWARD);
>> +        if (!edges.isEmpty()) {
>> +            for (Iterator itr = edges.iterator(); itr.hasNext();) {
>> +                Edge edge = (Edge) itr.next();
>> +                if (edge.getCycle() != null)  {
>> +                    return false;
>> +                }
>> +            }
>> +        }
>> +        return true;
>> +    }
>> +
>> +    /**
>>       * Comparator for toplogically sorting entries in the node  
>> info map.
>>       */
>>      private static class NodeInfoComparator
>> @@ -184,8 +327,8 @@
>>              NodeInfo n1 = (NodeInfo) e1.getValue();
>>              NodeInfo n2 = (NodeInfo) e2.getValue();
>>
>> -            // reverse finished order
>> -            int ret = n2.finished - n1.finished;
>> +            // sort by finished order
>> +            int ret = n1.finished - n2.finished;
>>              if (ret == 0 && _subComp != null)
>>                  ret = _subComp.compare(e1.getKey(), e2.getKey());
>>              return ret;
>>
>> Modified:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/Edge.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/ 
>> java/org/apache/openjpa/lib/graph/Edge.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/Edge.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/ 
>> graph/Edge.java
>> Tue Jul 17 16:56:45 2007
>> @@ -18,6 +18,8 @@
>>   */
>> package org.apache.openjpa.lib.graph;
>>
>> +import java.util.List;
>> +
>> /**
>>   * <p>A graph edge.  Includes the from and to nodes, an arbitrary  
>> user
>> object,
>>   * and a weight.  Edges can be either directed or undirected.</p>
>> @@ -52,6 +54,8 @@
>>      private int _type = 0;
>>      private double _weight = 0;
>>      private Object _userObj = null;
>> +    private List _cycle = null;
>> +    private boolean _removedFromGraph = false;
>>
>>      /**
>>       * Constructor.
>> @@ -176,10 +180,39 @@
>>      }
>>
>>      /**
>> +     * List of edges forming a cycle. Only set for TYPE_BACK and
>> TYPE_FORWARD edges.
>> +     */
>> +    public List getCycle() {
>> +        return _cycle;
>> +    }
>> +
>> +    /**
>> +     * List of edges forming a cycle. Only set for TYPE_BACK and
>> TYPE_FORWARD edges.
>> +     */
>> +    public void setCycle(List cycle) {
>> +        _cycle = cycle;
>> +    }
>> +
>> +    /**
>> +     * Returns if this edge is (still) part of the graph.
>> +     */
>> +    public boolean isRemovedFromGraph() {
>> +        return _removedFromGraph;
>> +    }
>> +
>> +    /**
>> +     * Mark this edge as removed from the graph.
>> +     */
>> +    public void setRemovedFromGraph() {
>> +        this._removedFromGraph = true;
>> +    }
>> +
>> +    /**
>>       * Clear traversal info.
>>       */
>>      public void clearTraversal() {
>>          _type = 0;
>> +        _cycle = null;
>>      }
>>
>>      public String toString() {
>>
>> Added:
>> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/ 
>> lib/graph/localizer.properties
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/ 
>> resources/org/apache/openjpa/lib/graph/localizer.properties? 
>> view=auto&rev=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/ 
>> lib/graph/localizer.properties
>> (added)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/ 
>> lib/graph/localizer.properties
>> Tue Jul 17 16:56:45 2007
>> @@ -0,0 +1,22 @@
>> +# Licensed to the Apache Software Foundation (ASF) under one
>> +# or more contributor license agreements.  See the NOTICE file
>> +# distributed with this work for additional information
>> +# regarding copyright ownership.  The ASF licenses this file
>> +# to you under the Apache License, Version 2.0 (the
>> +# "License"); you may not use this file except in compliance
>> +# with the License.  You may obtain a copy of the License at
>> +#
>> +# http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing,
>> +# software distributed under the License is distributed on an
>> +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>> +# KIND, either express or implied.  See the License for the
>> +# specific language governing permissions and limitations
>> +# under the License.
>> +
>> +cycle-null: The cycle must not be null for edge {0}!
>> +edge-no-loop: Edge {0} must be a single node loop, if the depth  
>> first
>> search \
>> +    path is null!
>> +node-not-on-path: Could not find node {1} in the depth first  
>> search path
>> \
>> +    leading to edge {0}!
>>
>> Propchange:
>> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/ 
>> lib/graph/localizer.properties
>>
>> --------------------------------------------------------------------- 
>> ---------
>>     svn:eol-style = LF
>>
>> Modified:
>> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/ 
>> graph/TestDepthFirstAnalysis.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/test/ 
>> java/org/apache/openjpa/lib/graph/TestDepthFirstAnalysis.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/ 
>> graph/TestDepthFirstAnalysis.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/ 
>> graph/TestDepthFirstAnalysis.java
>> Tue Jul 17 16:56:45 2007
>> @@ -20,6 +20,7 @@
>>
>> import java.util.Collection;
>> import java.util.Iterator;
>> +import java.util.List;
>>
>> import org.apache.openjpa.lib.test.AbstractTestCase;
>>
>> @@ -34,6 +35,10 @@
>>      private DepthFirstAnalysis _dfa = null;
>>
>>      public void setUp() {
>> +        setUpGraph1();
>> +    }
>> +
>> +    public void setUpGraph1() {
>>          Graph graph = new Graph();
>>          Object node1 = new Object();
>>          Object node2 = new Object();
>> @@ -51,15 +56,37 @@
>>          _dfa = new DepthFirstAnalysis(graph);
>>      }
>>
>> +    public void setUpGraph2() {
>> +        Graph graph = new Graph();
>> +        Integer node1 = new Integer(5);
>> +        Integer node2 = new Integer(4);
>> +        Integer node3 = new Integer(3);
>> +        Integer node4 = new Integer(2);
>> +        Integer node5 = new Integer(1);
>> +        graph.addNode(node1);
>> +        graph.addNode(node2);
>> +        graph.addNode(node3);
>> +        graph.addNode(node4);
>> +        graph.addNode(node5);
>> +        graph.addEdge(new Edge(node1, node2, true));
>> +        graph.addEdge(new Edge(node2, node3, true));
>> +        graph.addEdge(new Edge(node3, node3, true));
>> +        graph.addEdge(new Edge(node3, node4, true));
>> +        graph.addEdge(new Edge(node4, node1, true));
>> +        graph.addEdge(new Edge(node4, node2, true));
>> +        graph.addEdge(new Edge(node5, node2, true));
>> +        _dfa = new DepthFirstAnalysis(graph);
>> +    }
>> +
>>      public void testNodeSorting() {
>>          Collection nodes = _dfa.getSortedNodes();
>>          assertEquals(4, nodes.size());
>>
>> -        int time = Integer.MAX_VALUE;
>> +        int time = 0;
>>          Object node;
>>          for (Iterator itr = nodes.iterator(); itr.hasNext();) {
>>              node = itr.next();
>> -            assertTrue(time >= _dfa.getFinishedTime(node));
>> +            assertTrue(time <= _dfa.getFinishedTime(node));
>>              time = _dfa.getFinishedTime(node);
>>          }
>>      }
>> @@ -74,6 +101,51 @@
>>              || edge1.getTo() == edge1.getFrom());
>>      }
>>
>> +    public void testBackEdges() {
>> +        setUpGraph2();
>> +        Collection edges = _dfa.getEdges(Edge.TYPE_BACK);
>> +        assertEquals(2, edges.size());
>> +        Iterator itr = edges.iterator();
>> +        Edge edge0 = (Edge) itr.next();
>> +        Edge edge1 = (Edge) itr.next();
>> +        if (edge0.getTo() == edge0.getFrom()) {
>> +            assertTrue(edge0.getCycle() != null && edge0.getCycle 
>> ().size()
>> == 1);
>> +            List cycle = edge1.getCycle();
>> +            assertTrue(cycle != null && cycle.size() == 4);
>> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
>> ((Edge)cycle.get(3)).getTo());
>> +        } else if (edge1.getTo() == edge1.getFrom()) {
>> +            assertTrue(edge1.getCycle() != null && edge1.getCycle 
>> ().size()
>> == 1);
>> +            assertTrue(edge1 == edge1.getCycle());
>> +            List cycle = edge0.getCycle();
>> +            assertTrue(cycle != null && cycle.size() == 4);
>> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
>> ((Edge)cycle.get(3)).getTo());
>> +        } else {
>> +            // should not happen
>> +            assertFalse(true);
>> +        }
>> +    }
>> +
>> +    public void testForwardEdges() {
>> +        setUpGraph2();
>> +        Collection edges = _dfa.getEdges(Edge.TYPE_FORWARD);
>> +        assertEquals(2, edges.size());
>> +        Iterator itr = edges.iterator();
>> +        Edge edge0 = (Edge) itr.next();
>> +        Edge edge1 = (Edge) itr.next();
>> +        if (edge0.getCycle() == null) {
>> +            List cycle = edge1.getCycle();
>> +            assertTrue(cycle != null && cycle.size() == 3);
>> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
>> ((Edge)cycle.get(2)).getTo());
>> +        } else if (edge1.getCycle() == null) {
>> +            List cycle = edge0.getCycle();
>> +            assertTrue(cycle != null && cycle.size() == 3);
>> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
>> ((Edge)cycle.get(2)).getTo());
>> +        } else {
>> +            // should not happen
>> +            assertFalse(true);
>> +        }
>> +    }
>> +
>>      public static void main(String[] args) {
>>          main(TestDepthFirstAnalysis.class);
>>      }
>>
>> Modified:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityB.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- 
>> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityB.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityB.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityB.java
>> Tue Jul 17 16:56:45 2007
>> @@ -40,7 +40,7 @@
>>
>>      private String name;
>>
>> -    @OneToOne(cascade = CascadeType.ALL)
>> +    @OneToOne(cascade = CascadeType.ALL, optional = false)
>>      @JoinColumn(name = "entityc_id", referencedColumnName =  
>> "entityc_id",
>>          nullable = false)
>>      @ForeignKey
>>
>> Modified:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityC.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- 
>> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityC.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityC.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityC.java
>> Tue Jul 17 16:56:45 2007
>> @@ -41,7 +41,7 @@
>>
>>      private String name;
>>
>> -    @OneToOne(cascade = CascadeType.ALL)
>> +    @OneToOne(cascade = CascadeType.ALL, optional = false)
>>      @JoinColumn(name = "entityd_id", referencedColumnName =  
>> "entityd_id",
>>          nullable = false)
>>      @ForeignKey
>>
>> Modified:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityD.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- 
>> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityD.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityD.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityD.java
>> Tue Jul 17 16:56:45 2007
>> @@ -18,12 +18,16 @@
>>   */
>> package org.apache.openjpa.jdbc.kernel;
>>
>> +import org.apache.openjpa.persistence.jdbc.ForeignKey;
>> +
>> import javax.persistence.Column;
>> import javax.persistence.Entity;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>> import javax.persistence.Version;
>> +import javax.persistence.OneToOne;
>> +import javax.persistence.JoinColumn;
>>
>> @Entity
>> public class EntityD {
>> @@ -35,6 +39,16 @@
>>
>>      private String name;
>>
>> +    @OneToOne
>> +    @JoinColumn(name = "entitya_id", referencedColumnName =  
>> "entitya_id")
>> +    @ForeignKey
>> +    private EntityA entityA;
>> +
>> +    @OneToOne
>> +    @JoinColumn(name = "entityb_id", referencedColumnName =  
>> "entityb_id")
>> +    @ForeignKey
>> +    private EntityB entityB;
>> +
>>      @Version
>>      private Integer optLock;
>>
>> @@ -49,6 +63,22 @@
>>          return id;
>>      }
>>
>> +    public EntityA getEntityA() {
>> +        return this.entityA;
>> +    }
>> +
>> +    public void setEntityA(EntityA entityA) {
>> +        this.entityA = entityA;
>> +    }
>> +
>> +    public EntityB getEntityB() {
>> +        return entityB;
>> +    }
>> +
>> +    public void setEntityB(EntityB entityB) {
>> +        this.entityB = entityB;
>> +    }
>> +
>>      public String getName() {
>>          return this.name;
>>      }
>>
>> Added:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityE.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- 
>> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityE.java? 
>> view=auto&rev=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityE.java
>> (added)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityE.java
>> Tue Jul 17 16:56:45 2007
>> @@ -0,0 +1,76 @@
>> +/*
>> + * Licensed to the Apache Software Foundation (ASF) under one
>> + * or more contributor license agreements.  See the NOTICE file
>> + * distributed with this work for additional information
>> + * regarding copyright ownership.  The ASF licenses this file
>> + * to you under the Apache License, Version 2.0 (the
>> + * "License"); you may not use this file except in compliance
>> + * with the License.  You may obtain a copy of the License at
>> + *
>> + * http://www.apache.org/licenses/LICENSE-2.0
>> + *
>> + * Unless required by applicable law or agreed to in writing,
>> + * software distributed under the License is distributed on an
>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>> + * KIND, either express or implied.  See the License for the
>> + * specific language governing permissions and limitations
>> + * under the License.
>> + */
>> +package org.apache.openjpa.jdbc.kernel;
>> +
>> +import javax.persistence.Column;
>> +import javax.persistence.Entity;
>> +import javax.persistence.GeneratedValue;
>> +import javax.persistence.GenerationType;
>> +import javax.persistence.Id;
>> +import javax.persistence.JoinColumn;
>> +import javax.persistence.OneToOne;
>> +import javax.persistence.Version;
>> +
>> +import org.apache.openjpa.persistence.jdbc.ForeignKey;
>> +
>> +@Entity
>> +public class EntityE {
>> +
>> +    @Id
>> +    @Column(name = "entitye_id", nullable = false)
>> +    @GeneratedValue(strategy = GenerationType.IDENTITY)
>> +    private Integer id;
>> +
>> +    private String name;
>> +
>> +    @OneToOne
>> +    @JoinColumn(name = "entityb_id", referencedColumnName =  
>> "entityb_id")
>> +    @ForeignKey
>> +    private EntityB entityB;
>> +
>> +    @Version
>> +    private Integer optLock;
>> +
>> +    public EntityE() {
>> +    }
>> +
>> +    public void setId(Integer id) {
>> +        this.id = id;
>> +    }
>> +
>> +    public Integer getId() {
>> +        return id;
>> +    }
>> +
>> +    public EntityB getEntityB() {
>> +        return this.entityB;
>> +    }
>> +
>> +    public void setEntityB(EntityB entityB) {
>> +        this.entityB = entityB;
>> +    }
>> +
>> +    public String getName() {
>> +        return this.name;
>> +    }
>> +
>> +    public void setName(String name) {
>> +        this.name = name;
>> +    }
>> +}
>>
>> Propchange:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/EntityE.java
>>
>> --------------------------------------------------------------------- 
>> ---------
>>     svn:eol-style = LF
>>
>> Modified:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence- 
>> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/ 
>> TestNoForeignKeyViolation.java? 
>> view=diff&rev=557089&r1=557088&r2=557089
>>
>> ===================================================================== 
>> =========
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/ 
>> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
>> Tue Jul 17 16:56:45 2007
>> @@ -34,15 +34,20 @@
>>      private EntityA entityA;
>>      private EntityB entityB;
>>      private EntityC entityC;
>> +    private EntityD entityD;
>>
>>      public void setUp() {
>> -        setUp(EntityA.class, EntityB.class, EntityC.class,  
>> EntityD.class
>> );
>> +        setUp(EntityA.class, EntityB.class, EntityC.class,  
>> EntityD.class,
>> EntityE.class);
>>
>> +        createTestData();
>> +    }
>> +
>> +    private void createTestData() {
>>          entityA = new EntityA();
>> +        entityB = new EntityB();
>>          entityC = new EntityC();
>> -        EntityD entityD = new EntityD();
>> +        entityD = new EntityD();
>>          entityA.setName("entityA");
>> -        entityB = new EntityB();
>>          entityB.setName("entityB");
>>          entityC.setName("entityC");
>>          entityD.setName("entityD");
>> @@ -52,7 +57,6 @@
>>      }
>>
>>      public void testSqlOrder() {
>> -
>>          EntityManager em = emf.createEntityManager();
>>          try {
>>              em.getTransaction().begin();
>> @@ -70,12 +74,78 @@
>>              EntityC newEntityC = new EntityC();
>>              newEntityC.setName("newEntityC");
>>              newEntityD = new EntityD();
>> -            newEntityD.setName("newEntityD");
>> +            newEntityD.setName("newNewEntityD");
>>              newEntityC.setEntityD(newEntityD);
>>              entityB.setEntityC(newEntityC);
>>
>>              em.getTransaction().begin();
>>              em.merge(entityB);
>> +            em.getTransaction().commit();
>> +        }
>> +        finally {
>> +            if (em.getTransaction().isActive())
>> +                em.getTransaction().rollback();
>> +            em.close();
>> +        }
>> +    }
>> +
>> +    public void testSimpleCycle() {
>> +        EntityManager em = emf.createEntityManager();
>> +        try {
>> +            em.getTransaction().begin();
>> +            entityD.setEntityA(entityA);
>> +            em.persist(entityA);
>> +            em.getTransaction().commit();
>> +        }
>> +        finally {
>> +            if (em.getTransaction().isActive())
>> +                em.getTransaction().rollback();
>> +            em.close();
>> +        }
>> +    }
>> +
>> +    public void testComplexCycle() {
>> +        EntityManager em = emf.createEntityManager();
>> +        try {
>> +            EntityE entityE = new EntityE();
>> +            entityE.setName("entityE");
>> +            entityE.setEntityB(entityB);
>> +
>> +            em.getTransaction().begin();
>> +            em.persist(entityE);
>> +            entityD.setEntityA(entityA);
>> +            em.persist(entityA);
>> +            em.getTransaction().commit();
>> +
>> +            em.getTransaction().begin();
>> +            em.remove(entityE);
>> +            em.remove(entityA);
>> +            em.getTransaction().commit();
>> +        }
>> +        finally {
>> +            if (em.getTransaction().isActive())
>> +                em.getTransaction().rollback();
>> +            em.close();
>> +        }
>> +    }
>> +
>> +    public void testComplexTwoCycles() {
>> +        EntityManager em = emf.createEntityManager();
>> +        try {
>> +            EntityE entityE = new EntityE();
>> +            entityE.setName("entityE");
>> +            entityE.setEntityB(entityB);
>> +
>> +            em.getTransaction().begin();
>> +            em.persist(entityE);
>> +            entityD.setEntityA(entityA);
>> +            entityD.setEntityB(entityB);
>> +            em.persist(entityA);
>> +            em.getTransaction().commit();
>> +
>> +            em.getTransaction().begin();
>> +            em.remove(entityE);
>> +            em.remove(entityA);
>>              em.getTransaction().commit();
>>          }
>>          finally {
>>
>>
>>

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!


Mime
View raw message