Hibernate version:any
Problems with Session and transaction handling?
I have Read this:
http://hibernate.org/42.html
Hi I have a problem when I merge an entity with a modified One to many relationship : removed entries in the many side are not deleted (even if I use the same PersistentSet created by hibernate when reading). But this is not the subject:
I have created a utility class that I would like to use as a @PreUpdate inerceptor. The class simply compares the current state of the enity dependency with the one in db and removes the unwanted entries in db (with a single delete sql).
My only problem is that it's impossible for me to join the existing transaction from inside the interceptor. Is there a way to do that?
here's my code for the deletion but it's not relevant to the undersanding of the need.
Code:
package fr.into.common.jpa;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Query;
import com.sun.org.apache.commons.beanutils.BeanUtils;
import fr.into.common.util.DataUtil;
import fr.into.common.util.data.BasicItem;
import fr.into.common.util.data.collections.CollectionsHelper;
/**
* Should be used to handle One to many relationships deletion on update of a
* parent.
*
* @author Zied Hamdi for Into© corporation on 13 févr. 08
*
* www.into-i.fr
*/
public class JPAUtil {
private static class IdExtractor<T>
implements Comparator<T> {
protected Method idGetter;
protected Field idField;
private Object[] getterArgs = {};
private String propertyName;
@SuppressWarnings("unchecked")
public IdExtractor(T first) {
idGetter = findIdGetter( (Class<T>) first.getClass() );
if( idGetter == null ) {
idField = findIdField( (Class<T>) first.getClass() );
if( idField == null )
throw new IllegalArgumentException( "Class " + first.getClass().getName() + " is not an entity: can't find id field or property." );
idField.setAccessible( true );
propertyName = idField.getName();
}
propertyName = idGetter.getName().substring( 3, 4 ).toLowerCase() + idGetter.getName().substring( "getX".length() );
}
public String getPropertyName() {
return propertyName;
}
public Object getId(T entry) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
if( idGetter != null )
return idGetter.invoke( entry, getterArgs );
return idField.get( entry );
}
public Method findIdGetter(Class<T> clazz) {
Method[] methods = clazz.getMethods();
for( Method method : methods ) {
Annotation[] ann = method.getAnnotations();
for( Annotation annotation : ann ) {
if( annotation.annotationType().equals( Id.class ) )
return method;
}
}
return null;
}
public Field findIdField(Class<T> clazz) {
Field[] fields = clazz.getFields();
for( Field field : fields ) {
Annotation[] ann = field.getAnnotations();
for( Annotation annotation : ann ) {
if( annotation.annotationType().equals( Id.class ) )
return field;
}
}
return null;
}
public int compare(T o1, T o2) {
try {
return DataUtil.areEqual( getId( o1 ), getId( o2 ) ) == true ? 0 : 1;
} catch( Exception ex ) {
throw new RuntimeException( ex );
}
}
}
/**
* Deletes elements that are in newValues and not in dbValues. Existence of an
* element is determined by comparing fields anotated with {@link Id}
*
* @author Zied Hamdi for Into© corporation on 13 févr. 08
* @param <T>
* @param dbValues
* @param newValues
* @param em
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static <T> List<T> deletePrivatelyOwned(Collection<T> dbValues, Collection<T> newValues, BasicItem<String, Object> parent, EntityManager em)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
return deletePrivatelyOwned( dbValues, newValues, parent, null, em );
}
/**
* Deletes elements that are in newValues and not in dbValues. Existence of an
* element is determined by {@link Comparator#compare(Object, Object)} (if it
* returns 0 then the element is considered as existing). If comparator is
* null, an id comparison is made.
*
* @author Zied Hamdi for Into© corporation on 13 févr. 08
* @param <T>
* @param dbValues
* @param newValues
* @param comparator
* @param em
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deletePrivatelyOwned(Collection<T> dbValues, Collection<T> newValues, BasicItem<String, Object> parent, Comparator<T> comparator, EntityManager em)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
if( dbValues == null || dbValues.size() == 0 )
return null;
if( newValues == null )
throw new IllegalArgumentException( "newValues can't be null" );
T first = dbValues.iterator().next();
StringBuffer sql = new StringBuffer( "DELETE " + first.getClass().getSimpleName() );
sql.append( " t WHERE" );
sql.append( " t." + parent.getKey() + "=:parentId" );
if( newValues.size() == 0 ) {
Query query = em.createQuery( sql.toString() );
query.setParameter( "parentId", parent.getValue() );
query.executeUpdate();
return new ArrayList<T>( dbValues );
}
IdExtractor<T> idExtractor = new IdExtractor<T>( first );
List<T> toDelete = CollectionsHelper.negativeFilter( dbValues, newValues, comparator != null ? comparator : idExtractor );
if( toDelete.size() == 0 )
return null;
sql.append( " AND" );
String propertyName = idExtractor.getPropertyName();
int counter = 0;
for( ; counter < toDelete.size(); counter++ ) {
sql.append( " t." + propertyName + "=:value" + counter + " OR" );
}
Query query = em.createQuery( sql.substring( 0, sql.length() - " OR".length() ) );
counter = 0;
for( T entry : toDelete ) {
query.setParameter( "value" + counter++, idExtractor.getId( entry ) );
}
query.setParameter( "parentId", parent.getValue() );
query.executeUpdate();
return (List<T>) toDelete;
}
public static void deletePrivatelyOwned(Object entity, String oneToManySetProperty, EntityManager em) {
throw new UnsupportedOperationException();
}
/**
* Unused...
*
* @author Zied Hamdi for Into© corporation on 13 févr. 08
* @param entity
* @param property
* @param depth
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public Object getBeanPropertyValue(Object entity, String property, int depth) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
if( entity == null )
throw new IllegalArgumentException( "Bean can't be null" );
int indexOfLocalProperty = property.indexOf( "." );
String localProperty;
if( indexOfLocalProperty == -1 ) {
localProperty = property;
} else {
localProperty = property.substring( 0, indexOfLocalProperty );
}
if( depth == 0 )
return BeanUtils.getSimpleProperty( entity, localProperty );
return getBeanPropertyValue( entity, property.substring( indexOfLocalProperty + 1 ), depth - 1 );
}
}
Regards,
Zied Hamdi