OPENJPA-2120 Add option for optimizing copy operations for qualifying id classes.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1241207 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jeremy Bauer 2012-02-06 22:03:02 +00:00
parent e8aaaccf9c
commit e14af5d7ab
13 changed files with 839 additions and 24 deletions

View File

@ -1918,5 +1918,37 @@ public interface OpenJPAConfiguration
*/
public void setPostLoadOnMerge(Boolean postLoadOnMerge);
/**
* Whether to attempt to optimize id class copy operations during the
* enhancement process. Optimization is only applicable for simple id classes
* that have a constructor with the proper construction parameters and
* direct assignments to fields within the id class during construction.
* If the optimization cannot occur, the enhancer will fallback to the
* noraml behavior.
* @since 2.2.0
*/
public boolean getOptimizeIdCopy();
/**
* Whether to attempt to optimize id class copy operations during the
* enhancement process. Optimization is only applicable for simple id classes
* that have a constructor with the proper construction parameters and
* direct assignments to fields within the id class during construction.
* If the optimization cannot occur, the enhancer will fallback to the
* normal behavior.
* @since 2.2.0
*/
public void setOptimizeIdCopy(boolean optimizeIds);
/**
* Whether to attempt to optimize id class copy operations during the
* enhancement process. Optimization is only applicable for simple id classes
* that have a constructor with the proper construction parameters and
* direct assignments to fields within the id class during construction.
* If the optimization cannot occur, the enhancer will fallback to the
* normal behavior.
* @since 2.2.0
*/
public void setOptimizeIdCopy(Boolean optimizeIds);
}

View File

@ -177,6 +177,7 @@ public class OpenJPAConfigurationImpl
public ObjectValue instrumentationManager;
public PluginListValue instrumentationProviders;
public BooleanValue postLoadOnMerge;
public BooleanValue optimizeIdCopy;
// custom values
public BrokerFactoryValue brokerFactoryPlugin;
@ -402,6 +403,10 @@ public class OpenJPAConfigurationImpl
postLoadOnMerge.setDefault("false");
postLoadOnMerge.set(false);
optimizeIdCopy = addBoolean("OptimizeIdCopy");
optimizeIdCopy.setDefault("false");
optimizeIdCopy.set(false);
autoClear = addInt("AutoClear");
aliases =
new String[] { "datastore",
@ -1856,5 +1861,18 @@ public class OpenJPAConfigurationImpl
setPostLoadOnMerge(postLoadOnMerge.booleanValue());
}
public boolean getOptimizeIdCopy() {
return optimizeIdCopy.get();
}
public void setOptimizeIdCopy(boolean optimizeId) {
optimizeIdCopy.set(optimizeId);
}
public void setOptimizeIdCopy(Boolean optimizeId) {
if (optimizeId != null) {
setOptimizeIdCopy(optimizeId.booleanValue());
}
}
}

View File

@ -69,6 +69,7 @@ import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.GeneralException;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.BigDecimalId;
@ -102,6 +103,7 @@ import serp.bytecode.LoadInstruction;
import serp.bytecode.LookupSwitchInstruction;
import serp.bytecode.MethodInstruction;
import serp.bytecode.Project;
import serp.bytecode.PutFieldInstruction;
import serp.bytecode.TableSwitchInstruction;
import serp.bytecode.ClassInstruction;
import serp.util.Strings;
@ -212,6 +214,8 @@ public class PCEnhancer {
private boolean _isAlreadySubclassed = false;
private boolean _bcsConfigured = false;
private boolean _optimizeIdCopy = false; // whether to attempt optimizing id copy
/**
* Constructor. Supply configuration and type to enhance. This will look
* up the metadata for <code>type</code> from <code>conf</code>'s
@ -280,6 +284,8 @@ public class PCEnhancer {
} else
_repos = repos;
_meta = _repos.getMetaData(type.getType(), loader, false);
configureOptimizeIdCopy();
}
/**
@ -2010,11 +2016,52 @@ public class PCEnhancer {
// id.<field> = pc.<field>;
FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
: _meta.getDeclaredFields();
Class type;
Class<?> type;
String name;
Field field;
Method setter;
boolean reflect;
// If optimizeIdCopy is enabled and not a field manager method, try to
// optimize the copyTo by using a public constructor instead of reflection
if (_optimizeIdCopy && !fieldManager) {
ArrayList<Integer> pkfields = optimizeIdCopy(oidType, fmds);
if (pkfields != null) {
// search for a constructor on the IdClass that can be used
// to construct the IdClass
int parmOrder[] = getIdClassConstructorParmOrder(oidType, pkfields, fmds);
if (parmOrder != null) {
// found a matching constructor. parm array is constructor parm order
code.anew().setType(oidType);
code.dup();
// build the parm list in order
Class<?>[] clsArgs = new Class<?>[parmOrder.length];
for (int i = 0; i < clsArgs.length; i++) {
int parmIndex = parmOrder[i];
clsArgs[i] = fmds[parmIndex].getObjectIdFieldType();
loadManagedInstance(code, false);
addGetManagedValueCode(code, fmds[parmIndex]);
}
// invoke the public constructor to create a new local id
code.invokespecial().setMethod(oidType, "<init>", void.class, clsArgs);
int ret = code.getNextLocalsIndex();
code.astore().setLocal(ret);
// swap out the app id with the new one
code.aload().setLocal(1);
code.checkcast().setType(ObjectId.class);
code.aload().setLocal(ret);
code.invokestatic().setMethod(ApplicationIds.class,
"setAppId", void.class, new Class[] { ObjectId.class,
Object.class });
code.vreturn();
code.calculateMaxStack();
code.calculateMaxLocals();
return;
}
}
}
for (int i = 0; i < fmds.length; i++) {
if (!fmds[i].isPrimaryKey())
continue;
@ -2428,17 +2475,28 @@ public class PCEnhancer {
if (Modifier.isPublic(field.getModifiers()))
code.getfield().setField(field);
else {
// Reflection.getXXX(oid, Reflection.findField(...));
code.classconstant().setClass(oidType);
code.constant().setValue(name);
code.constant().setValue(true);
code.invokestatic().setMethod(Reflection.class,
"findField", Field.class, new Class[] {
Class.class, String.class, boolean.class });
code.invokestatic().setMethod
(getReflectionGetterMethod(type, Field.class));
if (!type.isPrimitive() && type != Object.class)
code.checkcast().setType(type);
boolean usedFastOid = false;
if (_optimizeIdCopy) {
// If fastOids, ignore access type and try to use a public getter
getter = Reflection.findGetter(oidType, name, false);
if (getter != null && Modifier.isPublic(getter.getModifiers())) {
usedFastOid = true;
code.invokevirtual().setMethod(getter);
}
}
if (!usedFastOid) {
// Reflection.getXXX(oid, Reflection.findField(...));
code.classconstant().setClass(oidType);
code.constant().setValue(name);
code.constant().setValue(true);
code.invokestatic().setMethod(Reflection.class,
"findField", Field.class, new Class[] {
Class.class, String.class, boolean.class });
code.invokestatic().setMethod
(getReflectionGetterMethod(type, Field.class));
if (!type.isPrimitive() && type != Object.class)
code.checkcast().setType(type);
}
}
} else {
getter = Reflection.findGetter(oidType, name, true);
@ -4823,4 +4881,116 @@ public class PCEnhancer {
}
return false;
}
/**
* Read the optimizedIdCopy value from the config (if available)
*/
private void configureOptimizeIdCopy() {
if (_repos != null && _repos.getConfiguration() != null) {
_optimizeIdCopy = _repos.getConfiguration().getOptimizeIdCopy();
}
}
/*
* Cycles through all primary keys verifying whether they can and should
* be used for faster oid copy. The field must be private and must
* not have a public setter. If this is the case, the list of pk fields is
* returned. If not, returns null.
*/
private ArrayList<Integer> optimizeIdCopy(Class<?> oidType, FieldMetaData[] fmds) {
// collect all object id fields and verify they
// a) have a private field
// b) do not have a public setter
ArrayList<Integer> pkFields = new ArrayList<Integer>();
// build list of primary key fields
for (int i = 0; i < fmds.length; i++) {
if (!fmds[i].isPrimaryKey())
continue;
// optimizing copy with PC type not (yet) supported
if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC) {
return null;
}
String name = fmds[i].getName();
Field fld = Reflection.findField(oidType, name, false);
if (fld == null || Modifier.isPublic(fld.getModifiers())) {
return null;
}
Method setter = Reflection.findSetter(oidType, name, false);
if (setter == null || !Modifier.isPublic(setter.getModifiers())) {
pkFields.add(i);
} else {
return null;
}
}
return pkFields.size() > 0 ? pkFields : null;
}
/*
* Cycles through all constructors of an IdClass and examines the instructions to find
* a matching constructor for the provided pk fields. If a match is found, it returns
* the order (relative to the field metadata) of the constructor parameters. If a match
* is not found, returns null.
*/
private int[] getIdClassConstructorParmOrder(Class<?> oidType, ArrayList<Integer> pkfields,
FieldMetaData[] fmds) {
Project project = new Project();
BCClass bc = project.loadClass(oidType);
BCMethod[] methods = bc.getDeclaredMethods("<init>");
if (methods == null || methods.length == 0) {
return null;
}
int parmOrder[] = new int[pkfields.size()];
for (BCMethod method : methods) {
// constructor must be public
if (!method.isPublic()) {
continue;
}
Class<?>[] parmTypes = method.getParamTypes();
// make sure the constructors have the same # of parms as
// the number of pk fields
if (parmTypes.length != pkfields.size()) {
continue;
}
int parmOrderIndex = 0;
Code code = method.getCode(false);
Instruction[] ins = code.getInstructions();
for (int i = 0; i < ins.length; i++) {
if (ins[i] instanceof PutFieldInstruction) {
PutFieldInstruction pfi = (PutFieldInstruction)ins[i];
for (int j = 0; j < pkfields.size(); j++) {
int fieldNum = pkfields.get(j);
// Compare the field being set with the current pk field
String parmName = fmds[fieldNum].getName();
Class<?> parmType = fmds[fieldNum].getType();
if (parmName.equals(pfi.getFieldName())) {
// backup and examine the load instruction parm
if (i > 0 && ins[i-1] instanceof LoadInstruction) {
LoadInstruction li = (LoadInstruction)ins[i-1];
// Get the local index from the instruction. This will be the index
// of the constructor parameter. must be less than or equal to the
// max parm index to prevent from picking up locals that could have
// been produced within the constructor. Also make sure the parm type
// matches the fmd type
int parm = li.getLocal();
if (parm <= pkfields.size() && parmTypes[parm-1].equals(parmType)) {
parmOrder[parmOrderIndex] = fieldNum;
parmOrderIndex++;
}
} else {
// Some other instruction found. can't make a determination of which local/parm
// is being used on the putfield.
break;
}
}
}
}
}
if (parmOrderIndex == pkfields.size()) {
return parmOrder;
}
}
return null;
}
}

