HHH-7072 force recreation of element collections of components with all
nullable properties
This commit is contained in:
parent
fab8b3a04a
commit
171e214458
|
@ -31,6 +31,7 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
@ -52,8 +53,8 @@ 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;
|
||||
|
||||
/**
|
||||
|
@ -650,6 +651,18 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
|||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced
|
|||
private final CascadeStyle[] cascade;
|
||||
private final FetchMode[] joinedFetch;
|
||||
private final boolean isKey;
|
||||
private boolean hasNotNullProperty;
|
||||
|
||||
protected final EntityMode entityMode;
|
||||
protected final ComponentTuplizer componentTuplizer;
|
||||
|
@ -88,6 +89,9 @@ public class ComponentType extends AbstractType implements CompositeType, Proced
|
|||
this.propertyNullability[i] = prop.isNullable();
|
||||
this.cascade[i] = prop.getCascadeStyle();
|
||||
this.joinedFetch[i] = prop.getFetchMode();
|
||||
if (!prop.isNullable()) {
|
||||
hasNotNullProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.entityMode = metamodel.getEntityMode();
|
||||
|
@ -805,4 +809,8 @@ public class ComponentType extends AbstractType implements CompositeType, Proced
|
|||
|
||||
return resolve( values, session, null );
|
||||
}
|
||||
|
||||
public boolean hasNotNullProperty() {
|
||||
return hasNotNullProperty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,4 +71,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue