OPENJPA-1135 Correct access type related issues with attribute overrides and multi-level parsing.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@790150 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jeremy Bauer 2009-07-01 12:52:56 +00:00
parent 80dbd79952
commit 0f45f13fc2
16 changed files with 523 additions and 13 deletions

View File

@ -0,0 +1,48 @@
/*
* 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.embed.attrOverrides;
import javax.persistence.Basic;
import javax.persistence.Embeddable;
@Embeddable
public class AnnoOverEmbed {
@Basic
private int intEmbed;
@Basic
private String strEmbed;
public void setIntEmbed(int intEmbed) {
this.intEmbed = intEmbed;
}
public int getIntEmbed() {
return intEmbed;
}
public void setStrEmbed(String strEmbed) {
this.strEmbed = strEmbed;
}
public String getStrEmbed() {
return strEmbed;
}
}

View File

@ -45,7 +45,9 @@ public class TestAttrOverridesXml extends SQLListenerTestCase {
public int eId = 1;
public void setUp() {
setUp(DROP_TABLES);
setUp(
org.apache.openjpa.persistence.embed.attrOverrides.AnnoOverEmbed.class,
DROP_TABLES);
}
@Override
@ -92,6 +94,38 @@ public class TestAttrOverridesXml extends SQLListenerTestCase {
em.close();
}
/**
* This test verifies that an XML defined entity with an annotated
* only embeddable has attribute overrides applied correctly.
*/
public void testXMLEntityWithAnnotatedOverrideEmbed() {
OpenJPAEntityManagerSPI em = emf.createEntityManager();
XMLOverEntity xoe = new XMLOverEntity();
xoe.setId(new Random().nextInt());
AnnoOverEmbed aoe = new AnnoOverEmbed();
aoe.setIntEmbed(1);
aoe.setStrEmbed("StrVal");
xoe.setEmbed(aoe);
em.getTransaction().begin();
em.persist(xoe);
em.getTransaction().commit();
assertTrue(verifyColumnOverride(em, "XMLOverEntity",
"intOverEmbed"));
assertFalse(verifyColumnOverride(em, "XMLOverEntity",
"intEmbed"));
assertTrue(verifyColumnOverride(em, "XMLOverEntity",
"strOverEmbed"));
assertFalse(verifyColumnOverride(em, "XMLOverEntity",
"strEmbed"));
em.close();
}
public void createObj1() {
EntityManager em = emf.createEntityManager();
EntityTransaction tran = em.getTransaction();

View File

@ -0,0 +1,42 @@
/*
* 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.embed.attrOverrides;
public class XMLOverEntity {
private int id;
private AnnoOverEmbed embed;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setEmbed(AnnoOverEmbed embed) {
this.embed = embed;
}
public AnnoOverEmbed getEmbed() {
return embed;
}
}

View File

@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.inheritance;
import java.util.Random;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.inheritance.entity.BaseCallback;
import org.apache.openjpa.persistence.inheritance.entity.XMLCallback;
import org.apache.openjpa.persistence.inheritance.entity.XMLSuperCallback;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestCallbackEntity
extends SingleEMFTestCase {
@Override
protected String getPersistenceUnitName() {
return "AbstractCallbackPU";
}
/*
* This test verifies that the persistence metadata factory can create
* and use separate copies of the XML metadata parser when a domain model
* contains a base class with unknown access type and multi-level
* inheritance of XML defined types. Per JPA spec, the superclass must
* be queried to determine the access type.
*/
public void testCallbackEntity() {
EntityManager em = emf.createEntityManager();
BaseCallback bc = new BaseCallback();
bc.setId(new Random().nextInt());
bc.setName("BaseCallback");
// Persist the entity
em.getTransaction().begin();
em.persist(bc);
em.getTransaction().commit();
// Assert callbacks fire expected # of times
assertEquals(1, BaseCallback.postPersistCount);
assertEquals(1, XMLCallback.prePersistCount);
assertEquals(1, XMLSuperCallback.postPersistCount);
// Remove the entity
em.getTransaction().begin();
em.remove(bc);
em.getTransaction().commit();
em.close();
}
}

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.inheritance.entity;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.PostPersist;
/*
* Annotated entity class with no persistent attributes or access type
* defined. This is necessary to force query of the superclass to determine
* the default access type.
*/
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class BaseCallback extends XMLCallback {
public transient static int postPersistCount = 0;
@PostPersist
public void basePostPersist() {
postPersistCount++;
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.inheritance.entity;
/*
* XML defined class with no persistent attributes, only a callback.
*/
public class XMLCallback extends XMLSuperCallback {
public transient static int prePersistCount = 0;
public void prePersist() {
prePersistCount++;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.inheritance.entity;
/*
* XML defined entity superclass with no persistent attributes.
*/
public class XMLSuperCallback {
private int id;
private String name;
public transient static int postPersistCount = 0;
public void superPostPersist() {
postPersistCount++;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -25,7 +25,9 @@ public class TestSimpleXmlEntity
extends SingleEMTestCase {
public void setUp() {
setUp(CLEAR_TABLES);
setUp(
org.apache.openjpa.persistence.embed.attrOverrides.AnnoOverEmbed.class,
CLEAR_TABLES);
}
protected String getPersistenceUnitName() {

View File

@ -34,7 +34,9 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestXmlOverrideEntity extends SingleEMFTestCase {
public void setUp() throws ClassNotFoundException {
setUp(CLEAR_TABLES);
setUp(
org.apache.openjpa.persistence.embed.attrOverrides.AnnoOverEmbed.class,
CLEAR_TABLES);
// make sure that XmlOverrideEntity is registered for our metadata tests
Class.forName(XmlOverrideEntity.class.getName(), true,

View File

@ -47,7 +47,8 @@
<mapping-file>org/apache/openjpa/persistence/access/access-pudef-prop-orm.xml</mapping-file>
<mapping-file>org/apache/openjpa/persistence/jdbc/order/order-orm.xml</mapping-file>
<mapping-file>org/apache/openjpa/persistence/jdbc/order/order-orm-2.xml</mapping-file>
<mapping-file>org/apache/openjpa/persistence/jdbc/order/order-orm-3.xml</mapping-file>
<mapping-file>org/apache/openjpa/persistence/jdbc/order/order-orm-3.xml</mapping-file>
<mapping-file>org/apache/openjpa/persistence/inheritance/orm.xml</mapping-file>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>
@ -120,6 +121,8 @@
<class>org.apache.openjpa.persistence.embed.OrderXml</class>
<class>org.apache.openjpa.persistence.embed.attrOverrides.AddressXml</class>
<class>org.apache.openjpa.persistence.embed.attrOverrides.ZipcodeXml</class>
<class>org.apache.openjpa.persistence.embed.attrOverrides.XMLOverEntity</class>
<class>org.apache.openjpa.persistence.embed.attrOverrides.AnnoOverEmbed</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>
@ -170,4 +173,16 @@
value="buildSchema"/>
</properties>
</persistence-unit>
<persistence-unit name="AbstractCallbackPU" transaction-type="RESOURCE_LOCAL">
<description>PU for testing with an entity with no persistent attributes</description>
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<mapping-file>org/apache/openjpa/persistence/inheritance/orm.xml</mapping-file>
<class>org.apache.openjpa.persistence.inheritance.entity.BaseCallback</class>
<class>org.apache.openjpa.persistence.inheritance.entity.XMLCallback</class>
<class>org.apache.openjpa.persistence.inheritance.entity.XMLSuperCallback</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema"/>
</properties>
</persistence-unit>
</persistence>

View File

@ -199,6 +199,22 @@ version="2.0">
</element-collection>
</attributes>
</entity>
<entity name="XMLOverEntity"
class="org.apache.openjpa.persistence.embed.attrOverrides.XMLOverEntity"
access="FIELD">
<attributes>
<id name="id"/>
<embedded name="embed">
<attribute-override name="intEmbed">
<column name="intOverEmbed"/>
</attribute-override>
<attribute-override name="strEmbed">
<column name="strOverEmbed"/>
</attribute-override>
</embedded>
</attributes>
</entity>
<embeddable
class="org.apache.openjpa.persistence.embed.attrOverrides.AddressXml"

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
version="1.0">
<entity class="org.apache.openjpa.persistence.inheritance.entity.XMLCallback">
<pre-persist method-name="prePersist" />
</entity>
<entity class="org.apache.openjpa.persistence.inheritance.entity.XMLSuperCallback"
access="FIELD">
<post-persist method-name="superPostPersist" />
<attributes>
<id name="id"/>
<basic name="name"/>
</attributes>
</entity>
</entity-mappings>

View File

@ -279,7 +279,8 @@ public class PersistenceMetaDataDefaults
return null;
Class<?> cls = meta.getDescribedType();
Class<?> sup = cls.getSuperclass();
if (sup == null)
if (sup == null || "java.lang.Object".equals(
sup.getName()))
return null;
MetaDataRepository repos = meta.getRepository();
ClassMetaData supMeta = repos.getCachedMetaData(sup);
@ -309,8 +310,13 @@ public class PersistenceMetaDataDefaults
return access;
ClassMetaData sup = getCachedSuperclassMetaData(meta);
while (sup != null && sup.isExplicitAccess())
sup = getCachedSuperclassMetaData(sup);
ClassMetaData tmpSup = sup;
while (tmpSup != null && tmpSup.isExplicitAccess()) {
tmpSup = getCachedSuperclassMetaData(tmpSup);
if (tmpSup != null) {
sup = tmpSup;
}
}
if (sup != null && !AccessCode.isUnknown(sup))
return sup.getAccessType();

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -29,6 +30,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
@ -84,6 +87,9 @@ public class PersistenceMetaDataFactory
private Set<URL> _unparsed = null; // xml rsrc
private boolean _fieldOverride = true;
protected Stack<XMLPersistenceMetaDataParser> _stack =
new Stack<XMLPersistenceMetaDataParser>();
/**
* Whether to use field-level override or class-level override.
* Defaults to true.
@ -141,11 +147,23 @@ public class PersistenceMetaDataFactory
}
/**
* Return XML metadata parser, creating it if it does not already exist.
* Return XML metadata parser, creating it if it does not already exist or
* if the existing parser is parsing.
*/
public XMLPersistenceMetaDataParser getXMLParser() {
if (_xmlParser == null) {
if (_xmlParser == null || _xmlParser.isParsing()) {
Class<?> parseCls = null;
ArrayList<Class<?>> parseList = null;
// If there is an existing parser and it is parsing, push it on
// the stack and return a new one.
if (_xmlParser != null) {
_stack.push(_xmlParser);
parseCls = _xmlParser.getParseClass();
parseList = _xmlParser.getParseList();
}
_xmlParser = newXMLParser(true);
_xmlParser.addToParseList(parseList);
_xmlParser.addToParseList(parseCls);
_xmlParser.setRepository(repos);
if (_fieldOverride)
_xmlParser.setAnnotationParser(getAnnotationParser());
@ -153,6 +171,15 @@ public class PersistenceMetaDataFactory
return _xmlParser;
}
public void resetXMLParser() {
// If a parser was pushed on the stack due to multi-level parsing,
// clear the current parser and pop the inner parser off the stack.
if (!_stack.isEmpty()) {
_xmlParser.clear();
_xmlParser = _stack.pop();
}
}
/**
* Set the metadata parser.
*/
@ -196,10 +223,12 @@ public class PersistenceMetaDataFactory
boolean parsedXML = false;
if (_unparsed != null && !_unparsed.isEmpty()
&& (mode & MODE_META) != 0) {
for (URL url : _unparsed)
Set<URL> unparsed = new HashSet<URL>(_unparsed);
for (URL url : unparsed) {
parseXML(url, cls, mode, envLoader);
parsedXML = _unparsed.contains(xml);
_unparsed.clear();
}
parsedXML = unparsed.contains(xml);
_unparsed.clear();
// XML process check
meta = repos.getCachedMetaData(cls);
@ -265,6 +294,9 @@ public class PersistenceMetaDataFactory
} catch (IOException ioe) {
throw new GeneralException(ioe);
}
finally {
resetXMLParser();
}
}
/**

View File

@ -184,6 +184,8 @@ public class XMLPersistenceMetaDataParser
private final Stack<Object> _parents = new Stack<Object>();
private Class<?> _cls = null;
// List of classes currently being parsed
private ArrayList<Class<?>> _parseList = new ArrayList<Class<?>>();
private int _fieldPos = 0;
private int _clsPos = 0;
private int _access = AccessCode.UNKNOWN;
@ -490,10 +492,14 @@ public class XMLPersistenceMetaDataParser
@Override
protected void reset() {
// Add all remaining deferred embeddable metadata
addDeferredEmbeddableMetaData();
super.reset();
_elements.clear();
_parents.clear();
_cls = null;
_parseList.clear();
_fieldPos = 0;
_clsPos = 0;
@ -823,6 +829,12 @@ public class XMLPersistenceMetaDataParser
// query mode only?
_cls = classForName(currentClassName());
// Prevent a reentrant parse for the same class
if (parseListContains(_cls)) {
return false;
}
if (_mode == MODE_QUERY) {
if (_parser != null)
_parser.parse(_cls);
@ -1806,6 +1818,13 @@ public class XMLPersistenceMetaDataParser
return false;
boolean system = currentElement() == null;
// If in a multi-level parse, do not add system level listeners.
// Otherwise, they will get added multiple times.
if (system && _parseList != null && _parseList.size() > 0) {
return false;
}
Class<?> type = currentElement() == null ? null :
((ClassMetaData) currentElement()).getDescribedType();
if (type == null)
@ -1895,7 +1914,30 @@ public class XMLPersistenceMetaDataParser
return PersistenceCapable.class;
return super.classForName(name, isRuntime());
}
/**
* Process all deferred embeddables using an unknown access type.
*/
protected void addDeferredEmbeddableMetaData() {
if (_embeddables != null && _embeddables.size() > 0) {
// Reverse iterate the array of remaining deferred embeddables
// since elements will be removed as they are processed.
Class<?>[] classes = _embeddables.keySet().toArray(
new Class<?>[_embeddables.size()]);
for (int i = classes.length - 1 ; i >= 0; i--) {
try {
addDeferredEmbeddableMetaData(classes[i],
AccessCode.UNKNOWN);
}
catch (Exception e) {
throw new MetaDataException(
_loc.get("no-embeddable-metadata",
classes[i].getName()), e);
}
}
}
}
/**
* Process all deferred embeddables and embeddable mapping overrides
* for a given class. This should only happen after the access type
@ -1964,4 +2006,45 @@ public class XMLPersistenceMetaDataParser
protected void applyDeferredEmbeddableOverrides(Class<?> cls)
throws SAXException {
}
/*
* Add the array of classes to the active parse list.
*/
public void addToParseList(ArrayList<Class<?>> parseList) {
if (parseList == null)
return;
_parseList.addAll(parseList);
}
/*
* Add the class to the active parse list.
*/
public void addToParseList(Class<?> parentCls) {
if (parentCls == null)
return;
_parseList.add(parentCls);
}
/*
* Whether the active parse list contains the specified class.
*/
public boolean parseListContains(Class<?> cls) {
if (_parseList.size() == 0)
return false;
return _parseList.contains(cls);
}
/*
* Returns the list of classes actively being parsed.
*/
public ArrayList<Class<?>> getParseList() {
return _parseList;
}
/*
* Returns class currently being parsed.
*/
public Class<?> getParseClass() {
return _cls;
}
}

View File

@ -197,4 +197,5 @@ dynamic-agent: OpenJPA dynamically loaded the class enhancer. Any classes \
that were not enhanced at build time will be enhanced when they are \
loaded by the JVM.
vlem-creation-info: OpenJPA dynamically loaded a validation provider.
no-embeddable-metadata: Unable to load metadata for embeddable class "{0}".