OPENJPA-297

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@560304 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-07-27 16:26:52 +00:00
parent 747226b1d6
commit 8c1710ed63
8 changed files with 159 additions and 32 deletions

View File

@ -35,6 +35,8 @@ import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.util.GeneratedClasses; import org.apache.openjpa.util.GeneratedClasses;
import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import serp.bytecode.BCClass; import serp.bytecode.BCClass;
/** /**
@ -74,7 +76,8 @@ public class ManagedClassSubclasser {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
Log log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE); Log log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
if (ClassRedefiner.canRedefineClasses()) boolean redefine = ClassRedefiner.canRedefineClasses();
if (redefine)
log.info(_loc.get("enhance-and-subclass-no-redef-start", log.info(_loc.get("enhance-and-subclass-no-redef-start",
classes)); classes));
else else
@ -88,21 +91,24 @@ public class ManagedClassSubclasser {
final Class cls = (Class) iter.next(); final Class cls = (Class) iter.next();
final PCEnhancer enhancer = new PCEnhancer(conf, cls); final PCEnhancer enhancer = new PCEnhancer(conf, cls);
// set this before enhancement as well as after since enhancement
// uses a different metadata repository, and the value of this
// setting matters in the enhancement contract.
setDetachedState(enhancer.getMetaData());
enhancer.setBytecodeWriter(new BytecodeWriter() { enhancer.setBytecodeWriter(new BytecodeWriter() {
public void write(BCClass bc) throws IOException { public void write(BCClass bc) throws IOException {
ManagedClassSubclasser.write(bc, enhancer, map, ManagedClassSubclasser.write(bc, enhancer, map,
cls, subs, ints); cls, subs, ints);
} }
}); });
if (ClassRedefiner.canRedefineClasses()) if (redefine)
enhancer.setRedefine(true); enhancer.setRedefine(true);
enhancer.setCreateSubclass(true); enhancer.setCreateSubclass(true);
enhancer.setAddDefaultConstructor(true); enhancer.setAddDefaultConstructor(true);
// set this before enhancement as well as after since enhancement
// uses a different metadata repository, and the metadata config
// matters in the enhancement contract. Don't do any warning here,
// since we'll issue warnings when we do the final metadata
// reconfiguration at the end of this method.
configureMetaData(enhancer.getMetaData(), conf, redefine, false);
enhancer.run(); enhancer.run();
try { try {
enhancer.record(); enhancer.record();
@ -115,16 +121,54 @@ public class ManagedClassSubclasser {
ClassRedefiner.redefineClasses(conf, map); ClassRedefiner.redefineClasses(conf, map);
for (Class cls : map.keySet()) { for (Class cls : map.keySet()) {
setIntercepting(conf, envLoader, cls); setIntercepting(conf, envLoader, cls);
configureMetaData(conf, envLoader, cls); configureMetaData(conf, envLoader, cls, redefine);
} }
for (Class cls : (Collection<Class>) subs) for (Class cls : (Collection<Class>) subs)
configureMetaData(conf, envLoader, cls); configureMetaData(conf, envLoader, cls, redefine);
for (Class cls : (Collection<Class>) ints) for (Class cls : (Collection<Class>) ints)
setIntercepting(conf, envLoader, cls); setIntercepting(conf, envLoader, cls);
return subs; return subs;
} }
private static void configureMetaData(OpenJPAConfiguration conf,
ClassLoader envLoader, Class cls, boolean redefineAvailable) {
ClassMetaData meta = conf.getMetaDataRepositoryInstance()
.getMetaData(cls, envLoader, true);
configureMetaData(meta, conf, redefineAvailable, true);
}
private static void configureMetaData(ClassMetaData meta,
OpenJPAConfiguration conf, boolean redefineAvailable, boolean warn) {
setDetachedState(meta);
if (warn && meta.getAccessType() == ClassMetaData.ACCESS_FIELD
&& !redefineAvailable) {
// only warn about declared fields; superclass fields will be
// warned about when the superclass is handled
for (FieldMetaData fmd : meta.getDeclaredFields()) {
switch (fmd.getTypeCode()) {
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
// we can lazily load these, since we own the
// relationship container
break;
default:
if (!fmd.isInDefaultFetchGroup()
&& !(fmd.isVersion() || fmd.isPrimaryKey())) {
Log log = conf.getLog(
OpenJPAConfiguration.LOG_ENHANCE);
log.warn(_loc.get("subclasser-fetch-group-override",
meta.getDescribedType().getName(),
fmd.getName()));
fmd.setInDefaultFetchGroup(true);
}
}
}
}
}
private static void write(BCClass bc, PCEnhancer enhancer, private static void write(BCClass bc, PCEnhancer enhancer,
Map<Class, byte[]> map, Class cls, List subs, List ints) Map<Class, byte[]> map, Class cls, List subs, List ints)
throws IOException { throws IOException {
@ -158,13 +202,6 @@ public class ManagedClassSubclasser {
meta.setIntercepting(true); meta.setIntercepting(true);
} }
private static void configureMetaData(OpenJPAConfiguration conf,
ClassLoader envLoader, Class cls) {
ClassMetaData meta = conf.getMetaDataRepositoryInstance()
.getMetaData(cls, envLoader, true);
setDetachedState(meta);
}
/** /**
* If the metadata is configured to use a synthetic * If the metadata is configured to use a synthetic
* detached state, reset it to not use a detached * detached state, reset it to not use a detached

View File

@ -187,3 +187,7 @@ subclasser-native-methods-not-allowed: The method {1} in type {0} is native. \
OpenJPA requires methods in unenhanced instances to be non-native. OpenJPA requires methods in unenhanced instances to be non-native.
subclasser-static-methods-not-supported: The method {1} in type {0} is static. \ subclasser-static-methods-not-supported: The method {1} in type {0} is static. \
OpenJPA requires methods in unenhanced instances to be non-static. OpenJPA requires methods in unenhanced instances to be non-static.
subclasser-fetch-group-override: The field {1} in type {0} is configured to be \
lazily loaded, but lazy loading is not available for classes that use field\
access when not running the OpenJPA enhancer or when dynamic class \
redefinition is not available.

View File

@ -39,7 +39,6 @@ public abstract class AbstractUnenhancedClassTest
// ##### To do: // ##### To do:
// - clearing in pnew property-access without redefinition // - clearing in pnew property-access without redefinition
// - lazy loading override for -to-one field types
// - figure out how to auto-test the redefinition code, either in Java 5 // - figure out how to auto-test the redefinition code, either in Java 5
// or in Java 6 // or in Java 6
// - run CTS in the following combinations: // - run CTS in the following combinations:
@ -48,8 +47,7 @@ public abstract class AbstractUnenhancedClassTest
// * Java 5 without javaagent // * Java 5 without javaagent
public void setUp() { public void setUp() {
setUp(getUnenhancedClass(), getUnenhancedSubclass(), CLEAR_TABLES, setUp(getUnenhancedClass(), getUnenhancedSubclass(), CLEAR_TABLES);
"openjpa.Log", "Enhance=TRACE");
// trigger class redefinition // trigger class redefinition
emf.createEntityManager().close(); emf.createEntityManager().close();
} }
@ -237,17 +235,17 @@ public abstract class AbstractUnenhancedClassTest
PCEnhancer.toPCSubclassName(getUnenhancedClass())); PCEnhancer.toPCSubclassName(getUnenhancedClass()));
} }
public void testLazyLoadingInUserCreatedInstance() public void testEvictionInUserCreatedInstance()
throws NoSuchFieldException, IllegalAccessException { throws NoSuchFieldException, IllegalAccessException {
lazyLoading(true); evictionHelper(true);
} }
public void testLazyLoadingInOpenJPACreatedInstance() public void testEvictionInOpenJPACreatedInstance()
throws NoSuchFieldException, IllegalAccessException { throws NoSuchFieldException, IllegalAccessException {
lazyLoading(false); evictionHelper(false);
} }
private void lazyLoading(boolean userDefined) private void evictionHelper(boolean userDefined)
throws NoSuchFieldException, IllegalAccessException { throws NoSuchFieldException, IllegalAccessException {
OpenJPAEntityManager em = emf.createEntityManager(); OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newUnenhancedInstance(); UnenhancedType un = newUnenhancedInstance();
@ -303,6 +301,52 @@ public abstract class AbstractUnenhancedClassTest
em.close(); em.close();
} }
public void testLazyLoading()
throws NoSuchFieldException, IllegalAccessException {
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newUnenhancedInstance();
em.getTransaction().begin();
em.persist(un);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
un = em.find(getUnenhancedClass(), un.getId());
assertTrue(getUnenhancedClass() != un.getClass());
OpenJPAStateManager sm = (OpenJPAStateManager)
ImplHelper.toPersistenceCapable(un, null).pcGetStateManager();
// we only expect lazy loading to work when we can redefine classes
// or when accessing a property-access record that OpenJPA created.
if (ClassRedefiner.canRedefineClasses()
|| (sm.getMetaData().getAccessType() != ClassMetaData.ACCESS_FIELD))
{
assertFalse(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
// make sure that the value was cleared
Field field = getUnenhancedClass().getDeclaredField("lazyField");
field.setAccessible(true);
assertEquals(null, field.get(un));
} else {
// unredefined field access
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
// make sure that the value was loaded already
Field field = getUnenhancedClass().getDeclaredField("lazyField");
field.setAccessible(true);
assertEquals("lazy", field.get(un));
}
// make sure that the value is available, one way or another
assertEquals("lazy", un.getLazyField());
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
em.close();
}
public void testSerializationOfUserDefinedInstance() public void testSerializationOfUserDefinedInstance()
throws IOException, ClassNotFoundException { throws IOException, ClassNotFoundException {
serializationHelper(true, false); serializationHelper(true, false);

View File

@ -26,6 +26,8 @@ import javax.persistence.GeneratedValue;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Basic;
import javax.persistence.FetchType;
import org.apache.openjpa.persistence.DetachedState; import org.apache.openjpa.persistence.DetachedState;
@ -39,6 +41,9 @@ public class UnenhancedFieldAccess
@Version public int version; @Version public int version;
protected String stringField = "foo"; protected String stringField = "foo";
@Basic(fetch = FetchType.LAZY)
private String lazyField = "lazy";
public int getId() { public int getId() {
return id; return id;
} }
@ -51,6 +56,10 @@ public class UnenhancedFieldAccess
return stringField; return stringField;
} }
public String getLazyField() {
return lazyField;
}
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) if (o == this)
return true; return true;

View File

@ -27,8 +27,7 @@ import javax.persistence.Basic;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.FetchType;
import org.apache.openjpa.persistence.DetachedState;
@Entity @Entity
@Table(name="UN_PROP") @Table(name="UN_PROP")
@ -39,6 +38,7 @@ public class UnenhancedPropertyAccess
private int id; private int id;
private int version; private int version;
private String sf = "foo"; private String sf = "foo";
private String lazyField = "lazy";
@Id @GeneratedValue @Id @GeneratedValue
public int getId() { public int getId() {
@ -58,13 +58,22 @@ public class UnenhancedPropertyAccess
version = v; version = v;
} }
@Basic
public String getStringField() {
return sf;
}
public void setStringField(String s) { public void setStringField(String s) {
sf = s; sf = s;
} }
@Basic @Basic(fetch = FetchType.LAZY)
public String getStringField() { public String getLazyField() {
return sf; return lazyField;
}
public void setLazyField(String s) {
lazyField = s;
} }
public boolean equals(Object o) { public boolean equals(Object o) {

View File

@ -23,9 +23,12 @@ package org.apache.openjpa.enhance;
*/ */
public interface UnenhancedType { public interface UnenhancedType {
int getId();
void setStringField(String s); void setStringField(String s);
String getStringField(); String getStringField();
int getId();
String getLazyField();
Object clone() throws CloneNotSupportedException; Object clone() throws CloneNotSupportedException;
} }

View File

@ -50,6 +50,7 @@ import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.meta.AbstractMetaDataDefaults; import org.apache.openjpa.meta.AbstractMetaDataDefaults;
import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.FieldMetaData;
@ -57,6 +58,7 @@ import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData; import org.apache.openjpa.meta.ValueMetaData;
import static org.apache.openjpa.persistence.PersistenceStrategy.*; import static org.apache.openjpa.persistence.PersistenceStrategy.*;
import org.apache.openjpa.util.MetaDataException; import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.conf.OpenJPAConfiguration;
/** /**
* JPA-based metadata defaults. * JPA-based metadata defaults.
@ -294,10 +296,13 @@ public class PersistenceMetaDataDefaults
meta.getDescribedType(), "set" + meta.getDescribedType(), "set" +
StringUtils.capitalize(name), new Class[] { StringUtils.capitalize(name), new Class[] {
((Method) member).getReturnType() })); ((Method) member).getReturnType() }));
if (setter == null) if (setter == null) {
logNoSetter(meta, name, null);
return false; return false;
}
} catch (Exception e) { } catch (Exception e) {
// e.g., NoSuchMethodException // e.g., NoSuchMethodException
logNoSetter(meta, name, e);
return false; return false;
} }
} }
@ -307,4 +312,16 @@ public class PersistenceMetaDataDefaults
return false; return false;
return true; return true;
} }
private void logNoSetter(ClassMetaData meta, String name, Exception e) {
Log log = meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_METADATA);
if (log.isWarnEnabled())
log.warn(_loc.get("no-setter-for-getter", name,
meta.getDescribedType().getName()));
else if (log.isTraceEnabled())
// log the exception, if any, if we're in trace-level debugging
log.warn(_loc.get("no-setter-for-getter", name,
meta.getDescribedType().getName()), e);
}
} }

View File

@ -121,6 +121,10 @@ map-persistent-types-skipping-non-url: Skipping persistent type location \
map-persistent-types-skipping-class: Skipping persistent type location \ map-persistent-types-skipping-class: Skipping persistent type location \
association for location "{0}" since it is a class, and will not \ association for location "{0}" since it is a class, and will not \
need to be re-parsed later. need to be re-parsed later.
no-setter-for-getter: No setter was found for method {0} in type {1} while \
searching for persistent properties. This method will be ignored. If you \
intended for this to be persistent, please add a corresponding setter, \
or switch to field access for this type hierarchy.
EntityManagerFactory-name: EntityManagerFactory implementation EntityManagerFactory-name: EntityManagerFactory implementation
EntityManagerFactory-desc: Allows extension of standard \ EntityManagerFactory-desc: Allows extension of standard \