View File

@ -575,6 +575,14 @@ public class ApplicationIds {
}
/**
* Sets the underlying id of an ObjectId. Should only
* be used with simple (idclass) types.
*/
public static void setAppId(ObjectId id, Object newId) {
id.setId(newId);
}
/**
* Helper class used to transfer pk values to/from application oids.
*/

View File

@ -31,12 +31,12 @@
<istrue value="${maven.test.skip}" />
</condition>
<condition property="skip.enhance">
<or>
<condition property="skip.enhance">
<or>
<equals arg1="${test}" arg2="false" />
<equals arg1="${build.enhance}" arg2="false" />
<istrue value="${maven.test.skip}" />
<istrue value="${skipTests}" />
<equals arg1="${build.enhance}" arg2="false" />
<istrue value="${maven.test.skip}" />
<istrue value="${skipTests}" />
</or>
</condition>
@ -83,7 +83,8 @@
<exclude name="org/apache/openjpa/enhance/EnhancedSubClass.class"/>
<exclude name="**/AbstractUnenhanced*.class" />
<exclude name="**/unenhanced/*.class" />
<exclude name="**/persistence/property/AccessModsEntity.class"/>
<exclude name="**/persistence/property/AccessModsEntity.class"/>
<exclude name="org/apache/openjpa/enhance/ids/*.class"/>
</fileset>
<openjpac>
<classpath refid="cp" />
@ -99,17 +100,17 @@
<include name="**/detach/serializable/*.class" />
<!-- include files from orm.xml -->
<include name="**/xml/*.class" />
<exclude name="**/persistence/delimited/identifiers/xml/*.class"/>
<exclude name="**/persistence/delimited/identifiers/xml/*.class"/>
<exclude name="**/Test*.class" />
</fileset>
<config log="${openjpa.Log}" />
</openjpac>
<!-- Enhance with private persistent properties compatibility option -->
<!-- Enhance with private persistent properties compatibility option -->
<openjpac>
<config propertiesFile="${project.build.testOutputDirectory}/META-INF/nopriv_persistence.xml" />
<classpath refid="cp" />
<fileset dir="${project.build.testOutputDirectory}">
<include name="**/persistence/property/AccessModsEntity.class"/>
<include name="**/persistence/property/AccessModsEntity.class"/>
</fileset>
<config log="${openjpa.Log}" />
</openjpac>
@ -132,15 +133,24 @@
</fileset>
<config log="${openjpa.Log}" />
</openjpac>
<!-- Enhance delimited identifiers XML-based entities separately -->
<!-- Enhance delimited identifiers XML-based entities separately -->
<openjpac>
<config propertiesFile="${project.build.testOutputDirectory}/META-INF/delim_persistence.xml" />
<classpath refid="cp" />
<fileset dir="${project.build.testOutputDirectory}">
<include name="**/persistence/delimited/identifiers/xml/*.class"/>
<exclude name="**/persistence/delimited/identifiers/xml/Test*.class"/>
<include name="**/persistence/delimited/identifiers/xml/*.class"/>
<exclude name="**/persistence/delimited/identifiers/xml/Test*.class"/>
</fileset>
<config log="${openjpa.Log}"/>
</openjpac>
<!-- Enhance with optimized id copy option -->
<openjpac>
<config propertiesFile="${project.build.testOutputDirectory}/META-INF/optidcpy_persistence.xml" />
<classpath refid="cp" />
<fileset dir="${project.build.testOutputDirectory}">
<include name="org/apache/openjpa/enhance/ids/*.class"/>
</fileset>
<config log="${openjpa.Log}" />
</openjpac>
</target>
</project>

