HHH-8354 - New dirty-checking options based on bytecode enhancement

Conflicts:
	hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/Enhancer.java
	hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancerConstants.java
	hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java
	hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java
	hibernate-gradle-plugin/src/main/groovy/org/hibernate/tooling/gradle/EnhancerTask.groovy
	hibernate-maven-plugin/src/main/java/org/hibernate/bytecode/enhance/plugins/HibernateEnhancementMojo.java
This commit is contained in:
Ståle W. Pedersen 2013-10-17 18:21:29 -04:00 committed by Brett Meyer
parent 851bdbbcc3
commit 32a5633b35
28 changed files with 2513 additions and 176 deletions

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.bytecode.enhance.spi;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class CollectionTracker {
private Map<String, Integer> tracker;
public CollectionTracker() {
tracker = new HashMap<String, Integer>();
}
public void add(String name, int size) {
tracker.put(name, size);
}
public int getSize(String name) {
Integer size = tracker.get(name);
if(size == null)
return -1;
else
return size;
}
}

View File

@ -0,0 +1,85 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.bytecode.enhance.spi;
import org.hibernate.engine.spi.CompositeOwner;
/**
* small low memory class to keep references to composite owners
*
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class CompositeOwnerTracker {
private String[] names;
private CompositeOwner[] owners;
private int size = 0;
public CompositeOwnerTracker() {
names = new String[1];
owners = new CompositeOwner[1];
}
public void add(String name, CompositeOwner owner) {
for(int i=0; i<size;i++) {
if(names[i].equals(name)) {
owners[i] = owner;
return;
}
}
if ( size >= names.length) {
String[] tmpNames = new String[size+1];
System.arraycopy(names, 0, tmpNames, 0, size);
names = tmpNames;
CompositeOwner[] tmpOwners = new CompositeOwner[size+1];
System.arraycopy(owners, 0, tmpOwners, 0, size);
owners = tmpOwners;
}
names[size] = name;
owners[size] = owner;
size++;
}
public void callOwner(String fieldName) {
for(int i=0; i < size;i++) {
owners[i].$$_hibernate_trackChange(names[i]+fieldName);
}
}
public void removeOwner(String name) {
for(int i=0; i<size;i++) {
if(names[i].equals(name)) {
if(i < size) {
for(int j=i; j < size-1;j++) {
names[j] = names[j+1];
owners[j] = owners[j+1];
}
names[size-1] = null;
owners[size-1] = null;
size--;
}
}
}
}
}

View File

@ -95,4 +95,11 @@ public interface EnhancementContext {
public CtField[] order(CtField[] persistentFields);
public boolean isLazyLoadable(CtField field);
/**
*
* @param field the field to check
* @return {@code true} if the field is mapped
*/
public boolean isMappedCollection(CtField field);
}

View File

