private CtMethod generateFieldReader( CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) { final String fieldName = persistentField.getName(); 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 if (!enhancementContext.isLazyLoadable(persistentField)) { return MethodWriter.addGetter(managedCtClass, fieldName, readerName); } try { return MethodWriter.write( managedCtClass, "public %s %s() {%n %s%n return this.%s;%n}", persistentField.getType().getName(), readerName, typeDescriptor.buildReadInterceptionBodyFragment(fieldName), fieldName); } catch (CannotCompileException cce) { final String msg = String.format( "Could not enhance entity class [%s] to add field reader method [%s]", managedCtClass.getName(), readerName); throw new EnhancementException(msg, cce); } catch (NotFoundException nfe) { final String msg = String.format( "Could not enhance entity class [%s] to add field reader method [%s]", managedCtClass.getName(), readerName); throw new EnhancementException(msg, nfe); } }
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.warnf( "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 // don't use iterator to avoid ConcurrentModException fieldWriter.insertBefore( String.format( "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s(null); } }%n", persistentField.getName(), targetEntity.getName(), mappedBySetterName)); fieldWriter.insertAfter( String.format( "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; 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) { java.util.Collection c = $1.%s(); if (c != null && !c.contains($0)) c.add($0); }%n", mappedByGetterName)); } if (persistentField.hasAnnotation(ManyToMany.class)) { fieldWriter.insertBefore( String.format( "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s().remove($0); } }%n", persistentField.getName(), targetEntity.getName(), mappedByGetterName)); fieldWriter.insertAfter( String.format( "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; java.util.Collection c = target.%s(); if ( c != $0 && c != null) c.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() ... }