mirror of https://github.com/apache/openjpa.git
OPENJPA-316
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@595510 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
634bdc5c92
commit
0071f7b0d8
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.openjpa.enhance;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -34,6 +35,7 @@ import org.apache.openjpa.lib.log.Log;
|
|||
import org.apache.openjpa.lib.util.BytecodeWriter;
|
||||
import org.apache.openjpa.lib.util.JavaVersions;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.lib.util.Files;
|
||||
import org.apache.openjpa.lib.util.Localizer.Message;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
|
@ -249,10 +251,14 @@ public class ManagedClassSubclasser {
|
|||
// but do set the metadata accordingly.
|
||||
if (enhancer.isAlreadyRedefined())
|
||||
ints.add(bc.getType());
|
||||
else if (JavaVersions.VERSION >= 5)
|
||||
else if (JavaVersions.VERSION >= 5) {
|
||||
map.put(bc.getType(), bc.toByteArray());
|
||||
debugBytecodes(bc);
|
||||
}
|
||||
} else {
|
||||
if (!enhancer.isAlreadySubclassed()) {
|
||||
debugBytecodes(bc);
|
||||
|
||||
// this is the new subclass
|
||||
ClassLoader loader = GeneratedClasses.getMostDerivedLoader(
|
||||
cls, PersistenceCapable.class);
|
||||
|
@ -261,6 +267,22 @@ public class ManagedClassSubclasser {
|
|||
}
|
||||
}
|
||||
|
||||
private static void debugBytecodes(BCClass bc) throws IOException {
|
||||
// Write the bytecodes to disk for debugging purposes.
|
||||
if ("true".equals(System.getProperty(
|
||||
ManagedClassSubclasser.class.getName() + ".dumpBytecodes")))
|
||||
{
|
||||
File tmp = new File(System.getProperty("java.io.tmpdir"));
|
||||
File dir = new File(tmp, "openjpa");
|
||||
dir = new File(dir, "pcsubclasses");
|
||||
dir.mkdirs();
|
||||
dir = Files.getPackageFile(dir, bc.getPackageName(), true);
|
||||
File f = new File(dir, bc.getClassName() + ".class");
|
||||
System.err.println("Writing to " + f);
|
||||
bc.write(f);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setIntercepting(OpenJPAConfiguration conf,
|
||||
ClassLoader envLoader, Class cls) {
|
||||
ClassMetaData meta = conf.getMetaDataRepositoryInstance()
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* 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.enhance;
|
||||
|
||||
/**
|
||||
* Helper methods for dynamically-redefined managed types.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DynamicPCHelper {
|
||||
public static boolean isDetached(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -18,8 +18,15 @@
|
|||
*/
|
||||
package org.apache.openjpa.enhance;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StateManagerImpl;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.util.ImplHelper;
|
||||
|
||||
/**
|
||||
|
@ -185,4 +192,46 @@ public class RedefinitionHelper {
|
|||
sm.settingObjectField(pc, idx, cur, next,
|
||||
OpenJPAStateManager.SET_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a container instance that will delegate back to the state
|
||||
* manager to emulate lazy loading. This is used by PC subclasses for
|
||||
* unenhanced types that could not be redefined, and thus do not have
|
||||
* field-interception capabilities. Do this for all collection and
|
||||
* map field types, even if they are in the dfg, in case the fetch
|
||||
* groups are reset at runtime.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static void assignLazyLoadProxies(StateManagerImpl sm) {
|
||||
FieldMetaData[] fmds = sm.getMetaData().getFields();
|
||||
for (int i = 0; i < fmds.length; i++) {
|
||||
switch (fmds[i].getTypeCode()) {
|
||||
case JavaTypes.COLLECTION:
|
||||
case JavaTypes.MAP:
|
||||
PersistenceCapable pc = sm.getPersistenceCapable();
|
||||
Field field = (Field) fmds[i].getBackingMember();
|
||||
Reflection.set(pc, field,
|
||||
newLazyLoadingProxy(fmds[i].getDeclaredType(), i, sm));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object newLazyLoadingProxy(Class type, final int idx,
|
||||
final StateManagerImpl sm) {
|
||||
InvocationHandler handler = new InvocationHandler() {
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
// this will replace the field in the instance, so the dynamic
|
||||
// proxy should only be called the first time a
|
||||
// lazy-load-proxied field is used in normal usage.
|
||||
Object delegate = sm.fetch(idx);
|
||||
return method.invoke(delegate, args);
|
||||
}
|
||||
};
|
||||
return Proxy.newProxyInstance(type.getClassLoader(),
|
||||
new Class[] { type }, handler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,16 +21,25 @@ package org.apache.openjpa.kernel;
|
|||
import java.io.IOException;
|
||||
import java.io.ObjectOutput;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Calendar;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.enhance.DynamicPersistenceCapable;
|
||||
import org.apache.openjpa.enhance.FieldManager;
|
||||
import org.apache.openjpa.enhance.ManagedInstanceProvider;
|
||||
import org.apache.openjpa.enhance.PCRegistry;
|
||||
import org.apache.openjpa.enhance.PersistenceCapable;
|
||||
import org.apache.openjpa.enhance.RedefinitionHelper;
|
||||
import org.apache.openjpa.enhance.StateManager;
|
||||
import org.apache.openjpa.enhance.ManagedInstanceProvider;
|
||||
import org.apache.openjpa.enhance.DynamicPersistenceCapable;
|
||||
import org.apache.openjpa.event.LifecycleEvent;
|
||||
import org.apache.openjpa.event.LifecycleEventManager;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
|
@ -43,6 +52,7 @@ import org.apache.openjpa.meta.ValueMetaData;
|
|||
import org.apache.openjpa.meta.ValueStrategies;
|
||||
import org.apache.openjpa.util.ApplicationIds;
|
||||
import org.apache.openjpa.util.Exceptions;
|
||||
import org.apache.openjpa.util.ImplHelper;
|
||||
import org.apache.openjpa.util.InternalException;
|
||||
import org.apache.openjpa.util.InvalidStateException;
|
||||
import org.apache.openjpa.util.ObjectNotFoundException;
|
||||
|
@ -50,7 +60,6 @@ import org.apache.openjpa.util.OpenJPAId;
|
|||
import org.apache.openjpa.util.ProxyManager;
|
||||
import org.apache.openjpa.util.RuntimeExceptionTranslator;
|
||||
import org.apache.openjpa.util.UserException;
|
||||
import org.apache.openjpa.util.ImplHelper;
|
||||
import serp.util.Numbers;
|
||||
|
||||
/**
|
||||
|
@ -306,8 +315,11 @@ public class StateManagerImpl
|
|||
|
||||
// if this is a non-tracking PC, add a hard ref to the appropriate data
|
||||
// sets and give it an opportunity to make a state snapshot.
|
||||
if (!isIntercepting())
|
||||
if (!isIntercepting()) {
|
||||
saveFields(true);
|
||||
if (!isNew())
|
||||
RedefinitionHelper.assignLazyLoadProxies(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,6 +327,8 @@ public class StateManagerImpl
|
|||
* from {@link ClassMetaData#isIntercepting()} in that it checks for
|
||||
* property access + subclassing in addition to the redefinition /
|
||||
* enhancement checks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public boolean isIntercepting() {
|
||||
if (getMetaData().isIntercepting())
|
||||
|
|
|
@ -398,6 +398,7 @@
|
|||
<fileset id="enhance.path.ref"
|
||||
dir="${project.build.testOutputDirectory}">
|
||||
<include name="**/*.class"/>
|
||||
<exclude name="**/Test*.class"/>
|
||||
<exclude name="**/inheritance/serializable/*.class"/>
|
||||
<exclude name="**/xml/*.class"/>
|
||||
<exclude name="**/Unenhanced*.class"/>
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.enhance;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.openjpa.persistence.test.SingleEMTestCase;
|
||||
|
||||
public class TestUnenhancedOneToMany extends SingleEMTestCase {
|
||||
|
||||
public void setUp() {
|
||||
setUp(UnenhancedOne.class, UnenhancedMany.class, CLEAR_TABLES);
|
||||
}
|
||||
|
||||
public void testOneToMany() throws Exception {
|
||||
assertFalse(PersistenceCapable.class.isAssignableFrom(
|
||||
UnenhancedOne.class));
|
||||
assertFalse(PersistenceCapable.class.isAssignableFrom(
|
||||
UnenhancedMany.class));
|
||||
|
||||
em.getTransaction().begin();
|
||||
|
||||
UnenhancedOne one = new UnenhancedOne(1000);
|
||||
|
||||
UnenhancedMany manyA = new UnenhancedMany(1);
|
||||
one.getMany().add(manyA);
|
||||
manyA.setOne(one);
|
||||
|
||||
UnenhancedMany manyB = new UnenhancedMany(2);
|
||||
one.getMany().add(manyB);
|
||||
manyB.setOne(one);
|
||||
|
||||
UnenhancedMany manyC = new UnenhancedMany(3);
|
||||
one.getMany().add(manyC);
|
||||
manyC.setOne(one);
|
||||
|
||||
// em should not know about our entities
|
||||
assertFalse(em.contains(one));
|
||||
assertFalse(em.contains(manyA));
|
||||
assertFalse(em.contains(manyB));
|
||||
assertFalse(em.contains(manyC));
|
||||
|
||||
// persist the entity
|
||||
em.persist(one);
|
||||
em.persist(manyA);
|
||||
em.persist(manyB);
|
||||
em.persist(manyC);
|
||||
em.flush();
|
||||
|
||||
// em should now be aware of our entity
|
||||
assertTrue(em.contains(one));
|
||||
assertTrue(em.contains(manyA));
|
||||
assertTrue(em.contains(manyB));
|
||||
assertTrue(em.contains(manyC));
|
||||
|
||||
em.getTransaction().commit();
|
||||
|
||||
// recreate entity manager to avoid caching
|
||||
one = null;
|
||||
manyA = null;
|
||||
manyB = null;
|
||||
manyC = null;
|
||||
em.close();
|
||||
em = emf.createEntityManager();
|
||||
em.getTransaction().begin();
|
||||
|
||||
// reload one
|
||||
one = em.find(UnenhancedOne.class, 1000);
|
||||
assertNotNull("one is null", one);
|
||||
|
||||
// verify one.getMany(); ensure that it's a dynamic proxy before
|
||||
// it is accessed
|
||||
assertTrue(Proxy.isProxyClass(one.many.getClass()));
|
||||
assertNotNull("one.getMany() is null", one.getMany());
|
||||
Collection<UnenhancedMany> many = one.getMany();
|
||||
assertEquals(3, many.size());
|
||||
|
||||
// reload the many
|
||||
manyA = em.find(UnenhancedMany.class, 1);
|
||||
assertNotNull("manyA is null", manyA);
|
||||
manyB = em.find(UnenhancedMany.class, 2);
|
||||
assertNotNull("manyB is null", manyA);
|
||||
manyC = em.find(UnenhancedMany.class, 3);
|
||||
assertNotNull("manyc is null", manyA);
|
||||
|
||||
// verify many.getOne()
|
||||
assertNotNull("manyA.getOne() is null", manyA.getOne());
|
||||
assertEquals(one, manyA.getOne());
|
||||
assertNotNull("manyB.getOne() is null", manyB.getOne());
|
||||
assertEquals(one, manyB.getOne());
|
||||
assertNotNull("manyC.getOne() is null", manyC.getOne());
|
||||
assertEquals(one, manyC.getOne());
|
||||
|
||||
// verify collection contains each many
|
||||
assertTrue(many.contains(manyA));
|
||||
assertTrue(many.contains(manyB));
|
||||
assertTrue(many.contains(manyC));
|
||||
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
*
|
||||
* 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.enhance;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Entity
|
||||
public class UnenhancedMany implements Serializable, Cloneable {
|
||||
private static final long serialVersionUID = 4041356744771116705L;
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
@ManyToOne
|
||||
private UnenhancedOne one;
|
||||
|
||||
public UnenhancedMany() {
|
||||
}
|
||||
|
||||
public UnenhancedMany(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public UnenhancedOne getOne() {
|
||||
return one;
|
||||
}
|
||||
|
||||
public void setOne(UnenhancedOne one) {
|
||||
this.one = one;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (o == null) return false;
|
||||
if (!getClass().isAssignableFrom(o.getClass())) return false;
|
||||
|
||||
return id == ((UnenhancedMany) o).id;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
*
|
||||
* 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.enhance;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
@Entity
|
||||
public class UnenhancedOne implements Serializable, Cloneable {
|
||||
private static final long serialVersionUID = -5834998517804641711L;
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
@OneToMany
|
||||
Collection<UnenhancedMany> many = new HashSet<UnenhancedMany>();
|
||||
|
||||
public UnenhancedOne() {
|
||||
}
|
||||
|
||||
public UnenhancedOne(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Collection<UnenhancedMany> getMany() {
|
||||
return many;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (o == null) return false;
|
||||
if (!getClass().isAssignableFrom(o.getClass())) return false;
|
||||
|
||||
return id == ((UnenhancedOne) o).id;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue