OPENJPA-1176 Add compatibility option to allow private persistent properties

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@794774 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jeremy Bauer 2009-07-16 18:46:22 +00:00
parent c293165cb4
commit 5cb32680a9
10 changed files with 397 additions and 9 deletions

View File

@ -59,6 +59,7 @@ public class Compatibility {
private boolean _cascadeWithDetach = false;
private boolean _useJPA2DefaultOrderColumnName = true;
private boolean _copyOnDetach = false;
private boolean _privatePersistentProperties = false;
/**
* Whether to require exact identity value types when creating object
@ -354,4 +355,34 @@ public class Compatibility {
public void setUseJPA2DefaultOrderColumnName(boolean useJPA2Name) {
_useJPA2DefaultOrderColumnName = useJPA2Name;
}
/**
* Whether OpenJPA allows private, non-transient properties to be
* persistent. Prior to OpenJPA 2.0, if property access was used,
* private properties were considered persistent. This is contrary to the
* JPA specification, which states that persistent properties must be
* public or protected. The default value is false.
*
* @since 2.0.0
* @return true if non-transient private properties should be persistent
*/
public boolean getPrivatePersistentProperties() {
return _privatePersistentProperties;
}
/**
* Whether OpenJPA allows private, non-transient properties to be
* persistent. Prior to OpenJPA 2.0, if property access was used,
* private properties were considered persistent. This is contrary to the
* JPA specification, which states that persistent properties must be
* public or protected.
*
* @param privateProps true if non-transient private properties
* should be persistent
* @since 2.0.0
*/
public void setPrivatePersistentProperties(boolean privateProps) {
_privatePersistentProperties = privateProps;
}
}

View File

@ -375,11 +375,13 @@ public abstract class AbstractMetaDataDefaults
* or<br>
* <code> public T isXXX()</code> where T is boolean or Boolean.<br>
*/
public static boolean isGetter(Method method) {
public static boolean isGetter(Method method, boolean includePrivate) {
if (method == null)
return false;
int mods = method.getModifiers();
if (!Modifier.isPublic(mods)
if (!(Modifier.isPublic(mods)
|| Modifier.isProtected(mods)
|| (Modifier.isPrivate(mods) && includePrivate))
|| Modifier.isNative(mods)
|| Modifier.isStatic(mods))
return false;

View File

@ -80,6 +80,7 @@
<exclude name="**/Unenhanced*.class" />
<exclude name="**/AbstractUnenhanced*.class" />
<exclude name="**/unenhanced/*.class" />
<exclude name="**/persistence/property/PrivAccessModsEntity.class"/>
</fileset>
<openjpac>
<classpath refid="cp" />
@ -98,6 +99,14 @@
</fileset>
<config log="DefaultLevel=${openjpa.loglevel}" />
</openjpac>
<!-- Enhance with private persistent properties compatibility option -->
<openjpac>
<config compatibility="PrivatePersistentProperties=true" />
<classpath refid="cp" />
<fileset dir="${project.build.testOutputDirectory}">
<include name="**/persistence/property/PrivAccessModsEntity.class"/>
</fileset>
<config log="DefaultLevel=${openjpa.loglevel}" />
</openjpac>
</target>
</project>

View File

@ -0,0 +1,88 @@
/*
* 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.property;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
@Entity
@Table(name="PUBPROT_TBL")
public class AccessModsEntity {
private int id;
private String pubString;
private String protString;
private String privString;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setPubString(String pubString) {
this.pubString = pubString;
}
@Column(name="PUB_COL")
public String getPubString() {
return pubString;
}
protected void setProtString(String protString) {
this.protString = protString;
}
@Column(name="PROT_COL")
protected String getProtString() {
return protString;
}
private void setPrivString(String privString) {
this.privString = privString;
}
@Column(name="PRIV_COL")
private String getPrivString() {
return privString;
}
// Transient public wrapper around private property
public void setPubPrivString(String privString) {
setPrivString(privString);
}
// Transient public wrapper around private property
@Transient
public String getPubPrivString() {
return getPrivString();
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.property;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
@Entity
@Table(name="PRIV_TBL")
public class PrivAccessModsEntity {
private int id;
private String pubString;
private String protString;
private String privString;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setPubString(String pubString) {
this.pubString = pubString;
}
@Column(name="PUB_COL")
public String getPubString() {
return pubString;
}
protected void setProtString(String protString) {
this.protString = protString;
}
@Column(name="PROT_COL")
protected String getProtString() {
return protString;
}
private void setPrivString(String privString) {
this.privString = privString;
}
@Column(name="PRIV_COL")
private String getPrivString() {
return privString;
}
// Transient public wrapper around private property
public void setPubPrivString(String privString) {
setPrivString(privString);
}
// Transient public wrapper around private property
@Transient
public String getPubPrivString() {
return getPrivString();
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.property;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestAccessMods
extends SingleEMFTestCase {
public void setUp() {
setUp(DROP_TABLES, AccessModsEntity.class);
}
/**
* Verifies that by default, per JPA spec, non-transient public and
* protected properties should be persistent. Private should not be
* persistent.
*/
public void testAccessMods() {
EntityManager em = emf.createEntityManager();
AccessModsEntity ame = new AccessModsEntity();
// Set all properties
ame.setPubString("Public");
ame.setProtString("Protected");
ame.setPubPrivString("Private");
// Persist the entity. Public and protected properties should
// get persisted. Private should not.
em.getTransaction().begin();
em.persist(ame);
em.getTransaction().commit();
em.clear();
ame = em.find(AccessModsEntity.class, ame.getId());
assertNotNull(ame);
assertEquals("Public", ame.getPubString());
assertEquals("Protected", ame.getProtString());
assertNull(ame.getPubPrivString());
em.close();
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.property;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestCompatAccessMods extends SingleEMFTestCase {
@Override
public void setUp() {
setUp("openjpa.Compatibility", "PrivatePersistentProperties=true",
DROP_TABLES, PrivAccessModsEntity.class);
}
/**
* Verifies that with the PrivatePersistentProperties compatibility option
* enabled, non-transient private properties are persistent.
*
* Note: PrivAccessModsEntity must also be enhanced with the compatibility
* option set to true. This is currently handled in the test suite by
* a separate enhancement task for PrivAccessModsEntity.
*/
public void testAccessMods() {
EntityManager em = emf.createEntityManager();
PrivAccessModsEntity pame = new PrivAccessModsEntity();
// Set all properties
pame.setPubString("Public");
pame.setProtString("Protected");
pame.setPubPrivString("Private");
// Persist the entity. All properties should be persisted.
em.getTransaction().begin();
em.persist(pame);
em.getTransaction().commit();
em.clear();
pame = em.find(PrivAccessModsEntity.class, pame.getId());
assertNotNull(pame);
assertEquals("Public", pame.getPubString());
assertEquals("Protected", pame.getProtString());
assertEquals("Private", pame.getPubPrivString());
em.close();
}
}

View File

@ -305,7 +305,8 @@ public class PersistenceMetaDataDefaults
int access = determineExplicitAccessType(meta.getDescribedType());
if (!AccessCode.isUnknown(access))
return access;
access = determineImplicitAccessType(meta.getDescribedType());
access = determineImplicitAccessType(meta.getDescribedType(),
meta.getRepository().getConfiguration());
if (!AccessCode.isUnknown(access))
return access;
@ -331,7 +332,8 @@ public class PersistenceMetaDataDefaults
* Annotation can be placed on either fields or getters but not on both.
* If no field or getter is annotated then UNKNOWN access code is returned.
*/
private int determineImplicitAccessType(Class<?> cls) {
private int determineImplicitAccessType(Class<?> cls, OpenJPAConfiguration
conf) {
if (cls.isInterface()) // Managed interfaces
return AccessCode.PROPERTY;
Field[] allFields = AccessController.doPrivileged(J2DoPrivHelper.
@ -339,6 +341,15 @@ public class PersistenceMetaDataDefaults
Method[] methods = AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodsAction(cls));
List<Field> fields = filter(allFields, nonTransientFilter);
/*
* OpenJPA 1.x permitted private properties to be persistent. This is
* contrary to the JPA 1.0 specification, which states that persistent
* properties must be private or protected. OpenJPA 2.0+ will adhere
* to the specification by default, but provides a compatibility
* option to provide pre-2.0 behavior.
*/
getterFilter.setIncludePrivate(
conf.getCompatibilityInstance().getPrivatePersistentProperties());
List<Method> getters = filter(methods, getterFilter,
nonTransientFilter);
if (fields.isEmpty() && getters.isEmpty())
@ -550,9 +561,11 @@ public class PersistenceMetaDataDefaults
}
try {
// check for setters for methods
Method setter = meta.getDescribedType().getMethod(
"set" + StringUtils.capitalize(name),
new Class[] {((Method) member).getReturnType()});
Method setter = (Method) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodAction(
meta.getDescribedType(), "set" +
StringUtils.capitalize(name), new Class[] {
((Method) member).getReturnType() }));
if (setter == null && !isAnnotatedTransient(member)) {
logNoSetter(meta, name, null);
return false;
@ -716,8 +729,19 @@ public class PersistenceMetaDataDefaults
*
*/
static class GetterFilter implements InclusiveFilter<Method> {
private boolean includePrivate;
public boolean includes(Method method) {
return isGetter(method);
return isGetter(method, isIncludePrivate());
}
public void setIncludePrivate(boolean includePrivate) {
this.includePrivate = includePrivate;
}
public boolean isIncludePrivate() {
return includePrivate;
}
}

View File

@ -180,6 +180,7 @@ public class PersistenceProductDerivation
Compatibility compatibility = conf.getCompatibilityInstance();
compatibility.setFlushBeforeDetach(true);
compatibility.setCopyOnDetach(true);
compatibility.setPrivatePersistentProperties(true);
}
return true;
}

View File

@ -107,6 +107,28 @@
</programlisting>
</para>
</section>
<section id="private_persistent_properties">
<title>
Use of private persistent properties
</title>
<para>
In 1.x.x releases of OpenJPA, if property access was used,
private properties were considered persistent. This is
contrary to the JPA specification, which states that
persistent properties must be public or protected. In
OpenJPA 2.0 and later, private properties will not be
persistent by default.
</para>
<para>
It is possible to revert back to the 1.x.x behavior which
allowed private properties to be persistent by setting the
value of the <literal>openjpa.Compatibility</literal>
property <literal>PrivatePersistentProperties</literal> to
<literal>true</literal>. If compile time enhancement is
used, this property must be specified at the time of
enhancement and at runtime.
</para>
</section>
</section>
</section>
</appendix>