diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java index 485e8b09d..a2f527737 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java @@ -176,7 +176,9 @@ public class PCEnhancer { private boolean _bcsConfigured = false; /** - * Constructor. Supply configuration and type to enhance. + * Constructor. Supply configuration and type to enhance. This will look + * up the metadata for type from conf's + * repository. */ public PCEnhancer(OpenJPAConfiguration conf, Class type) { this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper @@ -185,12 +187,14 @@ public class PCEnhancer { } /** - * Constructor. Supply configuration and type to enhance. + * Constructor. Supply configuration and type to enhance. This will look + * up the metadata for meta by converting back to a class + * and then loading from conf's repository. */ - public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData type) { + public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) { this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper - .loadProjectClassAction(new Project(), type.getDescribedType())), - type.getRepository()); + .loadProjectClassAction(new Project(), meta.getDescribedType())), + meta.getRepository()); } /** @@ -241,6 +245,36 @@ public class PCEnhancer { _meta = _repos.getMetaData(type.getType(), loader, false); } + /** + * Constructor. Supply repository. The repository's configuration will + * be used, and the metadata passed in will be used as-is without doing + * any additional lookups. This is useful when running the enhancer + * during metadata load. + * + * @param repos a metadata repository to use for metadata access, + * or null to create a new reporitory; the repository + * from the given configuration isn't used by default + * because the configuration might be an + * implementation-specific subclass whose metadata + * required more than just base metadata files + * @param type the bytecode representation fo the type to + * enhance; this can be created from any stream or file + * @param meta the metadata to use for processing this type. + * + * @since 1.1.0 + */ + public PCEnhancer(MetaDataRepository repos, BCClass type, + ClassMetaData meta) { + _managedType = type; + _pc = type; + + _log = repos.getConfiguration() + .getLog(OpenJPAConfiguration.LOG_ENHANCE); + + _repos = repos; + _meta = meta; + } + static String toPCSubclassName(Class cls) { return Strings.getPackageName(PCEnhancer.class) + "." + cls.getName().replace('.', '$') + "$pcsubclass"; @@ -453,7 +487,7 @@ public class PCEnhancer { try { // if managed interface, skip - if (_managedType.isInterface()) + if (_pc.isInterface()) return ENHANCE_INTERFACE; // check if already enhanced @@ -526,8 +560,6 @@ public class PCEnhancer { } else { _isAlreadySubclassed = true; } - } else { - _pc = _managedType; } _bcsConfigured = true; @@ -2656,8 +2688,10 @@ public class PCEnhancer { } // pcPCSuperclass = ; - code.classconstant().setClass(getType(_meta. - getPCSuperclassMetaData())); + // this intentionally calls getDescribedType() directly + // instead of PCEnhancer.getType() + code.classconstant().setClass( + _meta.getPCSuperclassMetaData().getDescribedType()); code.putstatic().setField(SUPER, Class.class); } @@ -2698,7 +2732,7 @@ public class PCEnhancer { // PCRegistry.register (cls, // pcFieldNames, pcFieldTypes, pcFieldFlags, // pcPCSuperclass, alias, new XXX ()); - code.classconstant().setClass(_managedType); + code.classconstant().setClass(_meta.getDescribedType()); code.getstatic().setField(PRE + "FieldNames", String[].class); code.getstatic().setField(PRE + "FieldTypes", Class[].class); code.getstatic().setField(PRE + "FieldFlags", byte[].class); @@ -3627,19 +3661,19 @@ public class PCEnhancer { // first, see if we can convert the attribute name to a field name String fieldName = toBackingFieldName(attrName); - // next, find the field in the managed type. - BCField[] fields = (BCField[]) AccessController - .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(_managedType, - fieldName)); + // next, find the field in the managed type hierarchy BCField field = null; - for (int i = 0; i < fields.length; i++) { - field = fields[i]; - // if we reach a field declared in this type, then this is the - // most-masking field, and is the one that we want. - // ##### probably should walk up the hierarchy, or check that - // ##### serp does that. - if (fields[i].getDeclarer() == declarer) { - break; + outer: for (BCClass bc = _pc; bc != null; bc = bc.getSuperclassBC()) { + BCField[] fields = (BCField[]) AccessController + .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(bc, + fieldName)); + for (int i = 0; i < fields.length; i++) { + field = fields[i]; + // if we reach a field declared in this type, then this is the + // most-masking field, and is the one that we want. + if (fields[i].getDeclarer() == declarer) { + break outer; + } } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java index d69efafec..454e16efb 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java @@ -28,6 +28,7 @@ import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.ReferenceMap; import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; import org.apache.openjpa.util.UserException; +import org.apache.openjpa.util.InvalidStateException; /** * Tracks registered persistence-capable classes. diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java index 339220863..e945eba5a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java @@ -557,7 +557,11 @@ public interface Broker * an interface or an abstract class whose abstract methods follow the * JavaBeans convention, this method will create a concrete implementation * according to the metadata that defines the class. - * Otherwise, this will return an instance of the specified class. + * Otherwise, if cls is a managed type, this will return an + * instance of the specified class. + * + * @throws IllegalArgumentException if cls is not a managed + * type or interface. */ public Object newInstance(Class cls); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index 2cf9b8915..65af58107 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -2641,8 +2641,6 @@ public class BrokerImpl PersistenceCapable copy; PCState state; Class type = meta.getDescribedType(); - if (type.isInterface()) - type = meta.getInterfaceImpl(); if (obj != null) { // give copy and the original instance the same state manager // so that we can copy fields from one to the other @@ -4152,11 +4150,7 @@ public class BrokerImpl public Object newInstance(Class cls) { assertOpen(); - if (cls.isInterface()) { - ClassMetaData meta = _conf.getMetaDataRepositoryInstance(). - getMetaData(cls, _loader, true); - cls = meta.getInterfaceImpl(); - } else if (Modifier.isAbstract(cls.getModifiers())) + if (!cls.isInterface() && Modifier.isAbstract(cls.getModifiers())) throw new UnsupportedOperationException(_loc.get ("new-abstract", cls).getMessage()); @@ -4169,7 +4163,14 @@ public class BrokerImpl } catch (Throwable t) { } } - return PCRegistry.newInstance(cls, null, false); + try { + return PCRegistry.newInstance(cls, null, false); + } catch (IllegalStateException ise) { + IllegalArgumentException iae = + new IllegalArgumentException(ise.getMessage()); + iae.setStackTrace(ise.getStackTrace()); + throw iae; + } } public Object getObjectId(Object obj) { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index c11fa18f6..eb33507a7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -260,8 +260,6 @@ public class StateManagerImpl } _meta = sub; } - if (cls.isInterface()) - cls = _meta.getInterfaceImpl(); PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true); if (inst == null) { @@ -814,7 +812,8 @@ public class StateManagerImpl SaveFieldManager saved = getSaveFieldManager(); if (saved == null) - throw new InternalException(_loc.get("no-saved-fields")); + throw new InternalException(_loc.get("no-saved-fields", + getMetaData().getDescribedType().getName())); FieldMetaData[] fmds = getMetaData().getFields(); for (int i = 0; i < fmds.length; i++) { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java index 22396bde1..762eee919 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java @@ -180,6 +180,8 @@ public abstract class AbstractMetaDataDefaults */ private boolean populateFromPCRegistry(ClassMetaData meta) { Class cls = meta.getDescribedType(); + if (!PCRegistry.isRegistered(cls)) + return false; try { String[] fieldNames = PCRegistry.getFieldNames(cls); Class[] fieldTypes = PCRegistry.getFieldTypes(cls); @@ -198,9 +200,6 @@ public abstract class AbstractMetaDataDefaults populate(fmd); } return true; - } catch (IllegalStateException iae) { - // thrown by registry when no metadata available - return false; } catch (OpenJPAException ke) { throw ke; } catch (Exception e) { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java index b746af8c0..43c097b8d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java @@ -732,6 +732,11 @@ public class ClassMetaData if (!_type.isInterface()) throw new MetaDataException(_loc.get("not-interface", _type)); _interface = managedInterface ? Boolean.TRUE : Boolean.FALSE; + + // managed interfaces always do proper interception; OpenJPA generates + // the implementations. + if (isManagedInterface()) + setIntercepting(true); } /** @@ -2235,6 +2240,7 @@ public class ClassMetaData _extent = (meta.getRequiresExtent()) ? Boolean.TRUE : Boolean.FALSE; _embedded = (meta.isEmbeddedOnly()) ? Boolean.TRUE : Boolean.FALSE; _interface = (meta.isManagedInterface()) ? Boolean.TRUE : Boolean.FALSE; + setIntercepting(meta.isIntercepting()); _impl = meta.getInterfaceImpl(); _identity = meta.getIdentityType(); _idStrategy = meta.getIdentityStrategy(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java index e67ef031b..5f6fdd2b0 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java @@ -114,14 +114,13 @@ class InterfaceImplGenerator { // copy the BCClass into the enhancer project. bc = _enhProject.loadClass(new ByteArrayInputStream(bc.toByteArray()), loader); - PCEnhancer enhancer = new PCEnhancer(_repos.getConfiguration(), bc, - meta); + PCEnhancer enhancer = new PCEnhancer(_repos, bc, meta); int result = enhancer.run(); if (result != PCEnhancer.ENHANCE_PC) throw new InternalException(_loc.get("interface-badenhance", iface)).setFatal(true); - try{ + try { // load the class for real. impl = Class.forName(bc.getName(), true, enhLoader); } catch (Throwable t) { @@ -229,4 +228,18 @@ class InterfaceImplGenerator { } catch (PrivilegedActionException pae) {} return true; } + + boolean isImplType(Class cls) { + return (cls.getName().endsWith(POSTFIX) + && cls.getName().indexOf('$') != -1); + } + + public Class toManagedInterface(Class cls) { + Class[] ifaces = cls.getInterfaces(); + for (int i = 0; i < ifaces.length; i++) { + if (_impls.get(ifaces[i]) == cls) + return ifaces[i]; + } + throw new IllegalArgumentException(cls.getName()); + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java index 80938fb64..9cd2f0e2d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java @@ -287,6 +287,11 @@ public class MetaDataRepository DynamicPersistenceCapable.class.isAssignableFrom(cls)) cls = cls.getSuperclass(); + // if cls is a generated interface, use the user interface + // to locate metadata + if (cls != null && _implGen.isImplType(cls)) + cls = _implGen.toManagedInterface(cls); + ClassMetaData meta = getMetaDataInternal(cls, envLoader); if (meta == null && mustExist) { if (cls != null && @@ -952,7 +957,6 @@ public class MetaDataRepository throw new MetaDataException(_loc.get("not-managed-interface", meta, impl)); _ifaces.put(meta.getDescribedType(), impl); - _metas.put(impl, meta); addDeclaredInterfaceImpl(meta, meta.getDescribedType()); ClassMetaData sup = meta.getPCSuperclassMetaData(); while (sup != null) { @@ -964,9 +968,7 @@ public class MetaDataRepository } } - synchronized InterfaceImplGenerator getImplGenerator() { - if (_implGen == null) - _implGen = new InterfaceImplGenerator(this); + InterfaceImplGenerator getImplGenerator() { return _implGen; } @@ -1278,6 +1280,11 @@ public class MetaDataRepository cls = classForName((String) itr.next(), clsLoader); if (cls != null) classes.add(cls); + + // if the class is an interface, load its metadata to kick + // off the impl generator + if (cls.isInterface()) + getMetaData(cls, clsLoader, false); } return classes; } @@ -1548,6 +1555,8 @@ public class MetaDataRepository public void endConfiguration() { initializeMetaDataFactory(); + if (_implGen == null) + _implGen = new InterfaceImplGenerator(this); } private void initializeMetaDataFactory() { diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java index b0314014d..de67d40ae 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java @@ -301,9 +301,7 @@ public class ApplicationIds { // oid instance if (!Modifier.isAbstract(meta.getDescribedType().getModifiers()) && !hasPCPrimaryKeyFields(meta)) { - Class type = meta.getInterfaceImpl(); - if (type == null) - type = meta.getDescribedType(); + Class type = meta.getDescribedType(); PersistenceCapable pc = PCRegistry.newInstance(type, null, oid, false); Object copy = pc.pcNewObjectIdInstance(); diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties index 1cc80eecb..55970dfc0 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties @@ -388,11 +388,11 @@ multi-threaded-access: Multiple concurrent threads attempted to access a \ single broker. By default brokers are not thread safe; if you require \ and/or intend a broker to be accessed by more than one thread, set the \ openjpa.Multithreaded property to true to override the default behavior. -no-saved-fields: No state snapshot is available for "{0}", but this instance \ - uses state-comparison for dirty detection. +no-saved-fields: No state snapshot is available for instance of type "{0}", \ + but this instance uses state-comparison for dirty detection. cant-serialize-flushed-broker: Serialization not allowed once a broker has \ been flushed. cant-serialize-pessimistic-broker: Serialization not allowed for brokers with \ an active datastore (pessimistic) transaction. cant-serialize-connected-broker: Serialization not allowed for brokers with \ - an active connection to the database. \ No newline at end of file + an active connection to the database. diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java index bcf01c97a..d95680c4d 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java @@ -40,6 +40,9 @@ public class EmbedValue { @JoinColumn(name = "EMB_REL") protected EmbedOwner owner; + @Transient + private int transientField; + public void setBasic(String basic) { this.basic = basic; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java index b3fff4dc8..8e664029d 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java @@ -29,6 +29,9 @@ import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedClobFieldStrategy; import org.apache.openjpa.jdbc.meta.strats.StringFieldStrategy; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.JPAFacadeHelper; +import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.meta.FieldMetaData; /** * Test for embedded @@ -78,6 +81,15 @@ public class TestEJBEmbedded extends SingleEMFTestCase { em.close(); } + public void testEmbeddedMetaData() { + ClassMetaData ownerMeta = + JPAFacadeHelper.getMetaData(emf, EmbedOwner.class); + FieldMetaData fmd = ownerMeta.getField("embed"); + ClassMetaData embeddedMeta = fmd.getDefiningMetaData(); + assertNotNull(embeddedMeta); + assertNull(embeddedMeta.getField("transientField")); + } + public void testNull() { EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java new file mode 100644 index 000000000..b41b7f926 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java @@ -0,0 +1,47 @@ +package org.apache.openjpa.persistence.managedinterface; + + +import java.util.*; + +import javax.persistence.Entity; +import javax.persistence.Embedded; +import javax.persistence.OneToOne; +import javax.persistence.OneToMany; +import javax.persistence.CascadeType; + +import org.apache.openjpa.persistence.PersistentCollection; +import org.apache.openjpa.persistence.ManagedInterface; +import org.apache.openjpa.persistence.query.SimpleEntity; + +@ManagedInterface +@Entity +public interface ManagedIface extends ManagedInterfaceSup { + public int getIntField(); + public void setIntField(int i); + + @Embedded + public ManagedInterfaceEmbed getEmbed(); + public void setEmbed(ManagedInterfaceEmbed embed); + + @OneToOne(cascade=CascadeType.PERSIST) + public ManagedIface getSelf(); + public void setSelf(ManagedIface iface); + + @PersistentCollection + public Set getSetInteger(); + public void setSetInteger(Set collection); + + @OneToMany(cascade=CascadeType.PERSIST) + public Set getSetPC(); + public void setSetPC(Set collection); + + @OneToMany(cascade=CascadeType.PERSIST) + public Set getSetI(); + public void setSetI(Set collection); + + @OneToOne(cascade=CascadeType.PERSIST) + public SimpleEntity getPC(); + public void setPC(SimpleEntity pc); + + public void unimplemented(); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java new file mode 100644 index 000000000..a35bcecb0 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java @@ -0,0 +1,13 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.Embeddable; +import javax.persistence.Basic; + +import org.apache.openjpa.persistence.ManagedInterface; + +@ManagedInterface +@Embeddable +public interface ManagedInterfaceEmbed { + public int getEmbedIntField(); + public void setEmbedIntField(int i); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java new file mode 100644 index 000000000..8b4b27031 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java @@ -0,0 +1,46 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.OneToOne; +import javax.persistence.Id; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.CascadeType; + +@Entity +public class ManagedInterfaceOwner { + + @Id + private int id; + + private int intField; + + @OneToOne(cascade=CascadeType.PERSIST) + private ManagedInterfaceSup iface; + + @Embedded + private ManagedInterfaceEmbed embed; + + public int getIntField() { + return intField; + } + + public void setIntField(int i) { + intField = i; + } + + public ManagedInterfaceSup getIFace() { + return iface; + } + + public void setIFace(ManagedInterfaceSup iface) { + this.iface = iface; + } + + public ManagedInterfaceEmbed getEmbed() { + return embed; + } + + public void setEmbed(ManagedInterfaceEmbed embed) { + this.embed = embed; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java new file mode 100644 index 000000000..873746475 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java @@ -0,0 +1,18 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; + +import org.apache.openjpa.persistence.ManagedInterface; + +@ManagedInterface +@Entity +public interface ManagedInterfaceSup { + @Id @GeneratedValue + public int getId(); + public void setId(int id); + + public int getIntFieldSup(); + public void setIntFieldSup(int i); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java new file mode 100644 index 000000000..8befa73c6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java @@ -0,0 +1,20 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.apache.openjpa.persistence.ManagedInterface; + +@ManagedInterface +@Entity +public interface MixedInterface { + + @Id + @GeneratedValue + public int getId(); + public void setId(int id); + + public int getIntField(); + public void setIntField(int i); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java new file mode 100644 index 000000000..1380f21ac --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java @@ -0,0 +1,30 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; + +@Entity +public class MixedInterfaceImpl implements MixedInterface { + @Id + @GeneratedValue + private int id; + + private int intField; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getIntField() { + return intField; + } + + public void setIntField(int i) { + intField = i; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java new file mode 100644 index 000000000..bfc1eef73 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java @@ -0,0 +1,8 @@ +package org.apache.openjpa.persistence.managedinterface; + + +public interface NonMappedInterface { + public int getIntField(); + + public void setIntField(int i); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java new file mode 100644 index 000000000..e8bd3e33f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java @@ -0,0 +1,17 @@ +package org.apache.openjpa.persistence.managedinterface; + +import javax.persistence.Entity; + +@Entity +public class NonMappedInterfaceImpl + implements NonMappedInterface { + private int mismatch; + + public int getIntField() { + return mismatch; + } + + public void setIntField(int i) { + mismatch = i; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java new file mode 100644 index 000000000..2a4af537f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java @@ -0,0 +1,36 @@ +/* + * 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.managedinterface; + +import javax.persistence.Id; +import javax.persistence.Entity; + +import org.apache.openjpa.persistence.ManagedInterface; + +@ManagedInterface +@Entity +public interface SimpleManagedInterface { + + @Id + public int getId(); + public void setId(int id); + + public String getString(); + public void setString(String s); +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java new file mode 100644 index 000000000..6a37616c6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java @@ -0,0 +1,449 @@ +package org.apache.openjpa.persistence.managedinterface; + +import java.util.Set; +import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import javax.persistence.Query; +import javax.persistence.EntityNotFoundException; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.JPAFacadeHelper; +import org.apache.openjpa.persistence.Extent; +import org.apache.openjpa.persistence.query.SimpleEntity; +import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.persistence.PersistenceException; + +public class TestManagedInterfaces extends SingleEMFTestCase { + + @Override + public void setUp() { + super.setUp(SimpleEntity.class, ManagedInterfaceEmbed.class, + ManagedInterfaceSup.class, ManagedIface.class, + ManagedInterfaceOwner.class, MixedInterface.class, + MixedInterfaceImpl.class, NonMappedInterfaceImpl.class, + CLEAR_TABLES); + } + + public void testEmbeddedMetaData() { + emf.createEntityManager().close(); + ClassMetaData ownerMeta = JPAFacadeHelper.getMetaData(emf, + ManagedIface.class); + ClassMetaData embeddedMeta = ownerMeta.getField("embed") + .getDefiningMetaData(); + assertTrue(embeddedMeta.isManagedInterface()); + assertTrue(embeddedMeta.isIntercepting()); + + ClassMetaData embeddableMeta = JPAFacadeHelper.getMetaData(emf, + ManagedInterfaceEmbed.class); + assertTrue(embeddableMeta.isManagedInterface()); + assertTrue(embeddableMeta.isIntercepting()); + } + + public void testManagedInterface() throws Exception { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + ManagedIface pc = em.createInstance(ManagedIface.class); + pc.setIntFieldSup(3); + pc.setIntField(4); + pc.setEmbed(em.createInstance(ManagedInterfaceEmbed.class)); + + pc.getEmbed().setEmbedIntField(5); + assertEquals(5, pc.getEmbed().getEmbedIntField()); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + assertEquals(3, pc.getIntFieldSup()); + assertEquals(4, pc.getIntField()); + assertEquals(5, pc.getEmbed().getEmbedIntField()); + em.getTransaction().begin(); + pc.setIntField(14); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager (); + em.getTransaction().begin(); + Query query = em.createQuery("select o from ManagedIface o " + + "where o.intField = 14"); + pc = (ManagedIface) query.getSingleResult(); + assertEquals(14, pc.getIntField()); + em.remove(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + try { + assertNull(em.find(ManagedIface.class, oid)); + } catch (EntityNotFoundException onfe) {} + + em.close(); + } + + public void testInterfaceOwner() { + OpenJPAEntityManager em = emf.createEntityManager(); + ManagedInterfaceOwner pc = new ManagedInterfaceOwner(); + pc.setIFace(em.createInstance(ManagedInterfaceSup.class)); + pc.setEmbed(em.createInstance(ManagedInterfaceEmbed.class)); + pc.getIFace().setIntFieldSup(3); + pc.getEmbed().setEmbedIntField(5); + + em.getTransaction().begin(); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + pc = em.find(ManagedInterfaceOwner.class, oid); + assertEquals(3, pc.getIFace().getIntFieldSup()); + assertEquals(5, pc.getEmbed().getEmbedIntField()); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedInterfaceOwner.class, oid); + assertEquals(3, pc.getIFace().getIntFieldSup()); + assertEquals(5, pc.getEmbed().getEmbedIntField()); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + Query q = em.createQuery("select o from ManagedInterfaceOwner o " + + "where o.iface.intFieldSup = 3 and o.embed.embedIntField = 5"); + pc = (ManagedInterfaceOwner) q.getSingleResult(); + assertEquals(3, pc.getIFace().getIntFieldSup()); + assertEquals(5, pc.getEmbed().getEmbedIntField()); + + pc.getIFace().setIntFieldSup(13); + pc.getEmbed().setEmbedIntField(15); + assertEquals(13, pc.getIFace().getIntFieldSup()); + assertEquals(15, pc.getEmbed().getEmbedIntField()); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedInterfaceOwner.class, oid); + assertEquals(13, pc.getIFace().getIntFieldSup()); + assertEquals(15, pc.getEmbed().getEmbedIntField()); + em.close(); + } + + public void testCollection() { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + ManagedIface pc = em.createInstance(ManagedIface.class); + Set set = new HashSet(); + set.add(new Integer(3)); + set.add(new Integer(4)); + set.add(new Integer(5)); + pc.setSetInteger(set); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetInteger(); + assertEquals(3, set.size()); + assertTrue(set.contains(new Integer(3))); + assertTrue(set.contains(new Integer(4))); + assertTrue(set.contains(new Integer(5))); + em.getTransaction().begin(); + set.remove(new Integer(4)); + set.add(new Integer(14)); + set.add(new Integer(15)); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetInteger(); + assertEquals(4, set.size()); + assertTrue(set.contains(new Integer(3))); + assertTrue(set.contains(new Integer(5))); + assertTrue(set.contains(new Integer(14))); + assertTrue(set.contains(new Integer(15))); + em.getTransaction().begin(); + pc.setSetInteger(null); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetInteger(); + assertTrue (set == null || set.size() == 0); + em.close(); + } + + public void testCollectionPC() { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + ManagedIface pc = em.createInstance(ManagedIface.class); + Set set = new HashSet(); + set.add(new SimpleEntity("a", "3")); + set.add(new SimpleEntity("b", "4")); + set.add(new SimpleEntity("c", "5")); + pc.setSetPC(set); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetPC(); + assertEquals(3, set.size()); + Collection seen = new ArrayList(); + SimpleEntity rel; + SimpleEntity toRem = null; + for (Iterator it = set.iterator(); it.hasNext();) { + rel = (SimpleEntity) it.next(); + seen.add(rel.getName()); + if (rel.getValue().equals("4")) + toRem = rel; + } + assertEquals(3, seen.size()); + assertTrue(seen.contains("a")); + assertTrue(seen.contains("b")); + assertTrue(seen.contains("c")); + em.getTransaction().begin(); + assertNotNull(toRem); + set.remove(toRem); + set.add(new SimpleEntity("x", "14")); + set.add(new SimpleEntity("y", "15")); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetPC(); + assertEquals(4, set.size()); + seen.clear(); + for (Iterator it = set.iterator(); it.hasNext();) { + rel = (SimpleEntity) it.next(); + seen.add(rel.getName()); + } + assertEquals(4, seen.size()); + assertTrue(seen.contains("a")); + assertTrue(seen.contains("c")); + assertTrue(seen.contains("x")); + assertTrue(seen.contains("y")); + em.getTransaction().begin(); + pc.setSetPC(null); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetPC(); + assertTrue (set == null || set.size() == 0); + em.close(); + } + + public void testCollectionInterfaces() { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + ManagedIface pc = em.createInstance(ManagedIface.class); + Set set = new HashSet(); + set.add(createInstance(em, 3)); + set.add(createInstance(em, 4)); + set.add(createInstance(em, 5)); + pc.setSetI(set); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetI(); + assertEquals(3, set.size()); + Collection seen = new ArrayList(); + ManagedIface rel = null; + ManagedIface toRem = null; + for (Iterator it = set.iterator(); it.hasNext();) { + rel = (ManagedIface) it.next(); + seen.add(new Integer(rel.getIntField())); + if (rel.getIntField() == 4) + toRem = rel; + } + assertEquals(3, seen.size()); + assertTrue(seen.contains(new Integer(3))); + assertTrue(seen.contains(new Integer(4))); + assertTrue(seen.contains(new Integer(5))); + em.getTransaction().begin(); + assertNotNull(toRem); + set.remove(toRem); + set.add(createInstance(em, 14)); + set.add(createInstance(em, 15)); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetI(); + assertEquals(4, set.size()); + seen.clear(); + for (Iterator it = set.iterator(); it.hasNext();) { + rel = (ManagedIface) it.next(); + seen.add(new Integer(rel.getIntField())); + } + assertEquals(4, seen.size()); + assertTrue(seen.contains(new Integer(3))); + assertTrue(seen.contains(new Integer(5))); + assertTrue(seen.contains(new Integer(14))); + assertTrue(seen.contains(new Integer(15))); + em.getTransaction().begin(); + pc.setSetPC(null); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + set = pc.getSetPC(); + assertTrue (set == null || set.size() == 0); + em.close(); + } + + public void testMixedQuery() { + createMixed(); + + OpenJPAEntityManager em = emf.createEntityManager(); + try { + Query q = em.createQuery("select o from MixedInterface o " + + "where o.intField = 4"); + Collection c = q.getResultList(); + Set seen = new HashSet(); + assertEquals(2, c.size()); + MixedInterface pc; + for (Iterator it = c.iterator(); it.hasNext();) { + pc = (MixedInterface) it.next(); + assertEquals(4, pc.getIntField()); + seen.add(pc.getClass()); + } + assertEquals(2, seen.size()); + + fail("OPENJPA-481"); + } catch (PersistenceException e) { + // expected + } finally { + em.close(); + } + } + + public void testQueryForMixedInterfaceImpls() { + createMixed(); + + OpenJPAEntityManager em = emf.createEntityManager(); + Query q = em.createQuery("select o from MixedInterfaceImpl o " + + "where o.intField = 4"); + MixedInterface pc = (MixedInterface) q.getSingleResult(); + assertEquals(4, pc.getIntField()); + assertTrue(pc instanceof MixedInterfaceImpl); + em.close(); + } + + public void testMixedExtent() { + createMixed(); + + OpenJPAEntityManager em = emf.createEntityManager(); + Extent e = em.createExtent(MixedInterface.class, true); + Set seen = new HashSet(); + int size = 0; + for (Iterator it = e.iterator(); it.hasNext();) { + seen.add(it.next().getClass()); + size++; + } + assertEquals(3, size); + assertEquals(2, seen.size()); + + e = em.createExtent(MixedInterface.class, false); + seen = new HashSet(); + size = 0; + for (Iterator it = e.iterator(); it.hasNext();) { + seen.add(it.next().getClass()); + size++; + } + assertEquals(1, size); + assertNotEquals(MixedInterfaceImpl.class, seen.iterator().next()); + em.close(); + } + + private void createMixed() { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + MixedInterface pc = em.createInstance(MixedInterface.class); + pc.setIntField(4); + em.persist(pc); + pc = new MixedInterfaceImpl(); + pc.setIntField(4); + em.persist(pc); + pc = new MixedInterfaceImpl(); + pc.setIntField(8); + em.persist(pc); + em.getTransaction().commit(); + em.close(); + } + + public void testUnimplementedThrowsException() { + OpenJPAEntityManager em = emf.createEntityManager(); + ManagedIface pc = createInstance(em, 1); + try { + pc.unimplemented(); + fail("Exception expected."); + } catch (UnsupportedOperationException uoe) {} // good + em.close(); + } + + public void testNonMappedCreateInstanceException() { + // OpenJPA's support of non-mapped interfaces differs from JDO support; + // there is no special query or relation support for non-mapped + // interfaces in OpenJPA at this time. + OpenJPAEntityManager em = null; + try { + em = emf.createEntityManager(); + em.createInstance(NonMappedInterface.class); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) {} // good + if (em != null) + em.close(); + } + + public void testDetach() { + OpenJPAEntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + ManagedIface pc = createInstance(em, 4); + em.persist(pc); + Object oid = em.getObjectId(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + ManagedIface pcx = em.find(ManagedIface.class, oid); + pc = em.detach(pcx); + em.close(); + + assertTrue(em.isDetached(pc)); + pc.setIntField(7); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + em.merge(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(ManagedIface.class, oid); + assertEquals(7, pc.getIntField()); + em.close(); + } + + private ManagedIface createInstance(OpenJPAEntityManager em, int i) { + ManagedIface pc = em.createInstance(ManagedIface.class); + pc.setIntField(i); + return pc; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java new file mode 100644 index 000000000..da567bb1f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java @@ -0,0 +1,92 @@ +/* + * 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.managedinterface; + +import javax.persistence.EntityManager; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.JPAFacadeHelper; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.query.SimpleEntity; +import org.apache.openjpa.kernel.AbstractBrokerFactory; +import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.meta.MetaDataRepository; + +public class TestSimpleManagedInterface + extends SingleEMFTestCase { + + public void setUp() { + setUp(SimpleManagedInterface.class, SimpleEntity.class, CLEAR_TABLES); + } + + public void testMetaDataRepository() { + AbstractBrokerFactory bf = + (AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory(emf); + bf.makeReadOnly(); + MetaDataRepository repos = bf.getConfiguration() + .getMetaDataRepositoryInstance(); + ClassMetaData meta = repos.getMetaData(SimpleManagedInterface.class, + null, false); + assertNotNull(meta); + assertTrue(meta.isManagedInterface()); + assertEquals(SimpleManagedInterface.class, meta.getDescribedType()); + } + + public void testInterfaceImplGeneration() { + ((AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory(emf)) + .makeReadOnly(); + // load metadata to trigger instance creation + ClassMetaData meta = JPAFacadeHelper.getMetaData(emf, + SimpleManagedInterface.class); + assertEquals(SimpleManagedInterface.class, meta.getDescribedType()); + } + + public void testBasicOperations() { + OpenJPAEntityManager em = emf.createEntityManager(); + SimpleManagedInterface pc = + em.createInstance(SimpleManagedInterface.class); + pc.setId(17); + pc.setString("hello!"); + em.getTransaction().begin(); + em.persist(pc); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + pc = em.find(SimpleManagedInterface.class, 17); + assertNotNull(pc); + em.getTransaction().begin(); + pc.setString("updated"); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + em.remove(em.getReference(SimpleManagedInterface.class, 17)); + em.getTransaction().commit(); + em.close(); + } + + public void testJPQL() { + EntityManager em = emf.createEntityManager(); + assertEquals(0, em.createQuery("select o from SimpleManagedInterface o") + .getResultList().size()); + em.close(); + } +} \ No newline at end of file diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java index e188fc2f6..b12c818c8 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java @@ -172,6 +172,7 @@ public class AnnotationPersistenceMetaDataParser _tags.put(KeyType.class, KEY_TYPE); _tags.put(LoadFetchGroup.class, LOAD_FETCH_GROUP); _tags.put(LRS.class, LRS); + _tags.put(ManagedInterface.class, MANAGED_INTERFACE); _tags.put(ReadOnly.class, READ_ONLY); _tags.put(Type.class, TYPE); } @@ -572,6 +573,10 @@ public class AnnotationPersistenceMetaDataParser if (isMetaDataMode()) fgs = ((FetchGroups) anno).value(); break; + case MANAGED_INTERFACE: + if (isMetaDataMode()) + parseManagedInterface(meta, (ManagedInterface) anno); + break; default: throw new UnsupportedException(_loc.get("unsupported", _cls, anno.toString())); @@ -595,7 +600,8 @@ public class AnnotationPersistenceMetaDataParser // scan possibly non-PC hierarchy for callbacks. // redundant for PC superclass but we don't know that yet // so let LifecycleMetaData determine that - if (!Object.class.equals(_cls.getSuperclass())) { + if (_cls.getSuperclass() != null && + !Object.class.equals(_cls.getSuperclass())) { recordCallbacks(meta, parseCallbackMethods(_cls.getSuperclass(), null, true, false, getRepository()), null, true); } @@ -750,6 +756,11 @@ public class AnnotationPersistenceMetaDataParser meta.setDataCacheName(null); } + private void parseManagedInterface(ClassMetaData meta, + ManagedInterface iface) { + meta.setManagedInterface(true); + } + /** * Parse @DetachedState. The annotation may be null. */ @@ -798,6 +809,10 @@ public class AnnotationPersistenceMetaDataParser public static Collection[] parseCallbackMethods (Class cls, Collection[] callbacks, boolean sups, boolean listener, MetaDataRepository repos) { + + if (cls == null) + throw new IllegalArgumentException("cls cannot be null"); + // first sort / filter based on inheritance Set methods = new TreeSet(MethodComparator. getInstance()); diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java new file mode 100644 index 000000000..999b8c095 --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java @@ -0,0 +1,41 @@ +/* + * 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; + +import static java.lang.annotation.ElementType.TYPE; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; + +/** + * The annotated interface should be treated as a managed interface by OpenJPA. + * New instances of this type can be created by invoking + * {@link OpenJPAEntityManager#createInstance(Class)}. + * Interfaces with this annotation should also be annotated with one of the JPA + * entity annotations ({@link javax.persistence.Entity @Entity}, + * {@link javax.persistence.MappedSuperclass @MappedSuperclass}, + * or {@link javax.persistence.Embeddable @Embeddable}). + * + * @since 1.1.0 + * @published + */ +@Target({ TYPE }) +@Retention(RUNTIME) +public @interface ManagedInterface { +} diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java index 8c4199e3e..f10e07d03 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java @@ -77,6 +77,7 @@ public enum MetaDataTag { KEY_TYPE, LOAD_FETCH_GROUP, LRS, + MANAGED_INTERFACE, READ_ONLY, TYPE, } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java index d66c7b1c9..aba1c3ead 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java @@ -780,8 +780,11 @@ public interface OpenJPAEntityManager * create a subclass of the type that does implement * {@link org.apache.openjpa.enhance.PersistenceCapable}, and will attempt * to redefine the methods in cls to enable persistent - * attribute tracking. Otherwise, this will return an instance of the - * specified class. + * attribute tracking. Otherwise, if cls is a managed type, + * this will return an instance of the specified class. + * + * @throws IllegalArgumentException if cls is not a managed + * type or interface. */ public T createInstance(Class cls); diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java index 83e00b7e1..124055b96 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java @@ -261,7 +261,8 @@ public class PersistenceMetaDataDefaults J2DoPriv5Helper.getDeclaredFieldsAction(cls)))) access |= ClassMetaData.ACCESS_FIELD; if (usesAccess((Method[]) AccessController.doPrivileged( - J2DoPriv5Helper.getDeclaredMethodsAction(cls)))) + J2DoPriv5Helper.getDeclaredMethodsAction(cls))) + || cls.isInterface()) // OpenJPA managed ifaces must use prop access access |= ClassMetaData.ACCESS_PROPERTY; return (access == 0) ? getAccessType(cls.getSuperclass()) : access; } diff --git a/openjpa-project/src/doc/manual/ref_guide_pc.xml b/openjpa-project/src/doc/manual/ref_guide_pc.xml index 1b3ebea8b..f83dcb79f 100644 --- a/openjpa-project/src/doc/manual/ref_guide_pc.xml +++ b/openjpa-project/src/doc/manual/ref_guide_pc.xml @@ -483,6 +483,43 @@ a javaagent or in a Java 6 environment. +
+ Managed Interfaces + + interfaces + managed + + +OpenJPA's managed interface feature allows you to define your object model +entirely in terms of interfaces, instead of concrete classes. To use this +feature, you must annotate your managed interfaces with the +ManagedInterface annotation, and use the +OpenJPAEntityManager.createInstance(Class) method to +create new records. Note that createInstance() returns +unmanaged instances; you must pass them to +EntityManager.persist() to store them in the database. + + +@ManagedInterface +public interface PersonIface { + @Id @GeneratedValue + int getId(); + void setId(int id); + + // implicitly persistent per JPA property rules + String getName(); + void setName(String name); +} + + +OpenJPAEntityManager em = ...; +PersonIface person = em.createInstance(PersonIface.class); +person.setName("Homer Simpson"); +em.getTransaction().begin(); +em.persist(person); +em.getTransaction().commit(); + +
Object Identity