@ -23,16 +23,13 @@
*/
package org.hibernate.bytecode.enhance.spi;
import javax.persistence.Transient;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javassist.CannotCompileException;
import javassist.ClassPool;
@ -51,11 +48,17 @@
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.StackMapTable;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.stackmap.MapMaker;
import org.jboss.logging.Logger;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.enhance.EnhancementException;
@ -64,8 +67,9 @@
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.PersistentClass;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
@ -74,27 +78,6 @@
public class Enhancer {
private static final CoreMessageLogger log = Logger.getMessageLogger( CoreMessageLogger.class, Enhancer.class.getName() );
public static final String PERSISTENT_FIELD_READER_PREFIX = "$$_hibernate_read_";
public static final String PERSISTENT_FIELD_WRITER_PREFIX = "$$_hibernate_write_";
public static final String ENTITY_INSTANCE_GETTER_NAME = "$$_hibernate_getEntityInstance";
public static final String ENTITY_ENTRY_FIELD_NAME = "$$_hibernate_entityEntryHolder";
public static final String ENTITY_ENTRY_GETTER_NAME = "$$_hibernate_getEntityEntry";
public static final String ENTITY_ENTRY_SETTER_NAME = "$$_hibernate_setEntityEntry";
public static final String PREVIOUS_FIELD_NAME = "$$_hibernate_previousManagedEntity";
public static final String PREVIOUS_GETTER_NAME = "$$_hibernate_getPreviousManagedEntity";
public static final String PREVIOUS_SETTER_NAME = "$$_hibernate_setPreviousManagedEntity";
public static final String NEXT_FIELD_NAME = "$$_hibernate_nextManagedEntity";
public static final String NEXT_GETTER_NAME = "$$_hibernate_getNextManagedEntity";
public static final String NEXT_SETTER_NAME = "$$_hibernate_setNextManagedEntity";
public static final String INTERCEPTOR_FIELD_NAME = "$$_hibernate_attributeInterceptor";
public static final String INTERCEPTOR_GETTER_NAME = "$$_hibernate_getInterceptor";
public static final String INTERCEPTOR_SETTER_NAME = "$$_hibernate_setInterceptor";
private final EnhancementContext enhancementContext;
private final ClassPool classPool;
@ -104,7 +87,14 @@ public class Enhancer {
private final CtClass attributeInterceptableCtClass;
private final CtClass entityEntryCtClass;
private final CtClass objectCtClass;
private boolean isComposite;
/**
* Constructs the Enhancer, using the given context.
*
* @param enhancementContext Describes the context in which enhancement will occur so as to give access
* to contextual/environmental information.
*/
public Enhancer(EnhancementContext enhancementContext) {
this.enhancementContext = enhancementContext;
this.classPool = buildClassPool( enhancementContext );
@ -183,32 +173,52 @@ public byte[] enhance(String className, byte[] originalBytes) throws Enhancement
return originalBytes;
}
enhance( managedCtClass );
enhance(managedCtClass, false);
DataOutputStream out = null;
return getByteCode(managedCtClass);
}
public byte[] enhanceComposite(String className, byte[] originalBytes) throws EnhancementException {
final CtClass managedCtClass;
try {
managedCtClass = classPool.makeClassIfNew( new ByteArrayInputStream( originalBytes ) );
}
catch (IOException e) {
log.unableToBuildEnhancementMetamodel(className);
return originalBytes;
}
enhance(managedCtClass, true);
return getByteCode(managedCtClass);
}
private byte[] getByteCode(CtClass managedCtClass) {
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
final DataOutputStream out;
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
out = new DataOutputStream( byteStream );
try {
managedCtClass.toBytecode( out );
return byteStream.toByteArray();
}
catch (Exception e) {
log.unableToTransformClass( e.getMessage() );
throw new HibernateException( "Unable to transform class: " + e.getMessage() );
}
finally {
try {
if ( out != null ) {
out.close();
}
}
catch (IOException e) {
//swallow
}
}
}
catch (Exception e) {
log.unableToTransformClass( e.getMessage() );
throw new HibernateException( "Unable to transform class: " + e.getMessage() );
}
}
private void enhance(CtClass managedCtClass) {
private void enhance(CtClass managedCtClass, boolean isComposite) {
this.isComposite = isComposite;
final String className = managedCtClass.getName();
log.debugf( "Enhancing %s", className );
@ -228,10 +238,10 @@ private void enhance(CtClass managedCtClass) {
}
}
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
if (!isComposite && enhancementContext.isEntityClass( managedCtClass ) ) {
enhanceAsEntity( managedCtClass );
}
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
else if (isComposite || enhancementContext.isCompositeClass( managedCtClass ) ) {
enhanceAsComposite( managedCtClass );
}
else {
@ -261,7 +271,7 @@ private void addEntityInstanceHandling(CtClass managedCtClass) {
managedCtClass.addMethod(
CtNewMethod.make(
objectCtClass,
ENTITY_INSTANCE_GETTER_NAME,
EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME,
new CtClass[0],
new CtClass[0],
"{ return this; }",
@ -284,9 +294,9 @@ private void addEntityEntryHandling(CtClass managedCtClass) {
addFieldWithGetterAndSetter(
managedCtClass,
entityEntryCtClass,
ENTITY_ENTRY_FIELD_NAME,
ENTITY_ENTRY_GETTER_NAME,
ENTITY_ENTRY_SETTER_NAME
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME
);
}
@ -294,9 +304,9 @@ private void addLinkedPreviousHandling(CtClass managedCtClass) {
addFieldWithGetterAndSetter(
managedCtClass,
managedEntityCtClass,
PREVIOUS_FIELD_NAME,
PREVIOUS_GETTER_NAME,
PREVIOUS_SETTER_NAME
EnhancerConstants.PREVIOUS_FIELD_NAME,
EnhancerConstants.PREVIOUS_GETTER_NAME,
EnhancerConstants.PREVIOUS_SETTER_NAME
);
}
@ -304,9 +314,9 @@ private void addLinkedNextHandling(CtClass managedCtClass) {
addFieldWithGetterAndSetter(
managedCtClass,
managedEntityCtClass,
NEXT_FIELD_NAME,
NEXT_GETTER_NAME,
NEXT_SETTER_NAME
EnhancerConstants.NEXT_FIELD_NAME,
EnhancerConstants.NEXT_GETTER_NAME,
EnhancerConstants.NEXT_SETTER_NAME
);
}
@ -382,6 +392,34 @@ private CtField[] collectPersistentFields(CtClass managedCtClass) {
return enhancementContext.order( persistentFieldList.toArray( new CtField[persistentFieldList.size()]) );
}
private List<CtField> collectCollectionFields(CtClass managedCtClass) {
final List<CtField> collectionList = new ArrayList<CtField>();
try {
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
// skip static fields
if ( Modifier.isStatic( ctField.getModifiers() ) ) {
continue;
}
// skip fields added by enhancement
if ( ctField.getName().startsWith( "$" ) ) {
continue;
}
if ( enhancementContext.isPersistentField( ctField ) ) {
for(CtClass ctClass : ctField.getType().getInterfaces()) {
if(ctClass.getName().equals("java.util.Collection")) {
collectionList.add(ctField);
break;
}
}
}
}
}
catch (NotFoundException ignored) { }
return collectionList;
}
private void addInterceptorHandling(CtClass managedCtClass) {
// interceptor handling is only needed if either:
// a) in-line dirty checking has *not* been requested
@ -400,14 +438,271 @@ private void addInterceptorHandling(CtClass managedCtClass) {
addFieldWithGetterAndSetter(
managedCtClass,
attributeInterceptorCtClass,
INTERCEPTOR_FIELD_NAME,
INTERCEPTOR_GETTER_NAME,
INTERCEPTOR_SETTER_NAME
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
EnhancerConstants.INTERCEPTOR_SETTER_NAME
);
}
private boolean isClassAlreadyTrackingDirtyStatus(CtClass managedCtClass) {
try {
for(CtClass ctInterface : managedCtClass.getInterfaces()) {
if(ctInterface.getName().equals(SelfDirtinessTracker.class.getName()))
return true;
}
}
catch (NotFoundException e) {
e.printStackTrace();
}
return false;
}
private void addInLineDirtyHandling(CtClass managedCtClass ) {
// todo : implement
try {
//create composite methods
if(isComposite) {
managedCtClass.addInterface(classPool.get("org.hibernate.engine.spi.CompositeTracker"));
CtClass compositeCtType = classPool.get("org.hibernate.bytecode.enhance.spi.CompositeOwnerTracker");
addField(managedCtClass, compositeCtType, EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME, true);
createCompositeTrackerMethod(managedCtClass);
}
// "normal" entity
else {
managedCtClass.addInterface(classPool.get("org.hibernate.engine.spi.SelfDirtinessTracker"));
CtClass trackerCtType = classPool.get("java.util.Set");
addField(managedCtClass, trackerCtType, EnhancerConstants.TRACKER_FIELD_NAME, true);
CtClass collectionTrackerCtType = classPool.get("org.hibernate.bytecode.enhance.spi.CollectionTracker");
addField(managedCtClass, collectionTrackerCtType, EnhancerConstants.TRACKER_COLLECTION_NAME, true);
createDirtyTrackerMethods(managedCtClass);
}
}
catch (NotFoundException e) {
e.printStackTrace();
}
}
/**
* Create all dirty tracker methods
*/
private void createDirtyTrackerMethods(CtClass managedCtClass) {
try {
String trackerChangeMethod =
"public void "+EnhancerConstants.TRACKER_CHANGER_NAME+"(String name) {" +
" if(" +EnhancerConstants.TRACKER_FIELD_NAME+ " == null) {" +
" "+EnhancerConstants.TRACKER_FIELD_NAME+ " = new java.util.HashSet();" +
" }" +
" if(!" +EnhancerConstants.TRACKER_FIELD_NAME+ ".contains(name)) {" +
" "+EnhancerConstants.TRACKER_FIELD_NAME+".add(name);" +
" }"+
"}";
managedCtClass.addMethod(CtNewMethod.make(trackerChangeMethod, managedCtClass));
createCollectionDirtyCheckMethod(managedCtClass);
createCollectionDirtyCheckGetFieldsMethod(managedCtClass);
//createCompositeFieldsDirtyCheckMethod(managedCtClass);
//createGetCompositeDirtyFieldsMethod(managedCtClass);
createHasDirtyAttributesMethod(managedCtClass);
createClearDirtyCollectionMethod(managedCtClass);
createClearDirtyMethod(managedCtClass);
String trackerGetMethod =
"public java.util.List "+EnhancerConstants.TRACKER_GET_NAME+"() { "+
"if("+ EnhancerConstants.TRACKER_FIELD_NAME+" == null) "+
EnhancerConstants.TRACKER_FIELD_NAME+" = new java.util.HashSet();"+
EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME+"("+
EnhancerConstants.TRACKER_FIELD_NAME+");"+
"return "+EnhancerConstants.TRACKER_FIELD_NAME+"; }";
CtMethod getMethod = CtNewMethod.make(trackerGetMethod, managedCtClass);
MethodInfo methodInfo = getMethod.getMethodInfo();
SignatureAttribute signatureAttribute =
new SignatureAttribute(methodInfo.getConstPool(), "()Ljava/util/Set<Ljava/lang/String;>;");
methodInfo.addAttribute(signatureAttribute);
managedCtClass.addMethod(getMethod);
}
catch (CannotCompileException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void createTrackChangeCompositeMethod(CtClass managedCtClass) {
StringBuilder builder = new StringBuilder();
builder.append("public void ")
.append(EnhancerConstants.TRACKER_CHANGER_NAME)
.append("(String name) {")
.append("if (")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(" != null) ")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(".callOwner(\".\"+name); }");
System.out.println("COMPOSITE METHOD: "+builder.toString());
try {
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
}
catch (CannotCompileException e) {
}
}
private void createCompositeTrackerMethod(CtClass managedCtClass) {
try {
StringBuilder builder = new StringBuilder();
builder.append("public void ")
.append(EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER)
.append("(String name, org.hibernate.engine.spi.CompositeOwner tracker) {")
.append("if(")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(" == null) ")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(" = new org.hibernate.bytecode.enhance.spi.CompositeOwnerTracker();")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(".add(name, tracker); }");
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
builder = new StringBuilder();
builder.append("public void ")
.append(EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER)
.append("(String name) {")
.append(" if(")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(" != null)")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(".removeOwner(name);}");
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
}
catch (CannotCompileException e) {
e.printStackTrace();
}
}
private void createHasDirtyAttributesMethod(CtClass managedCtClass) throws CannotCompileException {
String trackerHasChangedMethod =
"public boolean "+EnhancerConstants.TRACKER_HAS_CHANGED_NAME+"() { return ("+
EnhancerConstants.TRACKER_FIELD_NAME+" != null && !" +
EnhancerConstants.TRACKER_FIELD_NAME+".isEmpty()) || "+
EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME+"(); } ";
managedCtClass.addMethod(CtNewMethod.make(trackerHasChangedMethod, managedCtClass));
}
/**
* Creates _clearDirtyAttributes
*/
private void createClearDirtyMethod(CtClass managedCtClass) throws CannotCompileException, ClassNotFoundException {
StringBuilder builder = new StringBuilder();
builder.append("public void ")
.append( EnhancerConstants.TRACKER_CLEAR_NAME)
.append("() {")
.append("if (")
.append(EnhancerConstants.TRACKER_FIELD_NAME)
.append(" != null) ")
.append(EnhancerConstants.TRACKER_FIELD_NAME)
.append(".clear(); ")
.append(EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME)
.append("(); }");
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
}
private void createClearDirtyCollectionMethod(CtClass managedCtClass) throws CannotCompileException {
StringBuilder builder = new StringBuilder();
builder.append("private void ")
.append(EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME)
.append("() { if(")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(" == null)")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(" = new org.hibernate.bytecode.enhance.spi.CollectionTracker();");
for(CtField ctField : collectCollectionFields(managedCtClass)) {
if(!enhancementContext.isMappedCollection(ctField)) {
builder.append("if(")
.append(ctField.getName())
.append(" != null) ")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(".add(\"")
.append(ctField.getName())
.append("\", ")
.append(ctField.getName())
.append(".size());");
}
}
builder.append("}");
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
}
/**
* create _areCollectionFieldsDirty
*/
private void createCollectionDirtyCheckMethod(CtClass managedCtClass) throws CannotCompileException {
StringBuilder builder = new StringBuilder("private boolean ")
.append(EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME)
.append("() { if ($$_hibernate_getInterceptor() == null || ")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(" == null) return false; ");
for(CtField ctField : collectCollectionFields(managedCtClass)) {
if(!enhancementContext.isMappedCollection(ctField)) {
builder.append("if(")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(".getSize(\"")
.append(ctField.getName())
.append("\") != ")
.append(ctField.getName())
.append(".size()) return true;");
}
}
builder.append("return false; }");
managedCtClass.addMethod(CtNewMethod.make(builder.toString(), managedCtClass));
}
/**
* create _getCollectionFieldDirtyNames
*/
private void createCollectionDirtyCheckGetFieldsMethod(CtClass managedCtClass) throws CannotCompileException {
StringBuilder collectionFieldDirtyFieldMethod = new StringBuilder("private void ")
.append(EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME)
.append("(java.util.Set trackerSet) { if(")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(" == null) return; else {");
for(CtField ctField : collectCollectionFields(managedCtClass)) {
if(!ctField.getName().startsWith("$$_hibernate") &&
!enhancementContext.isMappedCollection(ctField)) {
collectionFieldDirtyFieldMethod
.append("if(")
.append(EnhancerConstants.TRACKER_COLLECTION_NAME)
.append(".getSize(\"")
.append(ctField.getName())
.append("\") != ")
.append(ctField.getName())
.append(".size()) trackerSet.add(\"")
.append(ctField.getName())
.append("\");");
}
}
collectionFieldDirtyFieldMethod.append("}}");
managedCtClass.addMethod(CtNewMethod.make(collectionFieldDirtyFieldMethod.toString(), managedCtClass));
}
private void addFieldWithGetterAndSetter(
@ -490,7 +785,7 @@ private CtMethod generateFieldReader(
final FieldInfo fieldInfo = persistentField.getFieldInfo();
final String fieldName = fieldInfo.getName();
final String readerName = PERSISTENT_FIELD_READER_PREFIX + fieldName;
final String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
// read attempts only have to deal lazy-loading support, not dirty checking; so if the field
// is not enabled as lazy-loadable return a plain simple getter as the reader
@ -550,7 +845,7 @@ private CtMethod generateFieldWriter(
final FieldInfo fieldInfo = persistentField.getFieldInfo();
final String fieldName = fieldInfo.getName();
final String writerName = PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
final String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
final CtMethod writer;
@ -572,8 +867,37 @@ private CtMethod generateFieldWriter(
);
}
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( fieldName ) );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) && !isComposite ) {
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( persistentField ));
}
if( isComposite) {
StringBuilder builder = new StringBuilder();
builder.append(" if( ")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(" != null) ")
.append(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME)
.append(".callOwner(\".")
.append(persistentField.getName())
.append("\");");
writer.insertBefore( builder.toString() );
}
//composite types
if(persistentField.getAnnotation(Embedded.class) != null) {
//make sure to add the CompositeOwner interface
if(!doClassInheritCompositeOwner(managedCtClass)) {
managedCtClass.addInterface(classPool.get("org.hibernate.engine.spi.CompositeOwner"));
}
//if a composite have a embedded field we need to implement the method as well
if(isComposite)
createTrackChangeCompositeMethod(managedCtClass);
writer.insertBefore( cleanupPreviousOwner(persistentField));
writer.insertAfter( compositeMethodBody(persistentField));
}
managedCtClass.addMethod( writer );
@ -591,6 +915,47 @@ private CtMethod generateFieldWriter(
}
}
private boolean doClassInheritCompositeOwner(CtClass managedCtClass) {
try {
for(CtClass ctClass : managedCtClass.getInterfaces())
if(ctClass.getName().equals("org.hibernate.engine.spi.CompositeOwner"))
return true;
return false;
}
catch (NotFoundException e) {
return false;
}
}
private String cleanupPreviousOwner(CtField currentValue) {
StringBuilder builder = new StringBuilder();
builder.append("if (")
.append(currentValue.getName())
.append(" != null) ")
.append("((org.hibernate.engine.spi.CompositeTracker)")
.append(currentValue.getName())
.append(").")
.append(EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER)
.append("(\"")
.append(currentValue.getName())
.append("\");");
return builder.toString();
}
private String compositeMethodBody(CtField currentValue) {
StringBuilder builder = new StringBuilder();
builder.append("((org.hibernate.engine.spi.CompositeTracker) ")
.append(currentValue.getName())
.append(").$$_hibernate_setOwner(\"")
.append(currentValue.getName())
.append("\",(org.hibernate.engine.spi.CompositeOwner) this);")
.append(EnhancerConstants.TRACKER_CHANGER_NAME + "(\"" + currentValue.getName() + "\");");
return builder.toString();
}
private void transformFieldAccessesIntoReadsAndWrites(
CtClass managedCtClass,
IdentityHashMap<String, PersistentAttributeDescriptor> attributeDescriptorMap) {
@ -602,15 +967,15 @@ private void transformFieldAccessesIntoReadsAndWrites(
final String methodName = methodInfo.getName();
// skip methods added by enhancement
if ( methodName.startsWith( PERSISTENT_FIELD_READER_PREFIX )
|| methodName.startsWith( PERSISTENT_FIELD_WRITER_PREFIX )
|| methodName.equals( ENTITY_INSTANCE_GETTER_NAME )
|| methodName.equals( ENTITY_ENTRY_GETTER_NAME )
|| methodName.equals( ENTITY_ENTRY_SETTER_NAME )
|| methodName.equals( PREVIOUS_GETTER_NAME )
|| methodName.equals( PREVIOUS_SETTER_NAME )
|| methodName.equals( NEXT_GETTER_NAME )
|| methodName.equals( NEXT_SETTER_NAME ) ) {
if ( methodName.startsWith( EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX )
|| methodName.startsWith( EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX )
|| methodName.equals( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME )
|| methodName.equals( EnhancerConstants.ENTITY_ENTRY_GETTER_NAME )
|| methodName.equals( EnhancerConstants.ENTITY_ENTRY_SETTER_NAME )
|| methodName.equals( EnhancerConstants.PREVIOUS_GETTER_NAME )
|| methodName.equals( EnhancerConstants.PREVIOUS_SETTER_NAME )
|| methodName.equals( EnhancerConstants.NEXT_GETTER_NAME )
|| methodName.equals( EnhancerConstants.NEXT_SETTER_NAME ) ) {
continue;
}
@ -714,7 +1079,7 @@ public AttributeTypeDescriptor getTypeDescriptor() {
private static interface AttributeTypeDescriptor {
public String buildReadInterceptionBodyFragment(String fieldName);
public String buildWriteInterceptionBodyFragment(String fieldName);
public String buildInLineDirtyCheckingBodyFragment(String fieldName);
public String buildInLineDirtyCheckingBodyFragment(CtField currentField);
}
private AttributeTypeDescriptor resolveAttributeTypeDescriptor(CtField persistentField) throws NotFoundException {
@ -750,13 +1115,73 @@ else if ( persistentField.getType() == CtClass.floatType ) {
private static abstract class AbstractAttributeTypeDescriptor implements AttributeTypeDescriptor {
@Override
public String buildInLineDirtyCheckingBodyFragment(String fieldName) {
// for now...
// todo : hook-in in-lined dirty checking
return String.format(
"System.out.println( \"DIRTY CHECK (%1$s) : \" + this.%1$s + \" -> \" + $1 + \" (dirty=\" + (this.%1$s != $1) +\")\" );",
fieldName
);
public String buildInLineDirtyCheckingBodyFragment(CtField currentValue) {
StringBuilder builder = new StringBuilder();
try {
//should ignore primary keys
for(Object o : currentValue.getType().getAnnotations()) {
if(o instanceof Id)
return "";
}
builder.append(entityMethodBody(currentValue));
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (NotFoundException e) {
e.printStackTrace();
}
return builder.toString();
}
private String entityMethodBody(CtField currentValue) {
StringBuilder inlineBuilder = new StringBuilder();
try {
inlineBuilder.append("if ( $$_hibernate_getInterceptor() != null ");
//primitives || enums
if(currentValue.getType().isPrimitive() || currentValue.getType().isEnum()) {
inlineBuilder.append("&& "+currentValue.getName()+" != $1)");
}
//simple data types
else if(currentValue.getType().getName().startsWith("java.lang") ||
currentValue.getType().getName().startsWith("java.math.Big") ||
currentValue.getType().getName().startsWith("java.sql.Time") ||
currentValue.getType().getName().startsWith("java.sql.Date") ||
currentValue.getType().getName().startsWith("java.util.Date") ||
currentValue.getType().getName().startsWith("java.util.Calendar")
) {
inlineBuilder.append("&& (("+currentValue.getName()+" == null) || (!" +currentValue.getName()+".equals( $1))))");
}
//all other objects
else {
//if the field is a collection we return since we handle that in a separate method
for(CtClass ctClass : currentValue.getType().getInterfaces()) {
if(ctClass.getName().equals("java.util.Collection")) {
//if the collection is not managed we should write it to the tracker
//todo: should use EnhancementContext.isMappedCollection here instead
if (currentValue.getAnnotation(OneToMany.class) != null ||
currentValue.getAnnotation(ManyToMany.class) != null ||
currentValue.getAnnotation(ElementCollection.class) != null) {
return "";
}
}
}
//todo: for now just call equals, should probably do something else here
inlineBuilder.append("&& (("+currentValue.getName()+" == null) || (!" +currentValue.getName()+".equals( $1))))");
}
inlineBuilder.append( EnhancerConstants.TRACKER_CHANGER_NAME+"(\""+currentValue.getName()+"\");");
} catch (NotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return inlineBuilder.toString();
}
}

View File

@ -0,0 +1,181 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.bytecode.enhance.spi;
/**
* Constants used during enhancement.
*
* @author Steve Ebersole
*/
public class EnhancerConstants {
/**
* Prefix for persistent-field reader methods.
*/
public static final String PERSISTENT_FIELD_READER_PREFIX = "$$_hibernate_read_";
/**
* Prefix for persistent-field writer methods.
*/
public static final String PERSISTENT_FIELD_WRITER_PREFIX = "$$_hibernate_write_";
/**
* Name of the method used to get reference the the entity instance (this in the case of enhanced classes).
*/
public static final String ENTITY_INSTANCE_GETTER_NAME = "$$_hibernate_getEntityInstance";
/**
* Name of the field used to hold the {@link org.hibernate.engine.spi.EntityEntry}
*/
public static final String ENTITY_ENTRY_FIELD_NAME = "$$_hibernate_entityEntryHolder";
/**
* Name of the method used to read the {@link org.hibernate.engine.spi.EntityEntry} field.
*
* @see #ENTITY_ENTRY_FIELD_NAME
*/
public static final String ENTITY_ENTRY_GETTER_NAME = "$$_hibernate_getEntityEntry";
/**
* Name of the method used to write the {@link org.hibernate.engine.spi.EntityEntry} field.
*
* @see #ENTITY_ENTRY_FIELD_NAME
*/
public static final String ENTITY_ENTRY_SETTER_NAME = "$$_hibernate_setEntityEntry";
/**
* Name of the field used to hold the previous {@link org.hibernate.engine.spi.ManagedEntity}.
*
* Together, previous/next are used to define a "linked list"
*
* @see #NEXT_FIELD_NAME
*/
public static final String PREVIOUS_FIELD_NAME = "$$_hibernate_previousManagedEntity";
/**
* Name of the method used to read the previous {@link org.hibernate.engine.spi.ManagedEntity} field
*
* @see #PREVIOUS_FIELD_NAME
*/
public static final String PREVIOUS_GETTER_NAME = "$$_hibernate_getPreviousManagedEntity";
/**
* Name of the method used to write the previous {@link org.hibernate.engine.spi.ManagedEntity} field
*
* @see #PREVIOUS_FIELD_NAME
*/
public static final String PREVIOUS_SETTER_NAME = "$$_hibernate_setPreviousManagedEntity";
/**
* Name of the field used to hold the previous {@link org.hibernate.engine.spi.ManagedEntity}.
*
* Together, previous/next are used to define a "linked list"
*
* @see #PREVIOUS_FIELD_NAME
*/
public static final String NEXT_FIELD_NAME = "$$_hibernate_nextManagedEntity";
/**
* Name of the method used to read the next {@link org.hibernate.engine.spi.ManagedEntity} field
*
* @see #NEXT_FIELD_NAME
*/
public static final String NEXT_GETTER_NAME = "$$_hibernate_getNextManagedEntity";
/**
* Name of the method used to write the next {@link org.hibernate.engine.spi.ManagedEntity} field
*
* @see #NEXT_FIELD_NAME
*/
public static final String NEXT_SETTER_NAME = "$$_hibernate_setNextManagedEntity";
/**
* Name of the field used to store the {@link org.hibernate.engine.spi.PersistentAttributeInterceptable}.
*/
public static final String INTERCEPTOR_FIELD_NAME = "$$_hibernate_attributeInterceptor";
/**
* Name of the method used to read the interceptor
*
* @see #INTERCEPTOR_FIELD_NAME
*/
public static final String INTERCEPTOR_GETTER_NAME = "$$_hibernate_getInterceptor";
/**
* Name of the method used to write the interceptor
*
* @see #INTERCEPTOR_FIELD_NAME
*/
public static final String INTERCEPTOR_SETTER_NAME = "$$_hibernate_setInterceptor";
/**
* Name of tracker field
*/
public static final String TRACKER_FIELD_NAME = "$$_hibernate_tracker";
/**
* Name of method that add changed fields
*/
public static final String TRACKER_CHANGER_NAME = "$$_hibernate_trackChange";
/**
* Name of method to see if any fields has changed
*/
public static final String TRACKER_HAS_CHANGED_NAME = "$$_hibernate_hasDirtyAttributes";
/**
* Name of method to fetch dirty attributes
*/
public static final String TRACKER_GET_NAME = "$$_hibernate_getDirtyAttributes";
/**
* Name of method to clear stored dirty attributes
*/
public static final String TRACKER_CLEAR_NAME = "$$_hibernate_clearDirtyAttributes";
/**
* Name of method to check if collection fields are dirty
*/
public static final String TRACKER_COLLECTION_CHANGED_NAME = "$$_hibernate_areCollectionFieldsDirty";
public static final String TRACKER_COLLECTION_NAME = "$$_hibernate_collectionTracker";
/**
* Name of method to get dirty collection field names
*/
public static final String TRACKER_COLLECTION_CHANGED_FIELD_NAME = "$$_hibernate_getCollectionFieldDirtyNames";
public static final String TRACKER_COLLECTION_CLEAR_NAME = "$$_hibernate_clearDirtyCollectionNames";
public static final String TRACKER_COMPOSITE_DIRTY_CHECK = "$$_hibernate_areCompositeFieldsDirty";
public static final String TRACKER_COMPOSITE_DIRTY_FIELDS_GETTER = "$$_hibernate_getCompositeDirtyFields";
public static final String TRACKER_COMPOSITE_FIELD_NAME = "$$_hibernate_compositeOwners";
public static final String TRACKER_COMPOSITE_SET_OWNER = "$$_hibernate_setOwner";
public static final String TRACKER_COMPOSITE_CLEAR_OWNER = "$$_hibernate_clearOwner";
private EnhancerConstants() {
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.spi;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public interface CompositeOwner {
/**
* @param attributeName to be added to the dirty list
*/
void $$_hibernate_trackChange(String attributeName);
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.spi;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public interface CompositeTracker {
void $$_hibernate_setOwner(String name, CompositeOwner tracker);
void $$_hibernate_clearOwner(String name);
}

View File

@ -236,6 +236,9 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
interceptor.clearDirty();
}
}
if( entity instanceof SelfDirtinessTracker)
((SelfDirtinessTracker) entity).$$_hibernate_clearDirtyAttributes();
persistenceContext.getSession()
.getFactory()
.getCustomEntityDirtinessStrategy()
@ -299,6 +302,10 @@ public boolean requiresDirtyCheck(Object entity) {
@SuppressWarnings( {"SimplifiableIfStatement"})
private boolean isUnequivocallyNonDirty(Object entity) {
if(entity instanceof SelfDirtinessTracker)
return ((SelfDirtinessTracker) entity).$$_hibernate_hasDirtyAttributes();
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
persistenceContext.getSession().getFactory().getCustomEntityDirtinessStrategy();
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), (Session) persistenceContext.getSession() ) ) {

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.spi;
import java.util.Set;
/**
* Specify if an entity class is instrumented to track field changes
*
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public interface SelfDirtinessTracker {
/**
* Return true if any fields has been changed
*/
boolean $$_hibernate_hasDirtyAttributes();
/**
* Get the field names of all the fields thats been changed
*/
Set<String> $$_hibernate_getDirtyAttributes();
/**
* Clear the stored dirty attributes
*/
void $$_hibernate_clearDirtyAttributes();
}

View File

@ -26,6 +26,7 @@
import java.io.Serializable;
import java.util.Arrays;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
@ -491,6 +492,12 @@ protected void dirtyCheck(final FlushEntityEvent event) throws HibernateExceptio
);
if ( dirtyProperties == null ) {
if(entity instanceof SelfDirtinessTracker) {
if(((SelfDirtinessTracker) entity).$$_hibernate_hasDirtyAttributes()) {
dirtyProperties = persister.resolveAttributeIndexes(((SelfDirtinessTracker) entity).$$_hibernate_getDirtyAttributes());
}
}
else {
// see if the custom dirtiness strategy can tell us...
class DirtyCheckContextImpl implements CustomEntityDirtinessStrategy.DirtyCheckContext {
int[] found = null;
@ -510,6 +517,7 @@ public void doDirtyChecking(CustomEntityDirtinessStrategy.AttributeChecker attri
context
);
dirtyProperties = context.found;
}
}
event.setDatabaseSnapshot(null);

View File

@ -2144,6 +2144,20 @@ protected String[] getSubclassPropertyNameClosure() {
return subclassPropertyNameClosure;
}
@Override
public int[] resolveAttributeIndexes(Set<String> properties) {
Iterator<String> iter = properties.iterator();
int[] fields = new int[properties.size()];
int counter = 0;
while(iter.hasNext()) {
Integer index = entityMetamodel.getPropertyIndexOrNull(iter.next());
if(index != null)
fields[counter++] = index;
}
return fields;
}
protected String[] getSubclassPropertySubclassNameClosure() {
return subclassPropertySubclassNameClosure;
}

View File

@ -25,6 +25,7 @@
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
@ -755,4 +756,7 @@ public Object createProxy(Serializable id, SessionImplementor session)
public EntityInstrumentationMetadata getInstrumentationMetadata();
public FilterAliasGenerator getFilterAliasGenerator(final String rootAlias);
public int[] resolveAttributeIndexes(Set<String> properties);
}

View File

@ -23,7 +23,11 @@
*/
package org.hibernate.tool.enhance;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import java.io.File;
import java.io.FileInputStream;
@ -87,36 +91,66 @@ public void execute() throws BuildException {
processClassFile( javaClassFile);
}
}
}
/**
* Atm only process files annotated with either @Entity or @Embeddable
* @param javaClassFile
*/
private void processClassFile(File javaClassFile) {
try {
final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );
if ( ! shouldInclude( ctClass ) ) {
return;
if(this.isEntityClass(ctClass))
processEntityClassFile(javaClassFile, ctClass);
else if(this.isCompositeClass(ctClass))
processCompositeClassFile(javaClassFile, ctClass);
}
catch (IOException e) {
throw new BuildException(
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ), e );
}
}
final byte[] enhancedBytecode;
private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {
try {
enhancedBytecode = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
if(result != null)
writeEnhancedClass(javaClassFile, result);
}
catch (Exception e) {
log( "Unable to enhance class [" + ctClass.getName() + "]", e, Project.MSG_WARN );
return;
}
}
private void processCompositeClassFile(File javaClassFile, CtClass ctClass) {
try {
byte[] result = enhancer.enhanceComposite(ctClass.getName(), ctClass.toBytecode());
if(result != null)
writeEnhancedClass(javaClassFile, result);
}
catch (Exception e) {
log( "Unable to enhance class [" + ctClass.getName() + "]", e, Project.MSG_WARN );
return;
}
}
private void writeEnhancedClass(File javaClassFile, byte[] result) {
try {
if ( javaClassFile.delete() ) {
if ( ! javaClassFile.createNewFile() ) {
log( "Unable to recreate class file [" + ctClass.getName() + "]", Project.MSG_INFO );
log( "Unable to recreate class file [" + javaClassFile.getName() + "]", Project.MSG_INFO );
}
}
else {
log( "Unable to delete class file [" + ctClass.getName() + "]", Project.MSG_INFO );
log( "Unable to delete class file [" + javaClassFile.getName() + "]", Project.MSG_INFO );
}
FileOutputStream outputStream = new FileOutputStream( javaClassFile, false );
try {
outputStream.write( enhancedBytecode );
outputStream.write( result);
outputStream.flush();
}
finally {
@ -132,20 +166,11 @@ private void processClassFile(File javaClassFile) {
}
catch (IOException e) {
throw new BuildException(
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ),
e
);
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ), e );
}
}
private boolean shouldInclude(CtClass ctClass) {
// we currently only handle entity enhancement
return ctClass.hasAnnotation( Entity.class );
}
// EnhancementContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public ClassLoader getLoadingClassLoader() {
return getClass().getClassLoader();
@ -153,18 +178,17 @@ public ClassLoader getLoadingClassLoader() {
@Override
public boolean isEntityClass(CtClass classDescriptor) {
// currently we only call enhance on the classes with @Entity, so here we always return true
return true;
return classDescriptor.hasAnnotation(Entity.class);
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
return false;
return classDescriptor.hasAnnotation(Embeddable.class);
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return false;
return true;
}
@Override
@ -183,6 +207,18 @@ public boolean isPersistentField(CtField ctField) {
return ! ctField.hasAnnotation( Transient.class );
}
@Override
public boolean isMappedCollection(CtField field) {
try {
return (field.getAnnotation(OneToMany.class) != null ||
field.getAnnotation(ManyToMany.class) != null ||
field.getAnnotation(ElementCollection.class) != null);
}
catch (ClassNotFoundException e) {
return false;
}
}
@Override
public CtField[] order(CtField[] persistentFields) {
// for now...

View File

@ -510,6 +510,11 @@ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, S
//TODO: if we support multiple fetch groups, we would need
// to clone the set of lazy properties!
FieldInterceptionHelper.injectFieldInterceptor( entity, getEntityName(), lazyProps, session );
//also clear the fields that are marked as dirty in the dirtyness tracker
if(entity instanceof org.hibernate.engine.spi.SelfDirtinessTracker) {
((org.hibernate.engine.spi.SelfDirtinessTracker) entity).$$_hibernate_clearDirtyAttributes();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.hibernate.test.bytecode.enhancement;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import java.io.Serializable;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
@Embeddable
public class Address implements Serializable {
private String street1;
private String street2;
private String city;
private String state;
@Embedded
private Country country;
private String zip;
private String phone;
public Address() {
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}

View File

@ -0,0 +1,84 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.bytecode.enhancement;
import org.hibernate.bytecode.enhance.spi.CompositeOwnerTracker;
import org.hibernate.engine.spi.CompositeOwner;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class CompositeOwnerTrackerTest {
private int counter = 0;
@Test
public void testCompositeOwnerTracker() {
CompositeOwnerTracker tracker = new CompositeOwnerTracker();
tracker.add("foo", new TestCompositeOwner());
tracker.callOwner(".street1");
assertEquals(1, counter);
tracker.add("bar", new TestCompositeOwner());
tracker.callOwner(".city");
assertEquals(3, counter);
tracker.removeOwner("foo");
tracker.callOwner(".country");
assertEquals(4, counter);
tracker.removeOwner("bar");
tracker.callOwner(".country");
tracker.add("moo", new TestCompositeOwner());
tracker.callOwner(".country");
assertEquals(5, counter);
}
class TestCompositeOwner implements CompositeOwner {
@Override
public void $$_hibernate_trackChange(String attributeName) {
if(counter == 0)
assertEquals("foo.street1", attributeName);
if(counter == 1)
assertEquals("foo.city", attributeName);
if(counter == 2)
assertEquals("bar.city", attributeName);
if(counter == 3)
assertEquals("bar.country", attributeName);
if(counter == 4)
assertEquals("moo.country", attributeName);
counter++;
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.hibernate.test.bytecode.enhancement;
import javax.persistence.Embeddable;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
@Embeddable
public class Country {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -23,35 +23,43 @@
*/
package org.hibernate.test.bytecode.enhancement;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.LoaderClassPath;
import javax.persistence.ElementCollection;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import org.hibernate.EntityMode;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.cfg.Configuration;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.Status;
import org.hibernate.mapping.PersistentClass;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import org.junit.Test;
/**
* @author Steve Ebersole
@ -88,6 +96,18 @@ public boolean isLazyLoadable(CtField field) {
return true;
}
@Override
public boolean isMappedCollection(CtField field) {
try {
return (field.getAnnotation(OneToMany.class) != null ||
field.getAnnotation(ManyToMany.class) != null ||
field.getAnnotation(ElementCollection.class) != null);
}
catch (ClassNotFoundException e) {
return false;
}
}
@Override
public boolean isPersistentField(CtField ctField) {
return true;
@ -109,6 +129,7 @@ private void testFor(Class entityClassToEnhance) throws Exception {
Enhancer enhancer = new Enhancer( enhancementContext );
CtClass entityCtClass = generateCtClassForAnEntity( entityClassToEnhance );
byte[] original = entityCtClass.toBytecode();
//byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
@ -116,6 +137,24 @@ private void testFor(Class entityClassToEnhance) throws Exception {
ClassPool cp = new ClassPool( false );
cp.appendClassPath( new LoaderClassPath( cl ) );
CtClass enhancedCtClass = cp.makeClass( new ByteArrayInputStream( enhanced ) );
enhancedCtClass.debugWriteFile("/tmp");
//just for debugging
Class addressClass = null;
Class countryClass = null;
if(entityClassToEnhance.getName().endsWith("SimpleEntity")) {
CtClass addressCtClass = generateCtClassForAnEntity( Address.class );
byte[] enhancedAddress = enhancer.enhanceComposite(Address.class.getName(), addressCtClass.toBytecode());
CtClass enhancedCtClassAddress = cp.makeClass( new ByteArrayInputStream( enhancedAddress ) );
enhancedCtClassAddress.debugWriteFile("/tmp");
addressClass = enhancedCtClassAddress.toClass( cl, this.getClass().getProtectionDomain() );
CtClass countryCtClass = generateCtClassForAnEntity( Country.class );
byte[] enhancedCountry = enhancer.enhanceComposite(Country.class.getName(), countryCtClass.toBytecode());
CtClass enhancedCtClassCountry = cp.makeClass( new ByteArrayInputStream( enhancedCountry ) );
enhancedCtClassCountry.debugWriteFile("/tmp");
countryClass = enhancedCtClassCountry.toClass( cl, this.getClass().getProtectionDomain() );
}
Class entityClass = enhancedCtClass.toClass( cl, this.getClass().getProtectionDomain() );
Object entityInstance = entityClass.newInstance();
@ -123,30 +162,30 @@ private void testFor(Class entityClassToEnhance) throws Exception {
// call the new methods
//
Method setter = entityClass.getMethod( Enhancer.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class );
Method getter = entityClass.getMethod( Enhancer.ENTITY_ENTRY_GETTER_NAME );
Method setter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class );
Method getter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_GETTER_NAME );
assertNull( getter.invoke( entityInstance ) );
setter.invoke( entityInstance, makeEntityEntry() );
assertNotNull( getter.invoke( entityInstance ) );
setter.invoke( entityInstance, new Object[] {null} );
assertNull( getter.invoke( entityInstance ) );
Method entityInstanceGetter = entityClass.getMethod( Enhancer.ENTITY_INSTANCE_GETTER_NAME );
Method entityInstanceGetter = entityClass.getMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME );
assertSame( entityInstance, entityInstanceGetter.invoke( entityInstance ) );
Method previousGetter = entityClass.getMethod( Enhancer.PREVIOUS_GETTER_NAME );
Method previousSetter = entityClass.getMethod( Enhancer.PREVIOUS_SETTER_NAME, ManagedEntity.class );
Method previousGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
Method previousSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
previousSetter.invoke( entityInstance, entityInstance );
assertSame( entityInstance, previousGetter.invoke( entityInstance ) );
Method nextGetter = entityClass.getMethod( Enhancer.PREVIOUS_GETTER_NAME );
Method nextSetter = entityClass.getMethod( Enhancer.PREVIOUS_SETTER_NAME, ManagedEntity.class );
Method nextGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
Method nextSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
nextSetter.invoke( entityInstance, entityInstance );
assertSame( entityInstance, nextGetter.invoke( entityInstance ) );
// add an attribute interceptor...
Method interceptorGetter = entityClass.getMethod( Enhancer.INTERCEPTOR_GETTER_NAME );
Method interceptorSetter = entityClass.getMethod( Enhancer.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
Method interceptorGetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
Method interceptorSetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
assertNull( interceptorGetter.invoke( entityInstance ) );
entityClass.getMethod( "getId" ).invoke( entityInstance );
@ -158,6 +197,7 @@ private void testFor(Class entityClassToEnhance) throws Exception {
entityClass.getMethod( "getId" ).invoke( entityInstance );
entityClass.getMethod( "setId", Long.class ).invoke( entityInstance, entityClass.getMethod( "getId" ).invoke( entityInstance ) );
entityClass.getMethod( "setId", Long.class ).invoke( entityInstance, 1L );
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
entityClass.getMethod( "isActive" ).invoke( entityInstance );
entityClass.getMethod( "setActive", boolean.class ).invoke( entityInstance, entityClass.getMethod( "isActive" ).invoke( entityInstance ) );
@ -166,6 +206,81 @@ private void testFor(Class entityClassToEnhance) throws Exception {
entityClass.getMethod( "getSomeNumber" ).invoke( entityInstance );
entityClass.getMethod( "setSomeNumber", long.class ).invoke( entityInstance, entityClass.getMethod( "getSomeNumber" ).invoke( entityInstance ) );
entityClass.getMethod( "setSomeNumber", long.class ).invoke(entityInstance, 1L);
assertEquals(3, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
//setting the same value should not make it dirty
entityClass.getMethod( "setSomeNumber", long.class ).invoke(entityInstance, 1L);
//assertEquals(0, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
//assertFalse((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
if(entityClass.getName().endsWith("SimpleEntity")) {
List<String> strings = new ArrayList<String>();
strings.add("FooBar");
entityClass.getMethod( "setSomeStrings", java.util.List.class ).invoke(entityInstance, strings);
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
Set tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(1, tracked.size());
assertEquals("someStrings", tracked.iterator().next());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
((List) entityClass.getMethod( "getSomeStrings").invoke(entityInstance)).add("JADA!");
Boolean isDirty = (Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance);
assertTrue(isDirty);
assertEquals("someStrings",
((Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance)).iterator().next());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
//this should not set the entity to dirty
Set<Integer> ints = new HashSet<Integer>();
ints.add(42);
entityClass.getMethod( "setSomeInts", java.util.Set.class ).invoke(entityInstance, ints);
isDirty = (Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance);
assertFalse(isDirty);
//testing composite object
assert addressClass != null;
Object address = addressClass.newInstance();
assert countryClass != null;
Object country = countryClass.newInstance();
//Method adrInterceptorGetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
//Method adrInterceptorSetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
//adrInterceptorSetter.invoke( address, new LocalPersistentAttributeInterceptor() );
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address);
addressClass.getMethod("setCity", String.class).invoke(address, "Arendal");
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(2, tracked.size());
Iterator iter = tracked.iterator();
assertEquals("address", iter.next());
assertEquals("address.city", iter.next());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
//make sure that new composite instances are cleared
Object address2 = addressClass.newInstance();
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address2);
addressClass.getMethod("setStreet1", String.class).invoke(address, "Heggedalveien");
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(1, tracked.size());
addressClass.getMethod("setCountry", countryClass).invoke(address2, country);
countryClass.getMethod("setName", String.class).invoke(country, "Norway");
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(3, tracked.size());
}
}
private CtClass generateCtClassForAnEntity(Class entityClassToEnhance) throws Exception {

View File

@ -23,8 +23,12 @@
*/
package org.hibernate.test.bytecode.enhancement;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;
import java.util.Set;
/**
* @author Steve Ebersole
@ -35,6 +39,13 @@ public class SimpleEntity {
private String name;
private boolean active;
private long someNumber;
private List<String> someStrings;
@OneToMany
private Set<Integer> someInts;
@Embedded
private Address address;
@Id
public Long getId() {
@ -68,4 +79,28 @@ public long getSomeNumber() {
public void setSomeNumber(long someNumber) {
this.someNumber = someNumber;
}
public List<String> getSomeStrings() {
return someStrings;
}
public void setSomeStrings(List<String> someStrings) {
this.someStrings = someStrings;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Set<Integer> getSomeInts() {
return someInts;
}
public void setSomeInts(Set<Integer> someInts) {
this.someInts = someInts;
}
}

View File

@ -0,0 +1,122 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/package org.hibernate.test.bytecode.enhancement.customer;
import java.io.Serializable;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class Address implements Serializable {
private String street1;
private String street2;
private String city;
private String state;
private String country;
private String zip;
private String phone;
public Address() {
}
public Address(String street1, String street2, String city, String state,
String country, String zip, String phone) {
this.street1 = street1;
this.street2 = street2;
this.city = city;
this.state = state;
this.country = country;
setZip(zip);
setPhone(phone);
}
public String toString() {
return street1 + "\n" + street2 + "\n" + city + "," + state + " " + zip + "\n" + phone;
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
assertNumeric(zip, "Non-numeric zip ");
this.zip = zip;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
assertNumeric(zip, "Non-numeric phone ");
this.phone = phone;
}
void assertNumeric(String s, String error) {
for (int i=0; i<s.length(); i++) {
if (!Character.isDigit(s.charAt(i))) {
throw new IllegalArgumentException(error + s);
}
}
}
}

View File

@ -0,0 +1,249 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.bytecode.enhancement.customer;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
@Entity
@Table(name="O_CUSTOMER")
public class Customer {
public static final String QUERY_ALL = "Customer.selectAll";
public static final String QUERY_COUNT = "Customer.count";
public static final String QUERY_BY_CREDIT = "Customer.selectByCreditLimit";
public static final String BAD_CREDIT = "BC";
@Id
@Column(name="C_ID")
private int id;
@Column(name="C_FIRST")
private String firstName;
@Column(name="C_LAST")
private String lastName;
@Column(name="C_CONTACT")
private String contact;
@Column(name="C_CREDIT")
private String credit;
@Column(name="C_CREDIT_LIMIT")
private BigDecimal creditLimit;
@Column(name="C_SINCE")
@Temporal(TemporalType.DATE)
private Calendar since;
@Column(name="C_BALANCE")
private BigDecimal balance;
@Column(name="C_YTD_PAYMENT")
private BigDecimal ytdPayment;
@OneToMany(mappedBy="customer", cascade= CascadeType.ALL, fetch= FetchType.EAGER)
private List<CustomerInventory> customerInventories;
@Embedded
@AttributeOverrides(
{@AttributeOverride(name="street1",column=@Column(name="C_STREET1")),
@AttributeOverride(name="street2",column=@Column(name="C_STREET2")),
@AttributeOverride(name="city", column=@Column(name="C_CITY")),
@AttributeOverride(name="state", column=@Column(name="C_STATE")),
@AttributeOverride(name="country",column=@Column(name="C_COUNTRY")),
@AttributeOverride(name="zip", column=@Column(name="C_ZIP")),
@AttributeOverride(name="phone", column=@Column(name="C_PHONE"))})
private Address address;
@Version
@Column(name = "C_VERSION")
private int version;
public Customer() {
}
public Customer(String first, String last, Address address,
String contact, String credit, BigDecimal creditLimit,
BigDecimal balance, BigDecimal YtdPayment) {
this.firstName = first;
this.lastName = last;
this.address = address;
this.contact = contact;
this.since = Calendar.getInstance();
this.credit = credit;
this.creditLimit = creditLimit;
this.balance = balance;
this.ytdPayment = YtdPayment;
}
public Integer getId() {
return id;
}
public void setId(Integer customerId) {
this.id = customerId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String getCredit() {
return credit;
}
public void setCredit(String credit) {
this.credit = credit;
}
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
public Calendar getSince() {
return since;
}
public void setSince(Calendar since) {
this.since = since;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public void changeBalance(BigDecimal change) {
setBalance(balance.add(change).setScale(2, BigDecimal.ROUND_DOWN));
}
public BigDecimal getYtdPayment() {
return ytdPayment;
}
public void setYtdPayment(BigDecimal ytdPayment) {
this.ytdPayment = ytdPayment;
}
public List<CustomerInventory> getInventories() {
if (customerInventories == null){
customerInventories = new ArrayList<CustomerInventory>();
}
return customerInventories;
}
public CustomerInventory addInventory(String item, int quantity,
BigDecimal totalValue) {
CustomerInventory inventory = new CustomerInventory(this, item,
quantity, totalValue);
getInventories().add(inventory);
return inventory;
}
public int getVersion() {
return version;
}
public boolean hasSufficientCredit(BigDecimal amount) {
return !BAD_CREDIT.equals(getCredit())
&& creditLimit != null
&& creditLimit.compareTo(amount) >= 0;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
return id == ((Customer) o).id;
}
@Override
public int hashCode() {
return new Integer(id).hashCode();
}
@Override
public String toString() {
return this.getFirstName() + " " + this.getLastName();
}
}

View File

@ -0,0 +1,384 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.bytecode.enhancement.customer;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.LoaderClassPath;
import org.hibernate.EntityMode;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.Status;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import javax.persistence.ElementCollection;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
public class CustomerEnhancerTest extends BaseUnitTestCase {
private static EnhancementContext enhancementContext = new EnhancementContext() {
@Override
public ClassLoader getLoadingClassLoader() {
return getClass().getClassLoader();
}
@Override
public boolean isEntityClass(CtClass classDescriptor) {
return true;
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
return false;
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return true;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return true;
}
@Override
public boolean isLazyLoadable(CtField field) {
return true;
}
@Override
public boolean isMappedCollection(CtField field) {
try {
return (field.getAnnotation(OneToMany.class) != null ||
field.getAnnotation(ManyToMany.class) != null ||
field.getAnnotation(ElementCollection.class) != null);
}
catch (ClassNotFoundException e) {
return false;
}
}
@Override
public boolean isPersistentField(CtField ctField) {
return true;
}
@Override
public CtField[] order(CtField[] persistentFields) {
return persistentFields;
}
};
@Test
public void testEnhancement() throws Exception {
testFor( Customer.class );
}
private void testFor(Class entityClassToEnhance) throws Exception {
Enhancer enhancer = new Enhancer( enhancementContext );
CtClass entityCtClass = generateCtClassForAnEntity( entityClassToEnhance );
byte[] original = entityCtClass.toBytecode();
//byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
byte[] enhanced = enhancer.enhance( entityCtClass.getName(), original );
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
ClassLoader cl = new ClassLoader() { };
ClassPool cp = new ClassPool( false );
cp.appendClassPath( new LoaderClassPath( cl ) );
CtClass enhancedCtClass = cp.makeClass( new ByteArrayInputStream( enhanced ) );
enhancedCtClass.debugWriteFile("/tmp");
//just for debugging
Class addressClass = null;
Class customerInventoryClass = null;
Class supplierComponentPKClass = null;
CtClass addressCtClass = generateCtClassForAnEntity( org.hibernate.test.bytecode.enhancement.customer.Address.class );
byte[] enhancedAddress = enhancer.enhanceComposite(Address.class.getName(), addressCtClass.toBytecode());
CtClass enhancedCtClassAddress = cp.makeClass( new ByteArrayInputStream( enhancedAddress ) );
enhancedCtClassAddress.debugWriteFile("/tmp");
addressClass = enhancedCtClassAddress.toClass( cl, this.getClass().getProtectionDomain() );
CtClass customerInventoryCtClass = generateCtClassForAnEntity( CustomerInventory.class );
byte[] enhancedCustomerInventory = enhancer.enhance(CustomerInventory.class.getName(), customerInventoryCtClass.toBytecode());
CtClass enhancedCtClassCustomerInventory = cp.makeClass( new ByteArrayInputStream( enhancedCustomerInventory ) );
enhancedCtClassCustomerInventory.debugWriteFile("/tmp");
customerInventoryClass = enhancedCtClassCustomerInventory.toClass( cl, this.getClass().getProtectionDomain() );
CtClass supplierComponentPKCtClass = generateCtClassForAnEntity( SupplierComponentPK.class );
byte[] enhancedSupplierComponentPK = enhancer.enhanceComposite(SupplierComponentPK.class.getName(), supplierComponentPKCtClass.toBytecode());
CtClass enhancedCtClassSupplierComponentPK = cp.makeClass( new ByteArrayInputStream( enhancedSupplierComponentPK ) );
enhancedCtClassSupplierComponentPK.debugWriteFile("/tmp");
supplierComponentPKClass = enhancedCtClassSupplierComponentPK.toClass( cl, this.getClass().getProtectionDomain() );
Class entityClass = enhancedCtClass.toClass( cl, this.getClass().getProtectionDomain() );
Object entityInstance = entityClass.newInstance();
assertTyping( ManagedEntity.class, entityInstance );
// call the new methods
//
Method setter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_SETTER_NAME, EntityEntry.class );
Method getter = entityClass.getMethod( EnhancerConstants.ENTITY_ENTRY_GETTER_NAME );
assertNull( getter.invoke( entityInstance ) );
setter.invoke( entityInstance, makeEntityEntry() );
assertNotNull( getter.invoke( entityInstance ) );
setter.invoke( entityInstance, new Object[] {null} );
assertNull( getter.invoke( entityInstance ) );
Method entityInstanceGetter = entityClass.getMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME );
assertSame( entityInstance, entityInstanceGetter.invoke( entityInstance ) );
Method previousGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
Method previousSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
previousSetter.invoke( entityInstance, entityInstance );
assertSame( entityInstance, previousGetter.invoke( entityInstance ) );
Method nextGetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_GETTER_NAME );
Method nextSetter = entityClass.getMethod( EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class );
nextSetter.invoke( entityInstance, entityInstance );
assertSame( entityInstance, nextGetter.invoke( entityInstance ) );
// add an attribute interceptor...
Method interceptorGetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
Method interceptorSetter = entityClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
assertNull( interceptorGetter.invoke( entityInstance ) );
entityClass.getMethod( "getId" ).invoke( entityInstance );
interceptorSetter.invoke( entityInstance, new LocalPersistentAttributeInterceptor() );
assertNotNull(interceptorGetter.invoke(entityInstance));
// dirty checking is unfortunately just printlns for now... just verify the test output
entityClass.getMethod( "getId" ).invoke( entityInstance );
entityClass.getMethod( "setId", Integer.class ).invoke( entityInstance, entityClass.getMethod( "getId" ).invoke( entityInstance ) );
entityClass.getMethod( "setId", Integer.class ).invoke( entityInstance, 1 );
assertTrue((Boolean) entityClass.getMethod("$$_hibernate_hasDirtyAttributes").invoke(entityInstance));
entityClass.getMethod( "setFirstName", String.class ).invoke(entityInstance, "Erik");
entityClass.getMethod( "setLastName", String.class ).invoke(entityInstance, "Mykland");
assertEquals(3, ((Set) entityClass.getMethod("$$_hibernate_getDirtyAttributes").invoke(entityInstance)).size());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
//testing composite object
assert addressClass != null;
Object address = addressClass.newInstance();
assert customerInventoryClass != null;
Object customerInventory = customerInventoryClass.newInstance();
//Method adrInterceptorGetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_GETTER_NAME );
//Method adrInterceptorSetter = addressClass.getMethod( EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class );
//adrInterceptorSetter.invoke( address, new LocalPersistentAttributeInterceptor() );
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address);
addressClass.getMethod("setCity", String.class).invoke(address, "Arendal");
Set tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(2, tracked.size());
Iterator iter = tracked.iterator();
assertEquals("address", iter.next());
assertEquals("address.city", iter.next());
entityClass.getMethod("$$_hibernate_clearDirtyAttributes").invoke(entityInstance);
//make sure that new composite instances are cleared
Object address2 = addressClass.newInstance();
entityClass.getMethod("setAddress", addressClass).invoke(entityInstance, address2);
addressClass.getMethod("setStreet1", String.class).invoke(address, "Heggedalveien");
tracked = (Set) entityClass.getMethod(EnhancerConstants.TRACKER_GET_NAME).invoke(entityInstance);
assertEquals(1, tracked.size());
}
private CtClass generateCtClassForAnEntity(Class entityClassToEnhance) throws Exception {
ClassPool cp = new ClassPool( false );
return cp.makeClass(
getClass().getClassLoader().getResourceAsStream(
entityClassToEnhance.getName().replace( '.', '/' ) + ".class"
)
);
}
private EntityEntry makeEntityEntry() {
return new EntityEntry(
Status.MANAGED,
null,
null,
new Long(1),
null,
LockMode.NONE,
false,
null,
EntityMode.POJO,
null,
false,
false,
null
);
}
private class LocalPersistentAttributeInterceptor implements PersistentAttributeInterceptor {
@Override
public boolean readBoolean(Object obj, String name, boolean oldValue) {
System.out.println( "Reading boolean [" + name + "]" );
return oldValue;
}
@Override
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
System.out.println( "Writing boolean [" + name + "]" );
return newValue;
}
@Override
public byte readByte(Object obj, String name, byte oldValue) {
System.out.println( "Reading byte [" + name + "]" );
return oldValue;
}
@Override
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
System.out.println( "Writing byte [" + name + "]" );
return newValue;
}
@Override
public char readChar(Object obj, String name, char oldValue) {
System.out.println( "Reading char [" + name + "]" );
return oldValue;
}
@Override
public char writeChar(Object obj, String name, char oldValue, char newValue) {
System.out.println( "Writing char [" + name + "]" );
return newValue;
}
@Override
public short readShort(Object obj, String name, short oldValue) {
System.out.println( "Reading short [" + name + "]" );
return oldValue;
}
@Override
public short writeShort(Object obj, String name, short oldValue, short newValue) {
System.out.println( "Writing short [" + name + "]" );
return newValue;
}
@Override
public int readInt(Object obj, String name, int oldValue) {
System.out.println( "Reading int [" + name + "]" );
return oldValue;
}
@Override
public int writeInt(Object obj, String name, int oldValue, int newValue) {
System.out.println( "Writing int [" + name + "]" );
return newValue;
}
@Override
public float readFloat(Object obj, String name, float oldValue) {
System.out.println( "Reading float [" + name + "]" );
return oldValue;
}
@Override
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
System.out.println( "Writing float [" + name + "]" );
return newValue;
}
@Override
public double readDouble(Object obj, String name, double oldValue) {
System.out.println( "Reading double [" + name + "]" );
return oldValue;
}
@Override
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
System.out.println( "Writing double [" + name + "]" );
return newValue;
}
@Override
public long readLong(Object obj, String name, long oldValue) {
System.out.println( "Reading long [" + name + "]" );
return oldValue;
}
@Override
public long writeLong(Object obj, String name, long oldValue, long newValue) {
System.out.println( "Writing long [" + name + "]" );
return newValue;
}
@Override
public Object readObject(Object obj, String name, Object oldValue) {
System.out.println( "Reading Object [" + name + "]" );
return oldValue;
}
@Override
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
System.out.println( "Writing Object [" + name + "]" );
return newValue;
}
}
}

View File

@ -0,0 +1,152 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/package org.hibernate.test.bytecode.enhancement.customer;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Version;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Comparator;
@SuppressWarnings("serial")
@Entity
@Table(name="O_CUSTINVENTORY")
@IdClass(CustomerInventoryPK.class)
public class CustomerInventory implements Serializable, Comparator<CustomerInventory> {
public static final String QUERY_COUNT = "CustomerInventory.count";
@Id
@TableGenerator(name="inventory",
table="U_SEQUENCES",
pkColumnName="S_ID",
valueColumnName="S_NEXTNUM",
pkColumnValue="inventory",
allocationSize=1000)
@GeneratedValue(strategy= GenerationType.TABLE,generator="inventory")
@Column(name="CI_ID")
private Long id;
@Id
@Column(name = "CI_CUSTOMERID", insertable = false, updatable = false)
private int custId;
@ManyToOne(cascade= CascadeType.MERGE)
@JoinColumn(name="CI_CUSTOMERID")
private Customer customer;
@ManyToOne(cascade=CascadeType.MERGE)
@JoinColumn(name = "CI_ITEMID")
private String vehicle;
@Column(name="CI_VALUE")
private BigDecimal totalCost;
@Column(name="CI_QUANTITY")
private int quantity;
@Version
@Column(name = "CI_VERSION")
private int version;
public CustomerInventory() {
}
CustomerInventory(Customer customer, String vehicle, int quantity,
BigDecimal totalValue) {
this.customer = customer;
this.vehicle = vehicle;
this.quantity = quantity;
this.totalCost = totalValue;
}
public String getVehicle() {
return vehicle;
}
public BigDecimal getTotalCost() {
return totalCost;
}
public int getQuantity() {
return quantity;
}
public Long getId() {
return id;
}
public Customer getCustomer() {
return customer;
}
public int getCustId() {
return custId;
}
public int getVersion() {
return version;
}
public int compare(CustomerInventory cdb1, CustomerInventory cdb2) {
return cdb1.id.compareTo(cdb2.id);
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof CustomerInventory))
return false;
if (this.id == ((CustomerInventory)obj).id)
return true;
if (this.id != null && ((CustomerInventory)obj).id == null)
return false;
if (this.id == null && ((CustomerInventory)obj).id != null)
return false;
return this.id.equals(((CustomerInventory)obj).id);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + custId;
return result;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.hibernate.test.bytecode.enhancement.customer;
import java.io.Serializable;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class CustomerInventoryPK implements Serializable {
private Long id;
private int custId;
public CustomerInventoryPK() {
}
public CustomerInventoryPK(Long id, int custId) {
this.id = id;
this.custId = custId;
}
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
CustomerInventoryPK cip = (CustomerInventoryPK) other;
return (custId == cip.custId && (id == cip.id ||
( id != null && id.equals(cip.id))));
}
public int hashCode() {
return (id == null ? 0 : id.hashCode()) ^ custId;
}
public Long getId() {
return id;
}
public int getCustId() {
return custId;
}
}

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.bytecode.enhancement.customer;
import javax.persistence.Embeddable;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
@Embeddable
public class SupplierComponentPK {
String componentID;
int supplierID;
public SupplierComponentPK() {
}
public SupplierComponentPK(String suppCompID, int suppCompSuppID) {
this.componentID = suppCompID;
this.supplierID = suppCompSuppID;
}
public String getComponentID() {
return componentID;
}
public int getSupplierID() {
return supplierID;
}
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + componentID.hashCode();
result = PRIME * result + supplierID;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final SupplierComponentPK other = (SupplierComponentPK) obj;
return componentID.equals(other.componentID);
}
}

View File

@ -25,6 +25,7 @@
import java.sql.SQLException;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
@ -579,6 +580,11 @@ public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
// TODO Auto-generated method stub
return null;
}
@Override
public int[] resolveAttributeIndexes(Set<String> attributes) {
return null;
}
}
public static class NoopCollectionPersister implements CollectionPersister {

View File

@ -4,6 +4,7 @@
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
@ -671,4 +672,9 @@ public EntityInstrumentationMetadata getInstrumentationMetadata() {
public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
return new StaticFilterAliasGenerator(rootAlias);
}
@Override
public int[] resolveAttributeIndexes(Set<String> attributes) {
return null;
}
}

View File

@ -23,6 +23,8 @@
import java.io.Serializable;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
@ -599,6 +601,12 @@ public EntityPersister getSubclassEntityPersister(Object instance, SessionFactor
public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) {
return null;
}
@Override
public int[] resolveAttributeIndexes(Set<String> properties) {
// TODO Auto-generated method stub
return null;
}
}
public static class GoofyException extends RuntimeException {