mirror of https://github.com/apache/openjpa.git
OPENJPA-591
git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/1.0.x@654226 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
667eed23b2
commit
9729eaed6e
|
@ -30,6 +30,7 @@ public class Compatibility {
|
||||||
private boolean _closeOnCommit = true;
|
private boolean _closeOnCommit = true;
|
||||||
private boolean _quotedNumbers = false;
|
private boolean _quotedNumbers = false;
|
||||||
private boolean _nonOptimisticVersionCheck = false;
|
private boolean _nonOptimisticVersionCheck = false;
|
||||||
|
private boolean _flushBeforeDetach = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to require exact identity value types when creating object
|
* Whether to require exact identity value types when creating object
|
||||||
|
@ -159,4 +160,34 @@ public class Compatibility {
|
||||||
public boolean getNonOptimisticVersionCheck() {
|
public boolean getNonOptimisticVersionCheck() {
|
||||||
return _nonOptimisticVersionCheck;
|
return _nonOptimisticVersionCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <P>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.
|
||||||
|
* <P>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.
|
||||||
|
* <P>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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ public class DetachManager
|
||||||
private final OpCallbacks _call;
|
private final OpCallbacks _call;
|
||||||
private final boolean _failFast;
|
private final boolean _failFast;
|
||||||
private boolean _flushed = false;
|
private boolean _flushed = false;
|
||||||
|
private boolean _flushBeforeDetach;
|
||||||
|
|
||||||
// if we're not detaching full, we need to track all detached objects;
|
// 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
|
// if we are, then we use a special field manager for more efficient
|
||||||
|
@ -82,7 +83,10 @@ public class DetachManager
|
||||||
if (!sm.isPersistent())
|
if (!sm.isPersistent())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
flushDirty(sm);
|
if (sm.getBroker().getConfiguration().getCompatibilityInstance()
|
||||||
|
.getFlushBeforeDetach()) {
|
||||||
|
flushDirty(sm);
|
||||||
|
}
|
||||||
|
|
||||||
ClassMetaData meta = sm.getMetaData();
|
ClassMetaData meta = sm.getMetaData();
|
||||||
boolean setState = meta.getDetachedState() != null
|
boolean setState = meta.getDetachedState() != null
|
||||||
|
@ -270,6 +274,9 @@ public class DetachManager
|
||||||
_detached = new IdentityMap();
|
_detached = new IdentityMap();
|
||||||
_fullFM = null;
|
_fullFM = null;
|
||||||
}
|
}
|
||||||
|
_flushBeforeDetach =
|
||||||
|
broker.getConfiguration().getCompatibilityInstance()
|
||||||
|
.getFlushBeforeDetach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -415,8 +422,14 @@ public class DetachManager
|
||||||
_broker.fireLifecycleEvent(toDetach, null, sm.getMetaData(),
|
_broker.fireLifecycleEvent(toDetach, null, sm.getMetaData(),
|
||||||
LifecycleEvent.BEFORE_DETACH);
|
LifecycleEvent.BEFORE_DETACH);
|
||||||
|
|
||||||
// any dirty instances cause a flush to occur
|
if(! _flushed) {
|
||||||
_flushed = _flushed || flushDirty(sm);
|
if(_flushBeforeDetach) {
|
||||||
|
// any dirty instances cause a flush to occur
|
||||||
|
flushDirty(sm);
|
||||||
|
}
|
||||||
|
_flushed = true;
|
||||||
|
}
|
||||||
|
|
||||||
BitSet fields = new BitSet();
|
BitSet fields = new BitSet();
|
||||||
preDetach(_broker, sm, fields);
|
preDetach(_broker, sm, fields);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.openjpa.persistence.simple;
|
package org.apache.openjpa.persistence.simple;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
@ -28,8 +30,10 @@ import javax.persistence.Table;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "ITEM")
|
@Table(name = "ITEM")
|
||||||
public class Item {
|
public class Item implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 489786296539819572L;
|
||||||
|
|
||||||
public int itemId;
|
public int itemId;
|
||||||
public String itemName;
|
public String itemName;
|
||||||
public java.math.BigDecimal itemPrice;
|
public java.math.BigDecimal itemPrice;
|
||||||
|
|
|
@ -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<Item> c = new ArrayList<Item>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -124,21 +124,43 @@ been rolled back, then the re-attachment process will throw an optimistic
|
||||||
concurrency exception.
|
concurrency exception.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
You can stop OpenJPA from assuming the transaction will commit by invoking
|
You can stop OpenJPA from assuming the transaction will commit in the following
|
||||||
<methodname>OpenJPAEntityManager.setRollbackOnly</methodname> prior to detaching
|
ways :
|
||||||
your objects. Setting the <literal>RollbackOnly</literal> flag prevents OpenJPA
|
<itemizedlist>
|
||||||
from flushing when detaching dirty objects; instead OpenJPA just runs its
|
<listitem>
|
||||||
pre-flush actions (see the <methodname>OpenJPAEntityManager.preFlush
|
<para>
|
||||||
</methodname>
|
Invoke <methodname>EntityTransaction.setRollbackOnly
|
||||||
|
</methodname> prior to detachingyour objects. Setting the
|
||||||
|
<literal>RollbackOnly</literal> flag prevents OpenJPA from
|
||||||
|
flushing when detaching dirty objects; instead OpenJPA just
|
||||||
|
runs its pre-flush actions (see the
|
||||||
|
<methodname>OpenJPAEntityManager.preFlush</methodname>
|
||||||
<ulink url="../javadoc/org/apache/openjpa/persistence/OpenJPAEntityManager.html">
|
<ulink url="../javadoc/org/apache/openjpa/persistence/OpenJPAEntityManager.html">
|
||||||
Javadoc</ulink> for details).
|
Javadoc</ulink> for details).
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
This allows you to use the same instances in multiple
|
This allows you to use the same instances in multiple
|
||||||
attach/modify/detach/rollback cycles. Alternatively, you might also prevent a
|
attach/modify/detach/rollback cycles.
|
||||||
flush by making your modifications outside of a transaction (with <literal>
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Make your modifications outside of a transaction (with <literal>
|
||||||
NontransactionalWrite</literal> enabled) before detaching.
|
NontransactionalWrite</literal> enabled) before detaching.
|
||||||
</para>
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Set <literal>flushBeforeDetach</literal>
|
||||||
|
to false (see <methodname>Compatibility.setFlushBeforeDetach
|
||||||
|
</methodname>
|
||||||
|
<ulink url="../javadoc/org/apache/openjpa/conf/Compatibility.html">
|
||||||
|
Javadoc</ulink> ). This option is similar to the first option, but does not
|
||||||
|
affect the current transaction.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
</section>
|
</section>
|
||||||
<section id="ref_guide_attach_behavior">
|
<section id="ref_guide_attach_behavior">
|
||||||
<title>
|
<title>
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -229,6 +229,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>rat-maven-plugin</artifactId>
|
<artifactId>rat-maven-plugin</artifactId>
|
||||||
|
<version>1.0-alpha-1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>verify</phase>
|
<phase>verify</phase>
|
||||||
|
|
Loading…
Reference in New Issue