View File

@ -0,0 +1,70 @@
/*
* 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.enhance.ids;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
@Entity
@Table(name="ID_DEVTBL")
@IdClass(DeviceId.class)
public class Device {
@Id
@Column(name="DEV_ID")
private int id;
@Id
@Column(name="DEV_TYPE")
private int type;
@Column(name="DEV_DESC")
private String description;
public Device() {
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setType(int type) {
this.type = type;
}
public int getType() {
return type;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.enhance.ids;
public class DeviceId {
public static boolean[] usedConstructor = new boolean[3];
private int id;
private int type;
public DeviceId() {
usedConstructor[0] = true;
}
@SuppressWarnings("unused")
private DeviceId(int i, int t) {
usedConstructor[1] = true;
id = i;
type = t;
}
public DeviceId(int i) {
usedConstructor[2] = true;
id = i;
}
public int getId() {
return id;
}
public int getType() {
return type;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DeviceId) {
DeviceId did = (DeviceId)obj;
return did.getId() == getId() &&
did.getType() == getType();
}
return false;
}
@Override
public int hashCode() {
return getId() + getType();
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.enhance.ids;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
@Table(name="ID_HWTBL")
@IdClass(HardwareId.class)
@Entity
public class Hardware {
@Id
private String serial;
@Id
private String model;
private String description;
public Hardware() {
}
public void setSerial(String serial) {
this.serial = serial;
}
public String getSerial() {
return serial;
}
public void setModel(String model) {
this.model = model;
}
public String getModel() {
return model;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.enhance.ids;
public class HardwareId {
public static boolean[] usedConstructor = new boolean[2];
private String serial;
private String model;
public HardwareId() {
}
// Parms out of order
public HardwareId(String model, String serial) {
usedConstructor[0] = true;
this.serial = serial;
this.model = model;
}
public HardwareId(String model,int serial) {
usedConstructor[1] = true;
this.model = model;
this.serial = Integer.toString(serial);
}
public String getSerial() {
return serial;
}
public String getModel() {
return model;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof HardwareId) {
HardwareId hid = (HardwareId)obj;
return hid.getModel().equals(getModel()) &&
hid.getSerial().equals(getSerial());
}
return false;
}
@Override
public int hashCode() {
return getSerial().hashCode() + getModel().hashCode();
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.enhance.ids;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
@Entity
@Table(name="ID_SWTBL")
@IdClass(SoftwareId.class)
public class Software {
@Id
private Integer idInteger;
@Id
private int idInt;
@Id
private String idString;
public Software() {
}
public void setIdInteger(Integer idInteger) {
this.idInteger = idInteger;
}
public Integer getIdInteger() {
return idInteger;
}
public void setIdInt(int idInt) {
this.idInt = idInt;
}
public int getIdInt() {
return idInt;
}
public void setIdString(String idString) {
this.idString = idString;
}
public String getIdString() {
return idString;
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.enhance.ids;
public class SoftwareId {
public static boolean[] usedConstructor = new boolean[3];
private Integer idInteger;
private int idInt;
private String idString;
public SoftwareId() {
}
public SoftwareId(int idint) {
usedConstructor[0] = true;
idInt = idint;
}
public SoftwareId(Integer idinteger, int idint) {
usedConstructor[1] = true;
idInteger = idinteger;
idInt = idint;
}
public SoftwareId(Integer idinteger, int idint, String idstring) {
usedConstructor[2] = true;
idInteger = idinteger;
idInt = idint;
idString =idstring;
}
public Integer getIdInteger() {
return idInteger;
}
public int getIdInt() {
return idInt;
}
public String getIdString() {
return idString;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SoftwareId) {
SoftwareId swid = (SoftwareId)obj;
return swid.getIdInt() == getIdInt() &&
swid.getIdInteger().equals(getIdInteger()) &&
swid.getIdString().equals(getIdString());
}
return false;
}
@Override
public int hashCode() {
return getIdInt() + getIdInteger().hashCode() + getIdString().hashCode();
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.enhance.ids;
import java.util.List;
import java.util.Random;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
* Verifies the use of the openjpa.OptimizeIdCopy configuration parameter. This parameter
* changes the behavior of the enhancer and thus, must be set before enhancement occurs. There
* is special enhancement task in main/ant/enhancer.xml to ensure this value is set
* during the enhancement process for the entities used by this test.
*/
public class TestOptimizeIdCopy extends SingleEMFTestCase {
@Override
public void setUp() {
setUp(Device.class,Hardware.class,Software.class, CLEAR_TABLES);
}
/*
* Verifies that constructor-based Id optimization occurs during Id copy. Asserts
* only the proper/expected public constructor is called during the id copy operation.
*/
public void testIdOptimization() {
EntityManager em = emf.createEntityManager();
// Add a software entity
Software sw = new Software();
int id = new Random().nextInt();
sw.setIdInt(id);
sw.setIdInteger(10);
sw.setIdString("StringIdVal");
em.getTransaction().begin();
em.persist(sw);
em.getTransaction().commit();
em.clear();
TypedQuery<Software> swq = em.createQuery("select sw from Software sw", Software.class);
List<Software> swl = swq.getResultList();
assertTrue("Software result list > 0", swl.size() > 0);
// Id copy optimization should have used the 3rd constructor
assertFalse("First constructor was not used", SoftwareId.usedConstructor[0]);
assertFalse("Second constructor was not used", SoftwareId.usedConstructor[1]);
assertTrue("Third (correct) constructor was used", SoftwareId.usedConstructor[2]);
em.close();
}
/*
* Verifies that constructor based optimization functions even if parms
* are different than field order
*/
public void testIdOptimizationConstructorOutOfOrder() {
EntityManager em = emf.createEntityManager();
Hardware hw = new Hardware();
String id = "Model" + (new Random().nextInt());
hw.setModel("Model" + id);
hw.setSerial("123XYZ");
em.getTransaction().begin();
em.persist(hw);
em.getTransaction().commit();
em.clear();
TypedQuery<Hardware> hwq = em.createQuery("select hw from Hardware hw", Hardware.class);
List<Hardware> hwl = hwq.getResultList();
assertTrue("Hardware result list > 0", hwl.size() > 0);
// Id copy optimization should have used the first constructor
assertTrue("First (correct) constructor was used", HardwareId.usedConstructor[0]);
assertFalse("Second constructor was not used", HardwareId.usedConstructor[1]);
em.close();
}
/*
* Verifies that classes without a proper constructor do not get optimized
*/
public void testNoOptimization() {
EntityManager em = emf.createEntityManager();
int id = new Random().nextInt();
Device d = new Device();
d.setId(id);
d.setType(10);
em.getTransaction().begin();
em.persist(d);
em.getTransaction().commit();
em.clear();
TypedQuery<Device> dq = em.createQuery("select d from Device d", Device.class);
List<Device> dl = dq.getResultList();
assertTrue("Device result list > 0", dl.size() > 0);
// Only the first, default constructor should have been called
assertTrue("First (default) constructor was used", DeviceId.usedConstructor[0]);
assertFalse("Second constructor was not used", DeviceId.usedConstructor[1]);
assertFalse("Third constructor was not used", DeviceId.usedConstructor[2]);
em.close();
}
}

View File

@ -0,0 +1,38 @@
<?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.
-->
<persistence
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0" >
<persistence-unit name="OptimizeIdCopy_PU">
<description>
This PU
</description>
<class>org.apache.openjpa.enhance.ids.Device</class>
<class>org.apache.openjpa.enhance.ids.Hardware</class>
<class>org.apache.openjpa.enhance.ids.Software</class>
<properties>
<property name="openjpa.OptimizeIdCopy" value="true"/>
</properties>
</persistence-unit>
</persistence>