Tim,

How about using @ForeignKey or changing mapping defaults so that OpenJPA knows 
the constraints defined in the database [1]?

<property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/>


Cheers,
Milosz

[1] 
http://openjpa.apache.org/builds/2.0.1/apache-openjpa-2.0.1/docs/manual/manual.html#ref_guide_mapping_jpa_fk



> A little light shed when I set logging level to DEBUG. It appears that
> OpenJPA is trying to delete from "top to bottom": i.e. RECIPE then
> GROUPING then INGREDIENT then STEP. Since I have cascade deletes
> declared in the DDL I can understand how it would get errors when trying
> to delete from GROUPING etc.
> 
> I am reluctant to relax the cascade deletes in DDL. So how can I
> persuade OpenJPA to delete from the bottom up or just delete from RECIPE
> and be done with it?  I tried the following with no difference in
> outcome:
>       * switching the UpdateManager to 'batching-operation-order'
>       * paring down @OneToMany.cascade to just PERSIST.
> 
> Still welcoming any insights...
> 
> 
> On Tue, 2011-10-18 at 11:43 -0400, Tim Watts wrote:
> > Hi,
> > 
> > I'm getting some exceptions in my unit tests due to optimistic locking
> > errors and I don't quite understand why. There are 2 scenarios but for
> > brevity I'll just focus on one here and maybe that'll shed light on the
> > other case. BTW, I'm using OpenJPA 2.0.1.
> > 
> > Basically, I try to delete an entity which has 2 levels of entity
> > collections. Since the entities have CascadeType.ALL I expected that a
> > simple em.remove() would cascade to the others without error. Instead I
> > get the optimistic lock errors. Any insights appreciated. And my
> > apologies for including so much source; I tried to pare it down to the
> > essentials.
> > 
> > Below is the test which fails. (Some background: The methods
> > makeStubRecipe(), addGroup/Ingredient/Step() are just helpers that
> > populate fields with arbitrary data. The basic structure of the data is:
> > a Recipe has a list of Groupings; a Grouping has a list of Ingredients
> > and a list of Steps.):
> > 
> > @Test
> > public void testDelete_Cascades() {
> >     Recipe recipe = makeStubRecipe();
> >     Grouping g = addGroup(recipe);
> >     addIngredient(g);
> >     addStep(g);
> > 
> >     // Add to db
> >     et.begin();
> >     em.persist(recipe);
> >     et.commit();
> > 
> >     assertFalse(em.contains(recipe));
> > 
> >     int gid = recipe.getGroupings().get(0).getGroupId();
> >     int iid = 
> > recipe.getGroupings().get(0).getIngredients().get(0).getIngredientId();
> >     int sid = recipe.getGroupings().get(0).getSteps().get(0).getStepId();
> > 
> >     // Now delete
> >     recipe = em.find(Recipe.class, recipe.getRecipeId());
> >     et.begin();
> >     em.remove(recipe);
> >     et.commit();    // <- FAILS HERE
> >     
> >     assertNull(em.find(Recipe.class, recipe.getRecipeId()));
> >     assertNull(em.find(Grouping.class, gid));
> >     assertNull(em.find(Ingredient.class, iid));
> >     assertNull(em.find(Step.class, sid));
> > }
> > 
> > If I add the following right after et.begin() it succeeds:
> > 
> >     for (Grouping g2 : recipe.getGroupings()) {
> >             for (Ingredient i: g2.getIngredients()) {
> >                     em.remove(i);
> >             }
> >             for (Step s : g2.getSteps()) {
> >                     em.remove(s);
> >             }
> >             em.remove(g2);
> >     }
> > 
> > But it seems like I shouldn't have to programmatically cascade the
> > removes. Am I simply mistaken? If so, what's the value of declaring a
> > cascading relationship? The DDL, BTW, does have 'on delete cascade' on
> > the foreign key constraints.
> > 
> > Here's the exception:
> > 
> > <openjpa-2.0.1-r422266:989424 fatal store error> 
> > org.apache.openjpa.persistence.RollbackException: Optimistic locking errors 
> > were detected when flushing to the data store.  The following objects may 
> > have been concurrently modified in another transaction: 
> > [org.cliftonfarm.feed.domain.Grouping-1069, 
> > org.cliftonfarm.feed.domain.Ingredient-1107, 
> > org.cliftonfarm.feed.domain.Step-1119]
> >     at 
> > org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:584)
> >     at 
> > org.cliftonfarm.feed.domain.RecipeTest.testDelete_Cascades(RecipeTest.java:1451)
> >     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >     at 
> > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> >     at 
> > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
> >     at java.lang.reflect.Method.invoke(Method.java:597)
> >     at 
> > org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
> >     at 
> > org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
> >     at 
> > org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
> >     at 
> > org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
> >     at 
> > org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
> >     at 
> > org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
> >     at org.junit.rules.TestWatchman$1.evaluate(TestWatchman.java:48)
> >     at 
> > org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
> >     at 
> > org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
> >     at 
> > org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
> >     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
> >     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
> >     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
> >     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
> >     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
> >     at 
> > org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
> >     at 
> > org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
> >     at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
> >     at 
> > org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
> >     at 
> > org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> >     at 
> > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
> >     at 
> > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
> >     at 
> > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
> >     at 
> > org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
> > Caused by: <openjpa-2.0.1-r422266:989424 nonfatal store error> 
> > org.apache.openjpa.persistence.OptimisticLockException: Optimistic locking 
> > errors were detected when flushing to the data store.  The following 
> > objects may have been concurrently modified in another transaction: 
> > [org.cliftonfarm.feed.domain.Grouping-1069, 
> > org.cliftonfarm.feed.domain.Ingredient-1107, 
> > org.cliftonfarm.feed.domain.Step-1119]
> >     at 
> > org.apache.openjpa.kernel.BrokerImpl.newFlushException(BrokerImpl.java:2291)
> >     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2139)
> >     at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2037)
> >     at 
> > org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1955)
> >     at 
> > org.apache.openjpa.kernel.LocalManagedRuntime.commit(LocalManagedRuntime.java:81)
> >     at org.apache.openjpa.kernel.BrokerImpl.commit(BrokerImpl.java:1479)
> >     at 
> > org.apache.openjpa.kernel.DelegatingBroker.commit(DelegatingBroker.java:925)
> >     at 
> > org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:560)
> >     ... 29 more
> > Caused by: <openjpa-2.0.1-r422266:989424 nonfatal store error> 
> > org.apache.openjpa.persistence.OptimisticLockException: An optimistic lock 
> > violation was detected when flushing object instance 
> > "org.cliftonfarm.feed.domain.Grouping-1069" to the data store.  This 
> > indicates that the object was concurrently modified in another transaction.
> > FailedObject: org.cliftonfarm.feed.domain.Grouping-1069
> >     at 
> > org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushAndUpdate(PreparedStatementManagerImpl.java:123)
> >     at 
> > org.apache.openjpa.jdbc.kernel.BatchingPreparedStatementManagerImpl.flushAndUpdate(BatchingPreparedStatementManagerImpl.java:81)
> >     at 
> > org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushInternal(PreparedStatementManagerImpl.java:99)
> >     at 
> > org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flush(PreparedStatementManagerImpl.java:87)
> >     at 
> > org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:550)
> >     at 
> > org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:120)
> >     at 
> > org.apache.openjpa.jdbc.kernel.BatchingConstraintUpdateManager.flush(BatchingConstraintUpdateManager.java:59)
> >     at 
> > org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:103)
> >     at 
> > org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:76)
> >     at 
> > org.apache.openjpa.jdbc.kernel.JDBCStoreManager.flush(JDBCStoreManager.java:731)
> >     at 
> > org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:131)
> >     ... 36 more
> > 
> > 
> > 
> > The JPA properties I'm setting on the EntityManagerFactory are:
> > 
> > #-----------------------------------------------------------------
> > javax.persistence.jdbc.driver=org.apache.derby.jdbc.ClientDriver
> > javax.persistence.jdbc.url=jdbc:derby://localhost/feed
> > javax.persistence.jdbc.user=<whatever>
> > javax.persistence.jdbc.password=<whatever>
> > javax.persistence.sharedCache.mode=UNSPECIFIED
> > 
> > openjpa.Specification="JPA 2.0"
> > openjpa.AutoDetach=close,commit
> > openjpa.DynamicDataStructs=true
> > openjpa.Log=log4j
> > openjpa.TransactionMode=local
> > openjpa.jdbc.SynchronizeMappings=refresh
> > openjpa.jdbc.ResultSetType=scroll-insensitive
> > openjpa.jdbc.UpdateManager=batching-constraint
> > #-----------------------------------------------------------------
> > 
> > Finally, here's the entity sources with most of the boring details cut
> > out:
> > 
> > //----------------------------------------------------------------
> > @MappedSuperclass
> > public abstract class Versionable {
> > 
> >     @Version
> >     private Integer version;
> > 
> >     public Integer getVersion() {
> >             return version;
> >     }
> > }
> > 
> > //----------------------------------------------------------------
> > @Entity
> > public class Recipe extends Versionable implements Serializable {
> > 
> >     private static final long serialVersionUID = -2136187716531617857L;
> > 
> >     /** Unique ID for this Recipe */
> >     @Id
> >     @Column (name="RECIPE_ID", insertable=false, nullable=false, 
> > updatable=false)
> >     @GeneratedValue (strategy=GenerationType.IDENTITY)
> >     private Integer recipeId;
> > 
> >     @OneToMany (mappedBy="recipe", 
> >             cascade={CascadeType.ALL}, 
> >             fetch=FetchType.LAZY, 
> >             orphanRemoval=true)
> >     @OrderBy ("position")
> >     private List<Grouping> groupings = new LinkedList<Grouping>();
> > 
> >     /* ... uninteresting data fields omitted... */
> > 
> >     //--------------------------------------------------------------- 
> > ACCESSORS
> > 
> >     public Integer getRecipeId() {
> >             return recipeId;
> >     }
> > 
> >     public List<Grouping> getGroupings() {
> >             return groupings;
> >     }
> > 
> >     public void setGroupings(List<Grouping> groupings) {
> >             this.groupings = new LinkedList<Grouping>();
> >             this.groupings.addAll(groupings);
> >             for (Grouping g : this.groupings) {
> >                     // Welcome to the family:
> >                     g.setRecipe(this);
> >             }
> >     }
> > 
> >     /* ... uninteresting accessors & other methods omitted... */
> > }
> > 
> > //----------------------------------------------------------------
> > @Entity
> > public class Grouping extends Versionable implements Serializable {
> > 
> >     private static final long serialVersionUID = 4129360725018241352L;
> > 
> >     /** Unique ID for this Grouping */
> >     @Id
> >     @Column (name="GROUP_ID", insertable=false, nullable=false, 
> > updatable=false)
> >     @GeneratedValue (strategy=GenerationType.IDENTITY)
> >     private Integer groupId;
> > 
> >     /** Recipe to which this Grouping belongs */
> >     @ManyToOne
> >     @JoinColumn (name="RECIPE_ID", insertable=true, nullable=false, 
> > updatable=true)
> >     private Recipe recipe;
> >     
> >     @OneToMany (mappedBy="grouping", 
> >             cascade={CascadeType.ALL}, 
> >             fetch=FetchType.LAZY, 
> >             orphanRemoval=true)
> >     @OrderBy ("position")
> >     private List<Ingredient> ingredients = new LinkedList<Ingredient>();
> > 
> >     @OneToMany (mappedBy="grouping", 
> >             cascade={CascadeType.ALL}, 
> >             fetch=FetchType.LAZY, 
> >             orphanRemoval=true)
> >     @OrderBy ("position")
> >     private List<Step> steps = new LinkedList<Step>();
> > 
> >     /* ... uninteresting data fields omitted... */
> > 
> >     //--------------------------------------------------------------- 
> > ACCESSORS
> > 
> >     public Integer getGroupId() {
> >             return groupId;
> >     }
> > 
> >     public Recipe getRecipe() {
> >             return recipe;
> >     }
> > 
> >     public void setRecipe(Recipe recipe) {
> >             this.recipe = recipe;
> >     }
> > 
> >     public List<Ingredient> getIngredients() {
> >             return ingredients;
> >     }
> > 
> >     public void setIngredients(List<Ingredient> ingredients) {
> >             this.ingredients = new LinkedList<Ingredient>();
> >             this.ingredients.addAll(ingredients);
> >             for (Ingredient i : this.ingredients) {
> >                     // Welcome to the family:
> >                     i.setGrouping(this);
> >             }
> >     }
> > 
> >     public List<Step> getSteps() {
> >             return steps;
> >     }
> > 
> >     public void setSteps(List<Step> steps) {
> >             this.steps = new LinkedList<Step>();
> >             this.steps.addAll(steps);
> >             for (Step s : this.steps) {
> >                     // Welcome to the family:
> >                     s.setGrouping(this);
> >             }
> >     }
> > 
> >     /* ... uninteresting accessors & other methods omitted... */
> > }
> > 
> > //----------------------------------------------------------------
> > @Entity
> > public class Ingredient extends Versionable implements Serializable {
> > 
> >     private static final long serialVersionUID = 8616212484953302289L;
> > 
> >     /** Unique ID for this Ingredient */
> >     @Id
> >     @Column (name="INGREDIENT_ID", insertable=false, nullable=false, 
> > updatable=false)
> >     @GeneratedValue (strategy=GenerationType.IDENTITY)
> >     private Integer ingredientId;
> > 
> >     /** The Grouping to which this Ingredient belongs */
> >     @ManyToOne
> >     @JoinColumn (name="GROUP_ID", insertable=true, nullable=false, 
> > updatable=true)
> >     private Grouping grouping;
> > 
> >     /* ... uninteresting data fields omitted... */
> > 
> >     //--------------------------------------------------------------- 
> > ACCESSORS
> > 
> >     public Integer getIngredientId() {
> >             return ingredientId;
> >     }
> > 
> >     public Grouping getGrouping() {
> >             return grouping;
> >     }
> > 
> >     public void setGrouping(Grouping grouping) {
> >             this.grouping = grouping;
> >     }
> > 
> >     /* ... uninteresting accessors & other methods omitted... */
> > 
> > }
> > 
> > //----------------------------------------------------------------
> > @Entity
> > public class Step extends Versionable implements Serializable {
> > 
> >     private static final long serialVersionUID = -3765886461428007870L;
> > 
> >     /** Unique ID for this Step */
> >     @Id
> >     @Column (name="STEP_ID", insertable=false, nullable=false, 
> > updatable=false)
> >     @GeneratedValue (strategy=GenerationType.IDENTITY)
> >     private Integer stepId;
> > 
> >     /** The Grouping to which this Step belongs */
> >     @ManyToOne
> >     @JoinColumn (name="GROUP_ID", insertable=true, nullable=false, 
> > updatable=true)
> >     private Grouping grouping;
> > 
> >     /* ... uninteresting data fields omitted... */
> > 
> >     //--------------------------------------------------------------- 
> > ACCESSORS
> > 
> >     public Integer getStepId() {
> >             return stepId;
> >     }
> > 
> >     public Grouping getGrouping() {
> >             return grouping;
> >     }
> > 
> >     public void setGrouping(Grouping grouping) {
> >             this.grouping = grouping;
> >     }
> > 
> >     /* ... uninteresting accessors & other methods omitted... */
> > 
> > }
> > 
> > 
> 
> 
> 

Reply via email to