You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by dw...@apache.org on 2010/03/06 04:26:24 UTC
svn commit: r919696 - in /openjpa/trunk:
openjpa-kernel/src/main/java/org/apache/openjpa/kernel/
openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/
openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/
Author: dwoods
Date: Sat Mar 6 03:26:24 2010
New Revision: 919696
URL: http://svn.apache.org/viewvc?rev=919696&view=rev
Log:
OPENJPA-1097 Detachment processing of our proxied mutable types (Date, Timestamp, etc) needs to be consistent as EM.clear() was not unproxying JavaTypes.OBJECT fields but detach()/detachAll() were. Thanks to Rick Curtis for the DetachManager changes.
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java (with props)
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java (with props)
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java?rev=919696&r1=919695&r2=919696&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java Sat Mar 6 03:26:24 2010
@@ -476,8 +476,7 @@
_fullFM.reproxy(detSM);
_fullFM.setStateManager(null);
} else {
- InstanceDetachFieldManager fm = new InstanceDetachFieldManager
- (detachedPC, detSM);
+ InstanceDetachFieldManager fm = new InstanceDetachFieldManager(detachedPC, detSM);
fm.setStateManager(sm);
fm.detachFields(fields);
}
@@ -548,28 +547,28 @@
* Unproxies second class object fields.
*/
public void reproxy(DetachedStateManager dsm) {
- FieldMetaData[] fmds = sm.getMetaData().getFields();
- for (int i = 0; i < fmds.length; i++) {
- switch (fmds[i].getDeclaredTypeCode()) {
+ for (FieldMetaData fmd : sm.getMetaData().getProxyFields()) {
+ switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
// lrs proxies not detached
- if (fmds[i].isLRS()) {
+ if (fmd.isLRS()) {
objval = null;
- sm.replaceField(getDetachedPersistenceCapable(),
- this, i);
+ sm.replaceField(getDetachedPersistenceCapable(), this, fmd.getIndex());
break;
}
// no break
case JavaTypes.CALENDAR:
case JavaTypes.DATE:
case JavaTypes.OBJECT:
- sm.provideField(getDetachedPersistenceCapable(), this, i);
+ sm.provideField(getDetachedPersistenceCapable(), this, fmd.getIndex());
if (objval instanceof Proxy) {
Proxy proxy = (Proxy) objval;
if (proxy.getChangeTracker() != null)
proxy.getChangeTracker().stopTracking();
- proxy.setOwner(dsm, (dsm == null) ? -1 : i);
+ proxy.setOwner(dsm, (dsm == null) ? -1 : fmd.getIndex());
+ objval = proxy.copy(proxy);
+ sm.replaceField(getDetachedPersistenceCapable(), this, fmd.getIndex());
}
}
}
@@ -711,8 +710,10 @@
* Set the owner of the field's proxy to the detached state manager.
*/
private Object reproxy(Object obj, int field) {
- if (obj != null && _detSM != null && obj instanceof Proxy)
+ if (obj != null && _detSM != null && obj instanceof Proxy){
((Proxy) obj).setOwner(_detSM, field);
+ return ((Proxy) obj).copy(obj);
+ }
return obj;
}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java?rev=919696&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java Sat Mar 6 03:26:24 2010
@@ -0,0 +1,107 @@
+/*
+ * 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.detach;
+
+import java.io.Serializable;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+@Entity
+@Table(name="Entity20_detach")
+public class Entity20 implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ private Integer id;
+
+ @Column(name = "sqldate" )
+ @Temporal(TemporalType.DATE)
+ private Date sqlDate;
+
+ @Column(name = "sqltime")
+ @Temporal(TemporalType.TIME)
+ private Time sqlTime;
+
+ @Column(name = "sqltimestamp")
+ @Temporal(TemporalType.TIMESTAMP)
+ private Timestamp sqlTimestamp;
+
+ private String name;
+
+ public Entity20() {
+ }
+
+ public Entity20(int id) {
+ this.id = new Integer(id);
+ this.name = this.id.toString();
+ Long time = System.currentTimeMillis();
+ this.sqlTime = new Time(time);
+ this.sqlDate = new Date(time);
+ this.sqlTimestamp = new Timestamp(time);
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setDate(Date d) {
+ sqlDate = d;
+ }
+
+ public Date getDate() {
+ return sqlDate;
+ }
+
+ public void setTime(Time t) {
+ sqlTime = t;
+ }
+
+ public Time getTime() {
+ return sqlTime;
+ }
+
+ public void setTimestamp(Timestamp t) {
+ sqlTimestamp = t;
+ }
+
+ public Timestamp getTimestamp() {
+ return sqlTimestamp;
+ }
+}
Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/Entity20.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java?rev=919696&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java Sat Mar 6 03:26:24 2010
@@ -0,0 +1,262 @@
+/*
+ * 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.detach;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+
+import org.apache.openjpa.conf.Compatibility;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+public class TestDetachNoProxy extends SingleEMFTestCase {
+
+ private static final int numEntities = 3;
+ private static final String PROXY = new String("$proxy");
+ private Log log;
+
+ public void setUp() {
+ setUp(DROP_TABLES, Entity20.class);
+ log = emf.getConfiguration().getLog("test");
+
+ // check and set Compatibility values to new 2.0 values
+ Compatibility compat = emf.getConfiguration().getCompatibilityInstance();
+ assertNotNull(compat);
+ if (log.isTraceEnabled()) {
+ log.info("Before set, FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+ log.info("Before set, CopyOnDetach=" + compat.getCopyOnDetach());
+ log.info("Before set, CascadeWithDetach=" + compat.getCascadeWithDetach());
+ }
+ compat.setFlushBeforeDetach(false);
+ compat.setCopyOnDetach(false);
+ compat.setCascadeWithDetach(false);
+ if (log.isTraceEnabled()) {
+ log.info("After set, FlushBeforeDetach=" + compat.getFlushBeforeDetach());
+ log.info("After set, CopyOnDetach=" + compat.getCopyOnDetach());
+ log.info("After set, CascadeWithDetach=" + compat.getCascadeWithDetach());
+ }
+ createEntities(numEntities);
+ }
+
+ private void createEntities(int count) {
+ Entity20 e20 = null;
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ for (int i=0; i<count; i++) {
+ e20 = new Entity20(i);
+ em.persist(e20);
+ }
+ em.getTransaction().commit();
+ em.close();
+ }
+
+ /*
+ * Verify that an in-place detached entity does not use the proxy classes.
+ */
+ public void testDetach() {
+ if (log.isTraceEnabled())
+ log.info("***** testDetach() *****");
+ Integer id = new Integer(0);
+ OpenJPAEntityManager em = emf.createEntityManager();
+
+ em.clear();
+ Entity20 e20 = em.find(Entity20.class, id);
+ if (log.isTraceEnabled())
+ log.trace("** after find");
+ assertTrue(em.contains(e20));
+ verifySerializable(e20, true);
+
+ // new openjpa-2.0.0 behavior, where detach() doesn't return updated entity, but does it in-place
+ em.detach(e20);
+ if (log.isTraceEnabled())
+ log.trace("** after detach");
+ // in-place updated entity should not have any proxy classes and should be detached
+ assertTrue(em.isDetached(e20));
+ verifySerializable(e20, false);
+
+ em.close();
+ }
+
+ /*
+ * Verify that a detachCopy() returned entity does not contain any proxy classes.
+ */
+ public void testDetachCopy() {
+ if (log.isTraceEnabled())
+ log.info("***** testDetachCopy() *****");
+ Integer id = new Integer(0);
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.clear();
+
+ Entity20 e20 = em.find(Entity20.class, id);
+ if (log.isTraceEnabled())
+ log.trace("** after find");
+ assertTrue(em.contains(e20));
+ verifySerializable(e20, true);
+
+ // Test new detachCopy() method added in 2.0.0
+ Entity20 e20copy = em.detachCopy(e20);
+ if (log.isTraceEnabled())
+ log.trace("** after detachCopy");
+ assertTrue(em.isDetached(e20copy));
+ verifySerializable(e20copy, false);
+
+ em.close();
+ }
+
+ /*
+ * Verify that in-place detachAll entities do not use the proxy classes.
+ */
+ public void testDetachAll() {
+ if (log.isTraceEnabled())
+ log.info("***** testDetachAll() *****");
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.clear();
+
+ ArrayList<Entity20> e20List = new ArrayList<Entity20>(numEntities);
+ for (int i=0; i<numEntities; i++) {
+ Entity20 e20 = em.find(Entity20.class, new Integer(i));
+ e20List.add(e20);
+ if (log.isTraceEnabled())
+ log.trace("** after find Entity20(" + i + ")");
+ assertTrue(em.contains(e20));
+ verifySerializable(e20, true);
+ }
+
+ // new openjpa-2.0.0 behavior, where detachAll() updates entities in-place
+ // ArrayList<Entity20> e20ListCopy = new ArrayList<Entity20>(em.detachAll(e20List));
+ // em.detachAll(e20List); // for some reason calling with Collection causes a NPE, so use Object[] instead
+ em.detachAll(e20List.get(0), e20List.get(1), e20List.get(2));
+ for (int i=0; i<numEntities; i++) {
+ if (log.isTraceEnabled())
+ log.trace("** after EM.clear() verify Entity20(" + i + ")");
+ Entity20 e20 = e20List.get(i);
+ // entity should not have any proxy classes and should be detached
+ assertTrue(em.isDetached(e20));
+ verifySerializable(e20, false);
+ }
+
+ em.close();
+ }
+
+ /*
+ * Verify that after EM.clear() in-place detached entities do not contain any proxy classes.
+ */
+ public void testClear() {
+ if (log.isTraceEnabled())
+ log.info("***** testClear() *****");
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.clear();
+
+ ArrayList<Entity20> e20List = new ArrayList<Entity20>(numEntities);
+ for (int i=0; i<numEntities; i++) {
+ Entity20 e20 = em.find(Entity20.class, new Integer(i));
+ e20List.add(e20);
+ if (log.isTraceEnabled())
+ log.trace("** after find Entity20(" + i + ")");
+ assertTrue(em.contains(e20));
+ verifySerializable(e20, true);
+ }
+
+ em.clear();
+ for (int i=0; i<numEntities; i++) {
+ if (log.isTraceEnabled())
+ log.trace("** after EM.clear() verify Entity20(" + i + ")");
+ Entity20 e20 = e20List.get(i);
+ assertTrue(em.isDetached(e20));
+ verifySerializable(e20, false);
+ }
+
+ em.close();
+ }
+
+
+ private void verifySerializable(Entity20 e20, boolean usesProxy) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = null;
+ byte[] e20bytes = null;
+
+ if (log.isTraceEnabled())
+ log.trace("verifySerializable() - before serialize");
+ verifyEntities(e20, usesProxy);
+
+ // first serialize
+ try {
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(e20);
+ e20bytes = baos.toByteArray();
+ } catch (IOException e) {
+ fail(e.toString());
+ } finally {
+ try {
+ if (oos != null)
+ oos.close();
+ } catch (IOException e) {
+ }
+ }
+
+ // then deserialize
+ ByteArrayInputStream bais = new ByteArrayInputStream(e20bytes);
+ ObjectInputStream ois = null;
+ Entity20 e20new = null;
+ try {
+ ois = new ObjectInputStream(bais);
+ e20new = (Entity20) ois.readObject();
+ if (log.isTraceEnabled())
+ log.trace("verifySerializable() - after deserialize");
+ verifyEntities(e20new, false);
+ } catch (IOException e) {
+ fail(e.toString());
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ } finally {
+ try {
+ if (ois != null)
+ ois.close();
+ } catch (IOException e) {
+ }
+ }
+
+ }
+
+ private void verifyEntities(Entity20 e20, boolean usesProxy) {
+ if (log.isTraceEnabled()) {
+ printClassNames(e20);
+ log.trace("asserting expected proxy usage");
+ }
+ assertTrue("Expected sqlDate endsWith($proxy) to return " + usesProxy,
+ usesProxy == e20.getDate().getClass().getCanonicalName().endsWith(PROXY));
+ assertTrue("Expected sqlTime endsWith($proxy) to return " + usesProxy,
+ usesProxy == e20.getTime().getClass().getCanonicalName().endsWith(PROXY));
+ assertTrue("Expected sqlTimestamp endsWith($proxy) to return " + usesProxy,
+ usesProxy == e20.getTimestamp().getClass().getCanonicalName().endsWith(PROXY));
+
+ }
+
+ private void printClassNames(Entity20 e20) {
+ log.info("sqlDate = " + e20.getDate().getClass().getCanonicalName());
+ log.info("sqlTime = " + e20.getTime().getClass().getCanonicalName());
+ log.info("sqlTimestamp = " + e20.getTimestamp().getClass().getCanonicalName());
+ }
+
+}
Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detach/TestDetachNoProxy.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java?rev=919696&r1=919695&r2=919696&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/proxy/TestProxyCollection.java Sat Mar 6 03:26:24 2010
@@ -124,10 +124,12 @@
modified.modify(modifier);
em.merge(modified);
em.getTransaction().commit();
+
+ // this was unproxied by EM.clear() in create() below
+ // assertProxyCollection(root.getNodes(), true);
+ assertNotProxyCollection(root.getNodes());
+
em.clear();
-
- assertProxyCollection(root.getNodes(), false);
-
verify(root, modifier);
}
@@ -145,7 +147,11 @@
em.getTransaction().begin();
em.persist(root);
em.getTransaction().commit();
+ // OPENJPA-1097 Fixed behavior so entities will use the proxy classes until EM.clear() is called
+ assertProxyCollection(root.getNodes(), true);
em.clear();
+ // OPENJPA-1097 All proxies are removed after EM.clear()
+ assertNotProxyCollection(root.getNodes());
return root;
}
@@ -181,4 +187,12 @@
assertFalse(tracker.isTracking());
}
}
+
+ /**
+ * Asserts that the given object is NOT a proxy collection.
+ */
+ void assertNotProxyCollection(Object o) {
+ assertFalse(o instanceof ProxyCollection);
+ }
}
+