openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Kevin Sutter" <kwsut...@gmail.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:00:40 GMT
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.executeTestSet(
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.executeTestSet(
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 {
>
>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message