diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index e8aa07b6c..89859b45e 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -30,6 +30,7 @@ public class Compatibility { private boolean _closeOnCommit = true; private boolean _quotedNumbers = false; private boolean _nonOptimisticVersionCheck = false; + private boolean _flushBeforeDetach = true; /** * Whether to require exact identity value types when creating object @@ -159,4 +160,34 @@ public class Compatibility { public boolean getNonOptimisticVersionCheck() { return _nonOptimisticVersionCheck; } + + /** + *

Whether OpenJPA should flush changes before detaching or serializing an + * entity. In JPA this is usually false, but other persistence frameworks + * (ie JDO) may expect it to be true. + *

Prior to version 1.0.3 and 1.2.0 changes were always flushed. + * + * @since 1.0.3 + * @since 1.2.0 + * @return true if changes should be flushed, otherwise false. + */ + public boolean getFlushBeforeDetach() { + return _flushBeforeDetach; + } + + /** + * Whether OpenJPA should flush changes before detaching or serializing an + * entity. In JPA this is usually false, but other persistence frameworks + * (ie JDO) may expect it to be true. + *

Prior to version 1.0.3 and 1.2.0 changes were always flushed. + * + * @since 1.0.3 + * @since 1.2.0 + * @param beforeDetach if true changes will be flushed before detaching or + * serializing an entity. + */ + public void setFlushBeforeDetach(boolean beforeDetach) { + _flushBeforeDetach = beforeDetach; + } + } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java index f868bb5f6..b821825c8 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java @@ -67,6 +67,7 @@ public class DetachManager private final OpCallbacks _call; private final boolean _failFast; private boolean _flushed = false; + private boolean _flushBeforeDetach; // if we're not detaching full, we need to track all detached objects; // if we are, then we use a special field manager for more efficient @@ -82,7 +83,10 @@ public class DetachManager if (!sm.isPersistent()) return false; - flushDirty(sm); + if (sm.getBroker().getConfiguration().getCompatibilityInstance() + .getFlushBeforeDetach()) { + flushDirty(sm); + } ClassMetaData meta = sm.getMetaData(); boolean setState = meta.getDetachedState() != null @@ -270,6 +274,9 @@ public class DetachManager _detached = new IdentityMap(); _fullFM = null; } + _flushBeforeDetach = + broker.getConfiguration().getCompatibilityInstance() + .getFlushBeforeDetach(); } /** @@ -415,8 +422,14 @@ public class DetachManager _broker.fireLifecycleEvent(toDetach, null, sm.getMetaData(), LifecycleEvent.BEFORE_DETACH); - // any dirty instances cause a flush to occur - _flushed = _flushed || flushDirty(sm); + if(! _flushed) { + if(_flushBeforeDetach) { + // any dirty instances cause a flush to occur + flushDirty(sm); + } + _flushed = true; + } + BitSet fields = new BitSet(); preDetach(_broker, sm, fields); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Item.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Item.java index 04c8d1f20..eca116562 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Item.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Item.java @@ -18,6 +18,8 @@ */ package org.apache.openjpa.persistence.simple; +import java.io.Serializable; + import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; @@ -28,8 +30,10 @@ import javax.persistence.Table; @Entity @Table(name = "ITEM") -public class Item { +public class Item implements Serializable { + private static final long serialVersionUID = 489786296539819572L; + public int itemId; public String itemName; public java.math.BigDecimal itemPrice; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestFlushBeforeDetach.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestFlushBeforeDetach.java new file mode 100644 index 000000000..f18a1085b --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestFlushBeforeDetach.java @@ -0,0 +1,146 @@ +/* + * 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.persistence.simple; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.EntityManager; + +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestFlushBeforeDetach extends SQLListenerTestCase { + + private int _id; + + public void setUp() { + setUp(Item.class,"openjpa.Compatibility", + "default(flushBeforeDetach=false)"); + persistSampleEntity(); + } + + private void persistSampleEntity() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + Item i = new Item(); + em.persist(i); + em.getTransaction().commit(); + em.refresh(i); + _id = i.getItemId(); + em.close(); + } + + public void testClear() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Item i = em.find(Item.class, _id); + + i.setItemData("ABCD"); + + em.clear(); + em.getTransaction().rollback(); + assertNotSQL("UPDATE ITEM.*"); + em.close(); + + } + + public void testDetach() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Item i = em.find(Item.class, _id); + + i.setItemData("EFGH"); + + OpenJPAPersistence.cast(em).detach(i); + em.getTransaction().rollback(); + assertNotSQL("UPDATE ITEM SET.*"); + em.close(); + + } + + public void testDetachAll() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Item i = em.find(Item.class, _id); + + i.setItemData("IJKL"); + + OpenJPAPersistence.cast(em).detachAll(i); + em.getTransaction().rollback(); + assertNotSQL("UPDATE ITEM SET.*"); + em.close(); + } + + public void testDetachAllCollection() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Item i = em.find(Item.class, _id); + + i.setItemData("MNOP"); + + Collection c = new ArrayList(); + c.add(i); + OpenJPAPersistence.cast(em).detachAll(c); + em.getTransaction().rollback(); + assertNotSQL("UPDATE ITEM SET.*"); + em.close(); + } + + public void testSerialize() throws Exception { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Item i = em.find(Item.class, _id); + + i.setItemData("QRSTU"); + + serializeObject(i); + + em.getTransaction().rollback(); + assertNotSQL("UPDATE ITEM SET.*"); + em.close(); + } + + /** + * Helper to serialize an object to a byte[] + */ + private Object serializeObject(Object orig) throws Exception { + Object deserialized = null; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(orig); + + ByteArrayInputStream bais = + new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + + deserialized = ois.readObject(); + return deserialized; + } +} diff --git a/openjpa-project/src/doc/manual/ref_guide_remote.xml b/openjpa-project/src/doc/manual/ref_guide_remote.xml index bcd5deae8..a4b5b3311 100644 --- a/openjpa-project/src/doc/manual/ref_guide_remote.xml +++ b/openjpa-project/src/doc/manual/ref_guide_remote.xml @@ -124,21 +124,43 @@ been rolled back, then the re-attachment process will throw an optimistic concurrency exception. -You can stop OpenJPA from assuming the transaction will commit by invoking -OpenJPAEntityManager.setRollbackOnly prior to detaching -your objects. Setting the RollbackOnly flag prevents OpenJPA -from flushing when detaching dirty objects; instead OpenJPA just runs its -pre-flush actions (see the OpenJPAEntityManager.preFlush - +You can stop OpenJPA from assuming the transaction will commit in the following +ways : + + + + Invoke EntityTransaction.setRollbackOnly + prior to detachingyour objects. Setting the + RollbackOnly flag prevents OpenJPA from + flushing when detaching dirty objects; instead OpenJPA just + runs its pre-flush actions (see the + OpenJPAEntityManager.preFlush Javadoc for details). This allows you to use the same instances in multiple -attach/modify/detach/rollback cycles. Alternatively, you might also prevent a -flush by making your modifications outside of a transaction (with +attach/modify/detach/rollback cycles. + + + + + Make your modifications outside of a transaction (with NontransactionalWrite enabled) before detaching. + + + + Set flushBeforeDetach + to false (see Compatibility.setFlushBeforeDetach + + +Javadoc ). This option is similar to the first option, but does not + affect the current transaction. + + + +

diff --git a/pom.xml b/pom.xml index ccefd0365..6597a7ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -229,6 +229,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>rat-maven-plugin</artifactId> + <version>1.0-alpha-1</version> <executions> <execution> <phase>verify</phase>