OPENJPA-147 -- managed interface support. Also includes failure test case for OPENJPA-481.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@610924 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2008-01-10 20:44:35 +00:00
parent 2933147f53
commit d4a2929a57
30 changed files with 1008 additions and 55 deletions

View File

@ -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 <code>type</code> from <code>conf</code>'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 <code>meta</code> by converting back to a class
* and then loading from <code>conf</code>'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 = <superClass>;
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;
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.
// ##### probably should walk up the hierarchy, or check that
// ##### serp does that.
if (fields[i].getDeclarer() == declarer) {
break;
break outer;
}
}
}

View File

@ -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.

View File

@ -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 <code>cls</code> is a managed type, this will return an
* instance of the specified class.
*
* @throws IllegalArgumentException if <code>cls</code> is not a managed
* type or interface.
*/
public Object newInstance(Class cls);

View File

@ -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) {
}
}
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) {

View File

@ -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++) {

View File

@ -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) {

View File

@ -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();

View File

@ -114,8 +114,7 @@ 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)
@ -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());
}
}

View File

@ -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() {

View File

@ -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();

View File

@ -388,8 +388,8 @@ 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 \

View File

@ -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;
}

View File

@ -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();

View File

@ -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<Integer> getSetInteger();
public void setSetInteger(Set<Integer> collection);
@OneToMany(cascade=CascadeType.PERSIST)
public Set<SimpleEntity> getSetPC();
public void setSetPC(Set<SimpleEntity> collection);
@OneToMany(cascade=CascadeType.PERSIST)
public Set<ManagedIface> getSetI();
public void setSetI(Set<ManagedIface> collection);
@OneToOne(cascade=CascadeType.PERSIST)
public SimpleEntity getPC();
public void setPC(SimpleEntity pc);
public void unimplemented();
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package org.apache.openjpa.persistence.managedinterface;
public interface NonMappedInterface {
public int getIntField();
public void setIntField(int i);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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<LifecycleCallbacks>[] parseCallbackMethods
(Class cls, Collection<LifecycleCallbacks>[] callbacks, boolean sups,
boolean listener, MetaDataRepository repos) {
if (cls == null)
throw new IllegalArgumentException("cls cannot be null");
// first sort / filter based on inheritance
Set<Method> methods = new TreeSet<Method>(MethodComparator.
getInstance());

View File

@ -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 {
}

View File

@ -77,6 +77,7 @@ public enum MetaDataTag {
KEY_TYPE,
LOAD_FETCH_GROUP,
LRS,
MANAGED_INTERFACE,
READ_ONLY,
TYPE,
}

View File

@ -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 <code>cls</code> to enable persistent
* attribute tracking. Otherwise, this will return an instance of the
* specified class.
* attribute tracking. Otherwise, if <code>cls</code> is a managed type,
* this will return an instance of the specified class.
*
* @throws IllegalArgumentException if <code>cls</code> is not a managed
* type or interface.
*/
public <T> T createInstance(Class<T> cls);

View File

@ -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;
}

View File

@ -483,6 +483,43 @@ a javaagent or in a Java 6 environment.
</itemizedlist>
</section>
</section>
<section id="ref_guide_pc_interfaces">
<title>Managed Interfaces</title>
<indexterm zone="ref_guide_pc_interfaces">
<primary>interfaces</primary>
<secondary>managed</secondary>
</indexterm>
<para>
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
<classname>ManagedInterface</classname> annotation, and use the
<literal>OpenJPAEntityManager.createInstance(Class)</literal> method to
create new records. Note that <literal>createInstance()</literal> returns
unmanaged instances; you must pass them to
<literal>EntityManager.persist()</literal> to store them in the database.
</para>
<programlisting>
@ManagedInterface
public interface PersonIface {
@Id @GeneratedValue
int getId();
void setId(int id);
// implicitly persistent per JPA property rules
String getName();
void setName(String name);
}
</programlisting>
<programlisting>
OpenJPAEntityManager em = ...;
PersonIface person = em.createInstance(PersonIface.class);
person.setName("Homer Simpson");
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
</programlisting>
</section>
<section id="ref_guide_pc_oid">
<title>
Object Identity