HHH-7072 force recreation of element collections of components with all

nullable properties
Conflicts:
	hibernate-core/src/main/java/org/hibernate/type/ComponentType.java
	hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/embeddables/withcustomenumdef/Location.java
This commit is contained in:
Brett Meyer 2014-03-04 14:40:39 -05:00
parent d22c60cc68
commit 677ac13458
4 changed files with 84 additions and 3 deletions

View File

@ -53,6 +53,7 @@ import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
@ -660,6 +661,18 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
* Do we need to completely recreate this collection when it changes?
*/
public boolean needsRecreate(CollectionPersister persister) {
// Workaround for situations like HHH-7072. If the collection element is a component that consists entirely
// of nullable properties, we currently have to forcefully recreate the entire collection. See the use
// of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info. In order to delete
// row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
// the current "WHERE COL = ?" (fails for null for most DBs). Note that
// the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the
// AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing
// recreation isn't ideal, but not really any other option in ORM 4.
if (persister.getElementType() instanceof ComponentType) {
ComponentType componentType = (ComponentType) persister.getElementType();
return !componentType.hasNotNullProperty();
}
return false;
}

View File

@ -65,6 +65,7 @@ public class ComponentType extends AbstractType implements CompositeType {
private final CascadeStyle[] cascade;
private final FetchMode[] joinedFetch;
private final boolean isKey;
private boolean hasNotNullProperty;
protected final EntityMode entityMode;
protected final ComponentTuplizer componentTuplizer;
@ -87,6 +88,9 @@ public class ComponentType extends AbstractType implements CompositeType {
this.propertyNullability[i] = prop.isNullable();
this.cascade[i] = prop.getCascadeStyle();
this.joinedFetch[i] = prop.getFetchMode();
if (!prop.isNullable()) {
hasNotNullProperty = true;
}
}
this.entityMode = metamodel.getEntityMode();
@ -723,4 +727,8 @@ public class ComponentType extends AbstractType implements CompositeType {
"Unable to locate property named " + name + " on " + getReturnedClass().getName()
);
}
public boolean hasNotNullProperty() {
return hasNotNullProperty;
}
}

View File

@ -69,4 +69,25 @@ public class Location {
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (! obj.getClass().equals( Location.class )) {
return false;
}
Location loc = (Location) obj;
if (name != null ? !name.equals(loc.name) : loc.name != null) return false;
if (type != null ? !type.equals(loc.type) : loc.type != null) return false;
return true;
}
@Override
public int hashCode() {
int result;
result = (name != null ? name.hashCode() : 0);
result = 31 * result + (type != null ? type.hashCode() : 0);
return result;
}
}

View File

@ -23,12 +23,14 @@
*/
package org.hibernate.test.annotations.collectionelement.embeddables.withcustomenumdef;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import java.util.Iterator;
import org.hibernate.Session;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static junit.framework.Assert.assertEquals;
import org.junit.Test;
/**
* @author Steve Ebersole
@ -57,4 +59,41 @@ public class TestBasicOps extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-7072")
public void testEmbeddableWithNullables() {
Session s = openSession();
s.beginTransaction();
Query q = new Query( new Location( null, Location.Type.COMMUNE ) );
s.save( q );
s.getTransaction().commit();
s.clear();
s.beginTransaction();
q.getIncludedLocations().add( new Location( null, Location.Type.COUNTY ) );
s.update( q );
s.getTransaction().commit();
s.clear();
s.beginTransaction();
q = (Query) s.get( Query.class, q.getId() );
// assertEquals( 2, q.getIncludedLocations().size() );
s.getTransaction().commit();
s.clear();
s.beginTransaction();
Iterator<Location> itr = q.getIncludedLocations().iterator();
itr.next();
itr.remove();
s.update( q );
s.getTransaction().commit();
s.clear();
s.beginTransaction();
q = (Query) s.get( Query.class, q.getId() );
assertEquals( 1, q.getIncludedLocations().size() );
s.getTransaction().commit();
s.close();
}
}