HHH-8489 - bytecode enhancement: bi-directional associtation management
Closes #933
This commit is contained in:
parent
74161c9bfa
commit
165f037bad
|
@ -67,7 +67,7 @@ public abstract class AttributeTypeDescriptor {
|
||||||
|| currentValue.getType().getName().startsWith( "java.sql.Date" )
|
|| currentValue.getType().getName().startsWith( "java.sql.Date" )
|
||||||
|| currentValue.getType().getName().startsWith( "java.util.Date" )
|
|| currentValue.getType().getName().startsWith( "java.util.Date" )
|
||||||
|| currentValue.getType().getName().startsWith( "java.util.Calendar" ) ) {
|
|| currentValue.getType().getName().startsWith( "java.util.Calendar" ) ) {
|
||||||
builder.append( String.format( "&& ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
builder.append( String.format( " && ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
||||||
}
|
}
|
||||||
// all other objects
|
// all other objects
|
||||||
else {
|
else {
|
||||||
|
@ -81,7 +81,7 @@ public abstract class AttributeTypeDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: for now just call equals, should probably do something else here
|
// TODO: for now just call equals, should probably do something else here
|
||||||
builder.append( String.format( "&& ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
builder.append( String.format( " && ((%s == null) || (!%<s.equals($1))))", currentValue.getName() ) );
|
||||||
}
|
}
|
||||||
builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) );
|
builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,10 @@ import javassist.bytecode.CodeIterator;
|
||||||
import javassist.bytecode.ConstPool;
|
import javassist.bytecode.ConstPool;
|
||||||
import javassist.bytecode.MethodInfo;
|
import javassist.bytecode.MethodInfo;
|
||||||
import javassist.bytecode.Opcode;
|
import javassist.bytecode.Opcode;
|
||||||
|
import javassist.bytecode.SignatureAttribute;
|
||||||
import javassist.bytecode.stackmap.MapMaker;
|
import javassist.bytecode.stackmap.MapMaker;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
import org.hibernate.engine.spi.CompositeOwner;
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
|
@ -45,6 +46,10 @@ import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
import javax.persistence.Embedded;
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -117,7 +122,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
|
|
||||||
// TODO: temporary solution...
|
// TODO: temporary solution...
|
||||||
try {
|
try {
|
||||||
return MethodWriter.write( managedCtClass, "private %s %s() {%n %s%n return this.%s;%n}",
|
return MethodWriter.write( managedCtClass, "public %s %s() {%n %s%n return this.%s;%n}",
|
||||||
persistentField.getType().getName(),
|
persistentField.getType().getName(),
|
||||||
readerName,
|
readerName,
|
||||||
typeDescriptor.buildReadInterceptionBodyFragment( fieldName ),
|
typeDescriptor.buildReadInterceptionBodyFragment( fieldName ),
|
||||||
|
@ -144,7 +149,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
writer = MethodWriter.addSetter( managedCtClass, fieldName, writerName );
|
writer = MethodWriter.addSetter( managedCtClass, fieldName, writerName );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writer = MethodWriter.write( managedCtClass, "private void %s(%s %s) {%n %s%n}",
|
writer = MethodWriter.write( managedCtClass, "public void %s(%s %s) {%n %s%n}",
|
||||||
writerName,
|
writerName,
|
||||||
persistentField.getType().getName(),
|
persistentField.getType().getName(),
|
||||||
fieldName,
|
fieldName,
|
||||||
|
@ -160,36 +165,10 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( enhancementContext, persistentField ) );
|
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( enhancementContext, persistentField ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// composite fields
|
handleCompositeField( managedCtClass, persistentField, writer );
|
||||||
if ( persistentField.hasAnnotation( Embedded.class ) ) {
|
|
||||||
// make sure to add the CompositeOwner interface
|
|
||||||
managedCtClass.addInterface( classPool.get( CompositeOwner.class.getName() ) );
|
|
||||||
|
|
||||||
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
if ( enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) {
|
||||||
// if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method as well
|
handleBiDirectionalAssociation( managedCtClass, persistentField, writer );
|
||||||
MethodWriter.write( managedCtClass, "" +
|
|
||||||
"public void %1$s(String name) {%n" +
|
|
||||||
" if (%2$s != null) { %2$s.callOwner(\".\" + name) ; }%n}",
|
|
||||||
EnhancerConstants.TRACKER_CHANGER_NAME,
|
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup previous owner
|
|
||||||
writer.insertBefore( String.format( "" +
|
|
||||||
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
|
|
||||||
fieldName,
|
|
||||||
CompositeTracker.class.getName(),
|
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER ) );
|
|
||||||
|
|
||||||
// trigger track changes
|
|
||||||
writer.insertAfter( String.format( "" +
|
|
||||||
"((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this);%n" +
|
|
||||||
"%5$s(\"%1$s\");",
|
|
||||||
fieldName,
|
|
||||||
CompositeTracker.class.getName(),
|
|
||||||
CompositeOwner.class.getName(),
|
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
|
||||||
EnhancerConstants.TRACKER_CHANGER_NAME ) );
|
|
||||||
}
|
}
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
@ -205,6 +184,187 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
|
|
||||||
/* --- */
|
/* --- */
|
||||||
|
|
||||||
|
private void handleBiDirectionalAssociation(CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter) throws NotFoundException, CannotCompileException {
|
||||||
|
if ( !isPossibleBiDirectionalAssociation( persistentField ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final CtClass targetEntity = getTargetEntityClass( persistentField );
|
||||||
|
if ( targetEntity == null ) {
|
||||||
|
log.debugf( "Could not find type of bi-directional association for field [%s#%s]", managedCtClass.getName(), persistentField.getName() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String mappedBy = getMappedBy( persistentField, targetEntity );
|
||||||
|
if ( mappedBy.isEmpty() ) {
|
||||||
|
log.debugf( "Could not find bi-directional association for field [%s#%s]", managedCtClass.getName(), persistentField.getName() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a temporary getter and setter on the target entity to be able to compile our code
|
||||||
|
final String mappedByGetterName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy;
|
||||||
|
final String mappedBySetterName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy;
|
||||||
|
MethodWriter.addGetter( targetEntity, mappedBy, mappedByGetterName );
|
||||||
|
MethodWriter.addSetter( targetEntity, mappedBy, mappedBySetterName );
|
||||||
|
|
||||||
|
if ( persistentField.hasAnnotation( OneToOne.class ) ) {
|
||||||
|
// only unset when $1 != null to avoid recursion
|
||||||
|
fieldWriter.insertBefore( String.format( "if ($0.%s != null && $1 != null) $0.%<s.%s(null);%n",
|
||||||
|
persistentField.getName(),
|
||||||
|
mappedBySetterName));
|
||||||
|
fieldWriter.insertAfter( String.format( "if ($1 != null && $1.%s() != $0) $1.%s($0);%n",
|
||||||
|
mappedByGetterName,
|
||||||
|
mappedBySetterName) );
|
||||||
|
}
|
||||||
|
if ( persistentField.hasAnnotation( OneToMany.class ) ) {
|
||||||
|
// only remove elements not in the new collection or else we would loose those elements
|
||||||
|
fieldWriter.insertBefore( String.format( "if ($0.%s != null) for (java.util.Iterator itr = $0.%<s.iterator(); itr.hasNext(); ) { %s target = (%<s) itr.next(); if ($1 == null || !$1.contains(target)) target.%s(null); }%n",
|
||||||
|
persistentField.getName(),
|
||||||
|
targetEntity.getName(),
|
||||||
|
mappedBySetterName ) );
|
||||||
|
fieldWriter.insertAfter( String.format( "if ($1 != null) for (java.util.Iterator itr = $1.iterator(); itr.hasNext(); ) { %s target = (%<s) itr.next(); if (target.%s() != $0) target.%s((%s)$0); }%n",
|
||||||
|
targetEntity.getName(),
|
||||||
|
mappedByGetterName,
|
||||||
|
mappedBySetterName,
|
||||||
|
managedCtClass.getName() ) );
|
||||||
|
}
|
||||||
|
if ( persistentField.hasAnnotation( ManyToOne.class ) ) {
|
||||||
|
fieldWriter.insertBefore( String.format( "if ($0.%1$s != null && $0.%1$s.%2$s() != null) $0.%1$s.%2$s().remove($0);%n",
|
||||||
|
persistentField.getName(),
|
||||||
|
mappedByGetterName) );
|
||||||
|
// check .contains($0) to avoid double inserts (but preventing duplicates)
|
||||||
|
fieldWriter.insertAfter( String.format( "if ($1 != null && $1.%s() != null && !$1.%<s().contains($0) ) $1.%<s().add($0);%n",
|
||||||
|
mappedByGetterName));
|
||||||
|
}
|
||||||
|
if ( persistentField.hasAnnotation( ManyToMany.class ) ) {
|
||||||
|
fieldWriter.insertBefore( String.format( "if ($0.%s != null) for (java.util.Iterator itr = $0.%<s.iterator(); itr.hasNext(); ) { %s target = (%<s) itr.next(); if ($1 == null || !$1.contains(target)) target.%s().remove($0); }%n",
|
||||||
|
persistentField.getName(),
|
||||||
|
targetEntity.getName(),
|
||||||
|
mappedByGetterName));
|
||||||
|
fieldWriter.insertAfter( String.format( "if ($1 != null) for (java.util.Iterator itr = $1.iterator(); itr.hasNext(); ) { %s target = (%<s) itr.next(); if (target.%s() != $0 && target.%<s() != null) target.%<s().add($0); }%n",
|
||||||
|
targetEntity.getName(),
|
||||||
|
mappedByGetterName ) );
|
||||||
|
}
|
||||||
|
// implementation note: association management @OneToMany and @ManyToMay works for add() operations but for remove() a snapshot of the collection is needed so we know what associations to break.
|
||||||
|
// another approach that could force that behavior would be to return Collections.unmodifiableCollection() ...
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPossibleBiDirectionalAssociation(CtField persistentField) {
|
||||||
|
return persistentField.hasAnnotation( OneToOne.class ) ||
|
||||||
|
persistentField.hasAnnotation( OneToMany.class ) ||
|
||||||
|
persistentField.hasAnnotation( ManyToOne.class) ||
|
||||||
|
persistentField.hasAnnotation( ManyToMany.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMappedBy(CtField persistentField, CtClass targetEntity) {
|
||||||
|
final String local = getMappedByFromAnnotation( persistentField );
|
||||||
|
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity ) : local;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMappedByFromAnnotation(CtField persistentField) {
|
||||||
|
try {
|
||||||
|
if (persistentField.hasAnnotation( OneToOne.class )) {
|
||||||
|
return ((OneToOne) persistentField.getAnnotation( OneToOne.class )).mappedBy();
|
||||||
|
}
|
||||||
|
if (persistentField.hasAnnotation( OneToMany.class )) {
|
||||||
|
return ((OneToMany) persistentField.getAnnotation( OneToMany.class )).mappedBy();
|
||||||
|
}
|
||||||
|
// For @ManyToOne associations, mappedBy must come from the @OneToMany side of the association
|
||||||
|
if (persistentField.hasAnnotation( ManyToMany.class )) {
|
||||||
|
return ((ManyToMany) persistentField.getAnnotation( ManyToMany.class )).mappedBy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ignore) {
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMappedByFromTargetEntity(CtField persistentField, CtClass targetEntity) {
|
||||||
|
// get mappedBy value by searching in the fields of the target entity class
|
||||||
|
for (CtField f : targetEntity.getDeclaredFields() ) {
|
||||||
|
if ( enhancementContext.isPersistentField( f ) && getMappedByFromAnnotation( f ).equals( persistentField.getName() ) ) {
|
||||||
|
log.debugf( "mappedBy association for field [%s:%s] is [%s:%s]", persistentField.getDeclaringClass().getName(), persistentField.getName(), targetEntity.getName(), f.getName() );
|
||||||
|
return f.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CtClass getTargetEntityClass(CtField persistentField) throws NotFoundException {
|
||||||
|
// get targetEntity defined in the annotation
|
||||||
|
try {
|
||||||
|
Class<?> targetClass = null;
|
||||||
|
if (persistentField.hasAnnotation( OneToOne.class )) {
|
||||||
|
targetClass = ((OneToOne) persistentField.getAnnotation( OneToOne.class )).targetEntity();
|
||||||
|
}
|
||||||
|
if (persistentField.hasAnnotation( OneToMany.class )) {
|
||||||
|
targetClass = ((OneToMany) persistentField.getAnnotation( OneToMany.class )).targetEntity();
|
||||||
|
}
|
||||||
|
if (persistentField.hasAnnotation( ManyToOne.class )) {
|
||||||
|
targetClass = ((ManyToOne) persistentField.getAnnotation( ManyToOne.class )).targetEntity();
|
||||||
|
}
|
||||||
|
if (persistentField.hasAnnotation( ManyToMany.class )) {
|
||||||
|
targetClass = ((ManyToMany) persistentField.getAnnotation( ManyToMany.class )).targetEntity();
|
||||||
|
}
|
||||||
|
if ( targetClass != null && targetClass != void.class ) {
|
||||||
|
return classPool.get( targetClass.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// infer targetEntity from generic type signature
|
||||||
|
if ( persistentField.hasAnnotation( OneToOne.class ) || persistentField.hasAnnotation( ManyToOne.class )) {
|
||||||
|
return persistentField.getType();
|
||||||
|
}
|
||||||
|
if ( persistentField.hasAnnotation( OneToMany.class ) || persistentField.hasAnnotation( ManyToMany.class )) {
|
||||||
|
try {
|
||||||
|
final SignatureAttribute.TypeArgument target = ((SignatureAttribute.ClassType) SignatureAttribute.toFieldSignature( persistentField.getGenericSignature() )).getTypeArguments()[0];
|
||||||
|
return persistentField.getDeclaringClass().getClassPool().get( target.toString() );
|
||||||
|
}
|
||||||
|
catch (BadBytecode ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
private void handleCompositeField(CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter) throws NotFoundException, CannotCompileException {
|
||||||
|
if ( !persistentField.hasAnnotation( Embedded.class ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to add the CompositeOwner interface
|
||||||
|
managedCtClass.addInterface( classPool.get( CompositeOwner.class.getName() ) );
|
||||||
|
|
||||||
|
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||||
|
// if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method as well
|
||||||
|
MethodWriter.write( managedCtClass, "" +
|
||||||
|
"public void %1$s(String name) {%n" +
|
||||||
|
" if (%2$s != null) { %2$s.callOwner(\".\" + name) ; }%n}",
|
||||||
|
EnhancerConstants.TRACKER_CHANGER_NAME,
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup previous owner
|
||||||
|
fieldWriter.insertBefore( String.format( "" +
|
||||||
|
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
|
||||||
|
persistentField.getName(),
|
||||||
|
CompositeTracker.class.getName(),
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER) );
|
||||||
|
|
||||||
|
// trigger track changes
|
||||||
|
fieldWriter.insertAfter( String.format( "" +
|
||||||
|
"((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this);%n" +
|
||||||
|
"%5$s(\"%1$s\");",
|
||||||
|
persistentField.getName(),
|
||||||
|
CompositeTracker.class.getName(),
|
||||||
|
CompositeOwner.class.getName(),
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
||||||
|
EnhancerConstants.TRACKER_CHANGER_NAME ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
protected void enhanceAttributesAccess(CtClass managedCtClass, IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
|
protected void enhanceAttributesAccess(CtClass managedCtClass, IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
|
||||||
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
|
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,13 @@ public class DefaultEnhancementContext implements EnhancementContext {
|
||||||
return classDescriptor.hasAnnotation( Embeddable.class );
|
return classDescriptor.hasAnnotation( Embeddable.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -69,6 +69,17 @@ public interface EnhancementContext {
|
||||||
*/
|
*/
|
||||||
public boolean isCompositeClass(CtClass classDescriptor);
|
public boolean isCompositeClass(CtClass classDescriptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we manage association of bi-directional persistent attributes for this field?
|
||||||
|
*
|
||||||
|
* @param field The field to check.
|
||||||
|
*
|
||||||
|
* @return {@code true} indicates that the field is enhanced so that for bi-directional persistent fields
|
||||||
|
* the association is managed, i.e. the associations are automatically set; {@code false} indicates that
|
||||||
|
* the management is handled by the user.
|
||||||
|
*/
|
||||||
|
public boolean doBiDirectionalAssociationManagement(CtField field);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we in-line dirty checking for persistent attributes for this class?
|
* Should we in-line dirty checking for persistent attributes for this class?
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,23 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.tool.enhance;
|
package org.hibernate.tool.enhance;
|
||||||
|
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtField;
|
||||||
|
import org.apache.tools.ant.BuildException;
|
||||||
|
import org.apache.tools.ant.DirectoryScanner;
|
||||||
|
import org.apache.tools.ant.Project;
|
||||||
|
import org.apache.tools.ant.Task;
|
||||||
|
import org.apache.tools.ant.types.FileSet;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
|
||||||
|
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.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -30,25 +47,6 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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 javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
|
||||||
import org.apache.tools.ant.Project;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
import org.apache.tools.ant.types.FileSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ant task for performing build-time enhancement of entities and component/embeddable classes.
|
* Ant task for performing build-time enhancement of entities and component/embeddable classes.
|
||||||
|
@ -74,7 +72,7 @@ public class EnhancementTask extends Task implements EnhancementContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws BuildException {
|
public void execute() throws BuildException {
|
||||||
log( "Starting Hibernate EnhancementTask execution", Project.MSG_INFO );
|
log("Starting Hibernate EnhancementTask execution", Project.MSG_INFO);
|
||||||
|
|
||||||
// we use the CtClass stuff here just as a simple vehicle for obtaining low level information about
|
// we use the CtClass stuff here just as a simple vehicle for obtaining low level information about
|
||||||
// the class(es) contained in a file while still maintaining easy access to the underlying byte[]
|
// the class(es) contained in a file while still maintaining easy access to the underlying byte[]
|
||||||
|
@ -116,7 +114,7 @@ public class EnhancementTask extends Task implements EnhancementContext {
|
||||||
|
|
||||||
private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {
|
private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {
|
||||||
try {
|
try {
|
||||||
byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
|
byte[] result = enhancer.enhance(ctClass.getName(), ctClass.toBytecode());
|
||||||
if(result != null)
|
if(result != null)
|
||||||
writeEnhancedClass(javaClassFile, result);
|
writeEnhancedClass(javaClassFile, result);
|
||||||
}
|
}
|
||||||
|
@ -187,6 +185,11 @@ public class EnhancementTask extends Task implements EnhancementContext {
|
||||||
return classDescriptor.hasAnnotation(Embeddable.class);
|
return classDescriptor.hasAnnotation(Embeddable.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -30,16 +30,24 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||||
import org.hibernate.test.bytecode.enhancement.entity.customer.Address;
|
import org.hibernate.test.bytecode.enhancement.entity.customer.Address;
|
||||||
import org.hibernate.test.bytecode.enhancement.entity.customer.Customer;
|
import org.hibernate.test.bytecode.enhancement.entity.customer.Customer;
|
||||||
import org.hibernate.test.bytecode.enhancement.entity.customer.CustomerInventory;
|
import org.hibernate.test.bytecode.enhancement.entity.customer.CustomerInventory;
|
||||||
|
import org.hibernate.test.bytecode.enhancement.entity.customer.Group;
|
||||||
import org.hibernate.test.bytecode.enhancement.entity.customer.SupplierComponentPK;
|
import org.hibernate.test.bytecode.enhancement.entity.customer.SupplierComponentPK;
|
||||||
|
import org.hibernate.test.bytecode.enhancement.entity.customer.User;
|
||||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -52,7 +60,8 @@ public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testFor(Class entityClassToEnhance) throws Exception {
|
private void testFor(Class entityClassToEnhance) throws Exception {
|
||||||
ClassLoader cl = new ClassLoader() {};
|
ClassLoader cl = new ClassLoader() {
|
||||||
|
};
|
||||||
|
|
||||||
// just for debugging
|
// just for debugging
|
||||||
Class<?> addressClass = EnhancerTestUtils.enhanceAndDecompile(Address.class, cl);
|
Class<?> addressClass = EnhancerTestUtils.enhanceAndDecompile(Address.class, cl);
|
||||||
|
@ -69,7 +78,7 @@ public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||||
assertNull(getter.invoke(entityInstance));
|
assertNull(getter.invoke(entityInstance));
|
||||||
setter.invoke(entityInstance, EnhancerTestUtils.makeEntityEntry());
|
setter.invoke(entityInstance, EnhancerTestUtils.makeEntityEntry());
|
||||||
assertNotNull(getter.invoke(entityInstance));
|
assertNotNull(getter.invoke(entityInstance));
|
||||||
setter.invoke(entityInstance, new Object[] {null});
|
setter.invoke(entityInstance, new Object[]{null});
|
||||||
assertNull(getter.invoke(entityInstance));
|
assertNull(getter.invoke(entityInstance));
|
||||||
|
|
||||||
Method entityInstanceGetter = entityClass.getMethod(EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME);
|
Method entityInstanceGetter = entityClass.getMethod(EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME);
|
||||||
|
@ -83,7 +92,7 @@ public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||||
Method nextGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
Method nextGetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_GETTER_NAME);
|
||||||
Method nextSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
Method nextSetter = entityClass.getMethod(EnhancerConstants.PREVIOUS_SETTER_NAME, ManagedEntity.class);
|
||||||
nextSetter.invoke(entityInstance, entityInstance);
|
nextSetter.invoke(entityInstance, entityInstance);
|
||||||
assertSame( entityInstance, nextGetter.invoke(entityInstance));
|
assertSame(entityInstance, nextGetter.invoke(entityInstance));
|
||||||
|
|
||||||
// add an attribute interceptor...
|
// add an attribute interceptor...
|
||||||
assertNull(entityClass.getMethod(EnhancerConstants.INTERCEPTOR_GETTER_NAME).invoke(entityInstance));
|
assertNull(entityClass.getMethod(EnhancerConstants.INTERCEPTOR_GETTER_NAME).invoke(entityInstance));
|
||||||
|
@ -120,4 +129,86 @@ public class CustomerEnhancerTest extends BaseUnitTestCase {
|
||||||
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address");
|
EnhancerTestUtils.checkDirtyTracking(entityInstance, "address");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBiDirectionalAssociationManagement() throws Exception {
|
||||||
|
ClassLoader cl = new ClassLoader() {
|
||||||
|
};
|
||||||
|
|
||||||
|
Class<?> userClass = EnhancerTestUtils.enhanceAndDecompile(User.class, cl);
|
||||||
|
Class<?> groupClass = EnhancerTestUtils.enhanceAndDecompile(Group.class, cl);
|
||||||
|
Class<?> customerClass = EnhancerTestUtils.enhanceAndDecompile(Customer.class, cl);
|
||||||
|
Class<?> customerInventoryClass = EnhancerTestUtils.enhanceAndDecompile(CustomerInventory.class, cl);
|
||||||
|
|
||||||
|
Object userInstance = userClass.newInstance();
|
||||||
|
assertTyping(ManagedEntity.class, userInstance);
|
||||||
|
|
||||||
|
Object groupInstance = groupClass.newInstance();
|
||||||
|
assertTyping(ManagedEntity.class, groupInstance);
|
||||||
|
|
||||||
|
Object customerInstance = customerClass.newInstance();
|
||||||
|
assertTyping(ManagedEntity.class, customerInstance);
|
||||||
|
|
||||||
|
Object customerInventoryInstance = customerInventoryClass.newInstance();
|
||||||
|
assertTyping(ManagedEntity.class, customerInventoryInstance);
|
||||||
|
|
||||||
|
Method interceptorSetter = userClass.getMethod(EnhancerConstants.INTERCEPTOR_SETTER_NAME, PersistentAttributeInterceptor.class);
|
||||||
|
interceptorSetter.invoke(userInstance, new EnhancerTestUtils.LocalPersistentAttributeInterceptor());
|
||||||
|
|
||||||
|
/* --- @OneToOne */
|
||||||
|
|
||||||
|
userClass.getMethod("setLogin", String.class).invoke(userInstance, UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
customerClass.getMethod("setUser", userClass).invoke(customerInstance, userInstance);
|
||||||
|
assertEquals(customerInstance, userClass.getMethod("getCustomer").invoke(userInstance));
|
||||||
|
|
||||||
|
// check dirty tracking is set automatically with bi-directional association management
|
||||||
|
EnhancerTestUtils.checkDirtyTracking(userInstance, "login", "customer");
|
||||||
|
|
||||||
|
Object anotherUser = userClass.newInstance();
|
||||||
|
userClass.getMethod("setLogin", String.class).invoke(anotherUser, UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
customerClass.getMethod("setUser", userClass).invoke(customerInstance, anotherUser);
|
||||||
|
assertEquals(null, userClass.getMethod("getCustomer").invoke(userInstance));
|
||||||
|
assertEquals(customerInstance, userClass.getMethod("getCustomer").invoke(anotherUser));
|
||||||
|
|
||||||
|
userClass.getMethod("setCustomer", customerClass).invoke(userInstance, customerClass.newInstance());
|
||||||
|
assertEquals(userInstance, customerClass.getMethod("getUser").invoke(userClass.getMethod("getCustomer").invoke(userInstance)));
|
||||||
|
|
||||||
|
/* --- @OneToMany @ManyToOne */
|
||||||
|
|
||||||
|
assertTrue(((Collection<?>) customerClass.getMethod("getInventories").invoke(customerInstance)).isEmpty());
|
||||||
|
customerInventoryClass.getMethod("setCustomer", customerClass).invoke(customerInventoryInstance, customerInstance);
|
||||||
|
|
||||||
|
Collection<?> inventories = (Collection < ?>) customerClass.getMethod("getInventories").invoke(customerInstance);
|
||||||
|
assertTrue(inventories.size() == 1);
|
||||||
|
assertTrue(inventories.contains(customerInventoryInstance));
|
||||||
|
|
||||||
|
Object anotherCustomer = customerClass.newInstance();
|
||||||
|
customerInventoryClass.getMethod("setCustomer", customerClass).invoke(customerInventoryInstance, anotherCustomer);
|
||||||
|
assertTrue(((Collection<?>) customerClass.getMethod("getInventories").invoke(customerInstance)).isEmpty());
|
||||||
|
|
||||||
|
customerClass.getMethod("addInventory", customerInventoryClass).invoke(customerInstance, customerInventoryInstance);
|
||||||
|
assertTrue(customerInventoryClass.getMethod("getCustomer").invoke(customerInventoryInstance) == customerInstance);
|
||||||
|
|
||||||
|
inventories = (Collection < ?>) customerClass.getMethod("getInventories").invoke(customerInstance);
|
||||||
|
assertTrue(inventories.size() == 1);
|
||||||
|
|
||||||
|
customerClass.getMethod("addInventory", customerInventoryClass).invoke(customerInstance, customerInventoryClass.newInstance());
|
||||||
|
assertTrue(((Collection<?>) customerClass.getMethod("getInventories").invoke(customerInstance)).size() == 2);
|
||||||
|
|
||||||
|
/* --- @ManyToMany */
|
||||||
|
|
||||||
|
Object anotherGroup = groupClass.newInstance();
|
||||||
|
userClass.getMethod("addGroup", groupClass).invoke(userInstance, groupInstance);
|
||||||
|
userClass.getMethod("addGroup", groupClass).invoke(userInstance, anotherGroup);
|
||||||
|
userClass.getMethod("addGroup", groupClass).invoke(anotherUser, groupInstance);
|
||||||
|
|
||||||
|
assertTrue(((Collection<?>) groupClass.getMethod("getUsers").invoke(groupInstance)).size() == 2);
|
||||||
|
|
||||||
|
groupClass.getMethod("setUsers", Set.class).invoke(groupInstance, new HashSet());
|
||||||
|
assertTrue(((Collection<?>) userClass.getMethod("getGroups").invoke(userInstance)).size() == 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import javax.persistence.Entity;
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
@ -57,6 +58,9 @@ public class Customer {
|
||||||
@Column(name="C_ID")
|
@Column(name="C_ID")
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
private User user;
|
||||||
|
|
||||||
@Column(name="C_FIRST")
|
@Column(name="C_FIRST")
|
||||||
private String firstName;
|
private String firstName;
|
||||||
|
|
||||||
|
@ -209,6 +213,20 @@ public class Customer {
|
||||||
return customerInventories;
|
return customerInventories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInventory(CustomerInventory inventory) {
|
||||||
|
List<CustomerInventory> list = getInventories();
|
||||||
|
list.add(inventory);
|
||||||
|
customerInventories = list;
|
||||||
|
}
|
||||||
|
|
||||||
public CustomerInventory addInventory(String item, int quantity,
|
public CustomerInventory addInventory(String item, int quantity,
|
||||||
BigDecimal totalValue) {
|
BigDecimal totalValue) {
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,10 @@ public class CustomerInventory implements Serializable, Comparator<CustomerInven
|
||||||
return customer;
|
return customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCustomer(Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
public int getCustId() {
|
public int getCustId() {
|
||||||
return custId;
|
return custId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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.entity.customer;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "GROUP")
|
||||||
|
@SequenceGenerator(name = "GROUP_SEQUENCE", sequenceName = "GROUP_SEQUENCE", allocationSize = 1, initialValue = 0)
|
||||||
|
public class Group {
|
||||||
|
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "GROUP_SEQUENCE")
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToMany(mappedBy = "groups")
|
||||||
|
private Set<User> users = new HashSet<User>();
|
||||||
|
|
||||||
|
public Group() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<User> getUsers() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsers(Set<User> users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeUser(User user) {
|
||||||
|
Set<User> set = this.users;
|
||||||
|
set.remove(user);
|
||||||
|
this.users = set;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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.entity.customer;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "USER")
|
||||||
|
@SequenceGenerator(name = "USER_SEQUENCE", sequenceName = "USER_SEQUENCE", allocationSize = 1, initialValue = 0)
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_SEQUENCE")
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "user")
|
||||||
|
private Customer customer;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
private Set<Group> groups;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomer(Customer customer) {
|
||||||
|
this.customer = customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGroup(Group group) {
|
||||||
|
Set<Group> set = (groups == null ? new HashSet<Group>() : groups);
|
||||||
|
set.add(group);
|
||||||
|
groups = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Group> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(Set<Group> groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,26 +23,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.enhance.plugins;
|
package org.hibernate.bytecode.enhance.plugins;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javassist.ClassPool;
|
import javassist.ClassPool;
|
||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
||||||
import javassist.CtField;
|
import javassist.CtField;
|
||||||
|
|
||||||
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 org.apache.maven.plugin.AbstractMojo;
|
import org.apache.maven.plugin.AbstractMojo;
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
import org.apache.maven.plugin.MojoFailureException;
|
import org.apache.maven.plugin.MojoFailureException;
|
||||||
|
@ -53,6 +36,21 @@ import org.apache.maven.plugins.annotations.Parameter;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
|
||||||
|
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.FileFilter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This plugin will enhance Entity objects.
|
* This plugin will enhance Entity objects.
|
||||||
*
|
*
|
||||||
|
@ -228,6 +226,11 @@ public class MavenEnhancePlugin extends AbstractMojo implements EnhancementConte
|
||||||
return classDescriptor.hasAnnotation(Embeddable.class);
|
return classDescriptor.hasAnnotation(Embeddable.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -146,6 +146,10 @@ public class EnhancerTask extends DefaultTask implements EnhancementContext {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue