From e14af5d7abd79aebb8181d268d4b520ca920b904 Mon Sep 17 00:00:00 2001 From: Jeremy Bauer Date: Mon, 6 Feb 2012 22:03:02 +0000 Subject: [PATCH] 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 --- .../openjpa/conf/OpenJPAConfiguration.java | 32 +++ .../conf/OpenJPAConfigurationImpl.java | 18 ++ .../apache/openjpa/enhance/PCEnhancer.java | 194 ++++++++++++++++-- .../apache/openjpa/util/ApplicationIds.java | 8 + .../src/main/ant/enhancer.xml | 34 +-- .../apache/openjpa/enhance/ids/Device.java | 70 +++++++ .../apache/openjpa/enhance/ids/DeviceId.java | 67 ++++++ .../apache/openjpa/enhance/ids/Hardware.java | 65 ++++++ .../openjpa/enhance/ids/HardwareId.java | 67 ++++++ .../apache/openjpa/enhance/ids/Software.java | 68 ++++++ .../openjpa/enhance/ids/SoftwareId.java | 80 ++++++++ .../enhance/ids/TestOptimizeIdCopy.java | 122 +++++++++++ .../META-INF/optidcpy_persistence.xml | 38 ++++ 13 files changed, 839 insertions(+), 24 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Device.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/DeviceId.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Hardware.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/HardwareId.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Software.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/SoftwareId.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/TestOptimizeIdCopy.java create mode 100644 openjpa-persistence-jdbc/src/test/resources/META-INF/optidcpy_persistence.xml diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java index 670715bc2..99f82478b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java @@ -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); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java index 784e90b2c..f737c0aec 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java @@ -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()); + } + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java index 19df6d5bf..c181a5b26 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java @@ -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 type from conf'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. = pc.; 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 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, "", 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 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 pkFields = new ArrayList(); + // 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 pkfields, + FieldMetaData[] fmds) { + Project project = new Project(); + BCClass bc = project.loadClass(oidType); + BCMethod[] methods = bc.getDeclaredMethods(""); + 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; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java index 8db1372fa..4dacf43a5 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java @@ -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. */ diff --git a/openjpa-persistence-jdbc/src/main/ant/enhancer.xml b/openjpa-persistence-jdbc/src/main/ant/enhancer.xml index 8f54170ae..661b0dd3f 100644 --- a/openjpa-persistence-jdbc/src/main/ant/enhancer.xml +++ b/openjpa-persistence-jdbc/src/main/ant/enhancer.xml @@ -31,12 +31,12 @@ - - + + - - - + + + @@ -83,7 +83,8 @@ - + + @@ -99,17 +100,17 @@ - + - + - + @@ -132,15 +133,24 @@ - + - - + + + + + + + + + + + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Device.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Device.java new file mode 100644 index 000000000..19ab353d3 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Device.java @@ -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; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/DeviceId.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/DeviceId.java new file mode 100644 index 000000000..aa23ef450 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/DeviceId.java @@ -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(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Hardware.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Hardware.java new file mode 100644 index 000000000..7cd8c83dc --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Hardware.java @@ -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; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/HardwareId.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/HardwareId.java new file mode 100644 index 000000000..19156adba --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/HardwareId.java @@ -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(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Software.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Software.java new file mode 100644 index 000000000..f097c9169 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/Software.java @@ -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; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/SoftwareId.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/SoftwareId.java new file mode 100644 index 000000000..b34fc5d79 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/SoftwareId.java @@ -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(); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/TestOptimizeIdCopy.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/TestOptimizeIdCopy.java new file mode 100644 index 000000000..fa6a35ed9 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/ids/TestOptimizeIdCopy.java @@ -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 swq = em.createQuery("select sw from Software sw", Software.class); + List 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 hwq = em.createQuery("select hw from Hardware hw", Hardware.class); + List 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 dq = em.createQuery("select d from Device d", Device.class); + List 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(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/optidcpy_persistence.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/optidcpy_persistence.xml new file mode 100644 index 000000000..a59711f75 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/optidcpy_persistence.xml @@ -0,0 +1,38 @@ + + + + + + + This PU + + org.apache.openjpa.enhance.ids.Device + org.apache.openjpa.enhance.ids.Hardware + org.apache.openjpa.enhance.ids.Software + + + + + \ No newline at end of file