Hibernate SVN: r16521 - validator/trunk/hibernate-validator-tck-runner.
by hibernate-commits@lists.jboss.org
Author: jcosta(a)redhat.com
Date: 2009-05-07 14:38:51 -0400 (Thu, 07 May 2009)
New Revision: 16521
Modified:
validator/trunk/hibernate-validator-tck-runner/pom.xml
Log:
Fixed version of jboss-test-harness-jboss-as-50, from 1.0.0.Beta2 to 1.0.0.BETA2
Modified: validator/trunk/hibernate-validator-tck-runner/pom.xml
===================================================================
--- validator/trunk/hibernate-validator-tck-runner/pom.xml 2009-05-07 15:23:38 UTC (rev 16520)
+++ validator/trunk/hibernate-validator-tck-runner/pom.xml 2009-05-07 18:38:51 UTC (rev 16521)
@@ -37,7 +37,7 @@
<dependency>
<groupId>org.jboss.test-harness</groupId>
<artifactId>jboss-test-harness-jboss-as-50</artifactId>
- <version>1.0.0.Beta2</version>
+ <version>1.0.0.BETA2</version>
</dependency>
</dependencies>
15 years, 6 months
Hibernate SVN: r16520 - beanvalidation/trunk/validation-tck.
by hibernate-commits@lists.jboss.org
Author: jcosta(a)redhat.com
Date: 2009-05-07 11:23:38 -0400 (Thu, 07 May 2009)
New Revision: 16520
Modified:
beanvalidation/trunk/validation-tck/pom.xml
Log:
Changed jboss-test-harness-api and jboss-test-harness versions, from 1.0.0.Beta2 to 1.0.0.BETA2
Modified: beanvalidation/trunk/validation-tck/pom.xml
===================================================================
--- beanvalidation/trunk/validation-tck/pom.xml 2009-05-07 14:33:00 UTC (rev 16519)
+++ beanvalidation/trunk/validation-tck/pom.xml 2009-05-07 15:23:38 UTC (rev 16520)
@@ -59,12 +59,12 @@
<dependency>
<groupId>org.jboss.test-harness</groupId>
<artifactId>jboss-test-harness-api</artifactId>
- <version>1.0.0.Beta2</version>
+ <version>1.0.0.BETA2</version>
</dependency>
<dependency>
<groupId>org.jboss.test-harness</groupId>
<artifactId>jboss-test-harness</artifactId>
- <version>1.0.0.Beta2</version>
+ <version>1.0.0.BETA2</version>
</dependency>
</dependencies>
15 years, 6 months
Hibernate SVN: r16518 - core/trunk/annotations.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-05-07 08:37:21 -0400 (Thu, 07 May 2009)
New Revision: 16518
Modified:
core/trunk/annotations/pom.xml
Log:
set latest validator
Modified: core/trunk/annotations/pom.xml
===================================================================
--- core/trunk/annotations/pom.xml 2009-05-07 12:00:09 UTC (rev 16517)
+++ core/trunk/annotations/pom.xml 2009-05-07 12:37:21 UTC (rev 16518)
@@ -14,7 +14,7 @@
~ 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.
+ ~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
@@ -113,12 +113,12 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
- <version>4.0.0.Beta1</version>
+ <version>4.0.0.Beta2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
- <version>1.0.CR2</version>
+ <version>1.0.CR3-SNAPSHOT</version>
</dependency>
</dependencies>
@@ -160,6 +160,11 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.twdata.maven</groupId>
+ <artifactId>maven-cli-plugin</artifactId>
+ <version>0.6.3.CR2</version>
+ </plugin>
</plugins>
</build>
15 years, 6 months
Hibernate SVN: r16517 - beanvalidation/trunk/validation-api/src/main/java/javax/validation.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-05-07 08:00:09 -0400 (Thu, 07 May 2009)
New Revision: 16517
Modified:
beanvalidation/trunk/validation-api/src/main/java/javax/validation/ConstraintViolationException.java
Log:
BVAL-153 ConstraintViolationException now receives <?>
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/ConstraintViolationException.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/ConstraintViolationException.java 2009-05-06 15:52:26 UTC (rev 16516)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/ConstraintViolationException.java 2009-05-07 12:00:09 UTC (rev 16517)
@@ -21,11 +21,11 @@
/**
* Report the result of constraint violations
- *
+ * `
* @author Emmanuel Bernard
*/
public class ConstraintViolationException extends ValidationException {
- private final Set<ConstraintViolation> constraintViolations;
+ private final Set<ConstraintViolation<?>> constraintViolations;
/**
* Creates a constraint violation report
@@ -33,8 +33,8 @@
* @param message error message
* @param constraintViolations Set of ConstraintViolation
*/
- public ConstraintViolationException(String message,
- Set<ConstraintViolation> constraintViolations) {
+ public <T> ConstraintViolationException(String message,
+ Set<ConstraintViolation<?>> constraintViolations) {
super( message );
this.constraintViolations = constraintViolations;
}
@@ -44,7 +44,7 @@
*
* @param constraintViolations Set of ConstraintViolation
*/
- public ConstraintViolationException(Set<ConstraintViolation> constraintViolations) {
+ public <T> ConstraintViolationException(Set<ConstraintViolation<?>> constraintViolations) {
super();
this.constraintViolations = constraintViolations;
}
@@ -54,7 +54,7 @@
*
* @return Set of CosntraintViolation
*/
- public Set<ConstraintViolation> getConstraintViolations() {
+ public Set<ConstraintViolation<?>> getConstraintViolations() {
return constraintViolations;
}
}
15 years, 6 months
Hibernate SVN: r16516 - in core/trunk/annotations/src: main/java/org/hibernate/cfg/beanvalidation and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-05-06 11:52:26 -0400 (Wed, 06 May 2009)
New Revision: 16516
Added:
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
Log:
ANN-828 Use Bean Validation for DDL generation
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -396,8 +396,13 @@
}
}
}
+ applyDDLOnBeanValidation( (Collection<PersistentClass>) classes.values(), getProperties() );
}
+ private void applyDDLOnBeanValidation(Collection<PersistentClass> persistentClasses, Properties properties) {
+ BeanValidationActivator.applyDDL( persistentClasses, properties );
+ }
+
/**
* Processes FKSecondPass instances trying to resolve any
* graph circularity (ie PK made of a many to one linking to
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java 2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -2,6 +2,9 @@
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
@@ -11,6 +14,8 @@
import org.hibernate.util.ReflectHelper;
import org.hibernate.HibernateException;
import org.hibernate.AssertionFailure;
+import org.hibernate.cfg.Environment;
+import org.hibernate.mapping.PersistentClass;
import org.hibernate.event.EventListeners;
/**
@@ -22,34 +27,37 @@
private static final String BV_DISCOVERY_CLASS = "javax.validation.Validation";
private static final String TYPE_SAFE_ACTIVATOR_CLASS = "org.hibernate.cfg.beanvalidation.TypeSafeActivator";
+ private static final String TYPE_SAFE_DDL_METHOD = "applyDDL";
private static final String TYPE_SAFE_ACTIVATOR_METHOD = "activateBeanValidation";
private static final String MODE_PROPERTY = "javax.persistence.validation.mode";
public static void activateBeanValidation(EventListeners eventListeners, Properties properties) {
- ValidationMode mode = ValidationMode.getMode( properties.get( MODE_PROPERTY ) );
- if (mode == ValidationMode.NONE) return;
+ Set<ValidationMode> modes = ValidationMode.getModes( properties.get( MODE_PROPERTY ) );
+ if ( modes.contains( ValidationMode.NONE ) ) return;
+ //desactivate not-null tracking at the core level when Bean Validation is on unless the user really ask for it
+ if ( properties.getProperty( Environment.CHECK_NULLABILITY ) == null ) {
+ properties.setProperty( Environment.CHECK_NULLABILITY, "false" );
+ }
+
try {
//load Validation
ReflectHelper.classForName( BV_DISCOVERY_CLASS, BeanValidationActivator.class );
}
catch ( ClassNotFoundException e ) {
- if (mode == ValidationMode.CALLBACK) {
+ if ( modes.contains( ValidationMode.CALLBACK ) ) {
throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
}
- else if (mode == ValidationMode.AUTO) {
+ else if (modes.contains( ValidationMode.AUTO ) ) {
//nothing to activate
return;
}
- else {
- throw new AssertionFailure( "Unexpected ValidationMode: " + mode );
- }
}
try {
Class<?> activator = ReflectHelper.classForName( TYPE_SAFE_ACTIVATOR_CLASS, BeanValidationActivator.class );
- Method buildDefaultValidatorFactory =
+ Method activateBeanValidation =
activator.getMethod( TYPE_SAFE_ACTIVATOR_METHOD, EventListeners.class, Properties.class );
- buildDefaultValidatorFactory.invoke( null, eventListeners, properties );
+ activateBeanValidation.invoke( null, eventListeners, properties );
}
catch ( NoSuchMethodException e ) {
throw new HibernateException( "Unable to get the default Bean Validation factory", e);
@@ -65,18 +73,76 @@
}
}
+ public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
+ Set<ValidationMode> modes = ValidationMode.getModes( properties.get( MODE_PROPERTY ) );
+ if ( ! ( modes.contains( ValidationMode.DDL ) || modes.contains( ValidationMode.AUTO ) ) ) return;
+ try {
+ //load Validation
+ ReflectHelper.classForName( BV_DISCOVERY_CLASS, BeanValidationActivator.class );
+ }
+ catch ( ClassNotFoundException e ) {
+ if ( modes.contains( ValidationMode.DDL ) ) {
+ throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
+ }
+ else if (modes.contains( ValidationMode.AUTO ) ) {
+ //nothing to activate
+ return;
+ }
+ }
+ try {
+ Class<?> activator = ReflectHelper.classForName( TYPE_SAFE_ACTIVATOR_CLASS, BeanValidationActivator.class );
+ Method applyDDL =
+ activator.getMethod( TYPE_SAFE_DDL_METHOD, Collection.class, Properties.class );
+ applyDDL.invoke( null, persistentClasses, properties );
+ }
+ catch ( NoSuchMethodException e ) {
+ throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+ }
+ catch ( IllegalAccessException e ) {
+ throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+ }
+ catch ( InvocationTargetException e ) {
+ throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+ }
+ }
+
private static enum ValidationMode {
AUTO,
CALLBACK,
- NONE;
+ NONE,
+ DDL;
- public static ValidationMode getMode(Object modeProperty) {
+ public static Set<ValidationMode> getModes(Object modeProperty) {
+ Set<ValidationMode> modes = new HashSet<ValidationMode>(3);
if (modeProperty == null) {
+ modes.add(ValidationMode.AUTO);
+ }
+ else {
+ final String[] modesInString = modeProperty.toString().split( "," );
+ for ( String modeInString : modesInString ) {
+ modes.add( getMode(modeInString) );
+ }
+ }
+ if ( modes.size() > 1 && ( modes.contains( ValidationMode.AUTO ) || modes.contains( ValidationMode.NONE ) ) ) {
+ StringBuilder message = new StringBuilder( "Incompatible validation modes mixed: " );
+ for (ValidationMode mode : modes) {
+ message.append( mode ).append( ", " );
+ }
+ throw new HibernateException( message.substring( 0, message.length() - 2 ) );
+ }
+ return modes;
+ }
+
+ private static ValidationMode getMode(String modeProperty) {
+ if (modeProperty == null || modeProperty.length() == 0) {
return AUTO;
}
else {
try {
- return valueOf( modeProperty.toString().toUpperCase() );
+ return valueOf( modeProperty.trim().toUpperCase() );
}
catch ( IllegalArgumentException e ) {
throw new HibernateException( "Unknown validation mode in " + MODE_PROPERTY + ": " + modeProperty.toString() );
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java 2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -6,6 +6,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
import javax.validation.ValidatorFactory;
import javax.validation.ConstraintViolation;
import javax.validation.TraversableResolver;
@@ -21,6 +22,8 @@
import org.hibernate.event.PreDeleteEvent;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.util.ReflectHelper;
/**
@@ -29,83 +32,45 @@
//FIXME review exception model
public class BeanValidationEventListener implements
PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener {
- private static final String JPA_GROUP_PREFIX = "javax.persistence.validation.group.";
- private static final Class<?>[] DEFAULT_GROUPS = new Class<?>[] { Default.class };
- private static final Class<?>[] EMPTY_GROUPS = new Class<?>[] { };
+
private ValidatorFactory factory;
- private TraversableResolver tr;
- private Map<Operation, Class<?>[]> groupsPerOperation = new HashMap<Operation, Class<?>[]>(3);
+ private ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister =
+ new ConcurrentHashMap<EntityPersister, Set<String>>();
+ private GroupsPerOperation groupsPerOperation;
public BeanValidationEventListener(ValidatorFactory factory, Properties properties) {
this.factory = factory;
- setGroupsForOperation( Operation.INSERT, properties );
- setGroupsForOperation( Operation.UPDATE, properties );
- setGroupsForOperation( Operation.DELETE, properties );
+ groupsPerOperation = new GroupsPerOperation(properties);
}
- private void setGroupsForOperation(Operation operation, Properties properties) {
- Object property = properties.get( JPA_GROUP_PREFIX + operation.getGroupPropertyName() );
- Class<?>[] groups;
- if ( property == null ) {
- groups = operation == Operation.DELETE ? EMPTY_GROUPS : DEFAULT_GROUPS;
- }
- else {
- if ( property instanceof String ) {
- String stringProperty = (String) property;
- String[] groupNames = stringProperty.split( "," );
- if ( groupNames.length == 1 && groupNames[0].equals( "" ) ) {
- groups = EMPTY_GROUPS;
- }
- else {
- List<Class<?>> groupsList = new ArrayList<Class<?>>(groupNames.length);
- for (String groupName : groupNames) {
- String cleanedGroupName = groupName.trim();
- if ( cleanedGroupName.length() > 0) {
- try {
- groupsList.add( ReflectHelper.classForName( cleanedGroupName ) );
- }
- catch ( ClassNotFoundException e ) {
- throw new HibernateException( "Unable to load class " + cleanedGroupName, e );
- }
- }
- }
- groups = groupsList.toArray( new Class<?>[groupsList.size()] );
- }
- }
- else if ( property instanceof Class<?>[] ) {
- groups = (Class<?>[]) property;
- }
- else {
- //null is bad and excluded by instanceof => exception is raised
- throw new HibernateException( JPA_GROUP_PREFIX + operation.getGroupPropertyName() + " is of unknown type: String or Class<?>[] only");
- }
- }
- groupsPerOperation.put( operation, groups );
- }
-
public boolean onPreInsert(PreInsertEvent event) {
- validate( event.getEntity(), event.getSession().getEntityMode(), Operation.INSERT );
+ validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+ event.getSession().getFactory(), GroupsPerOperation.Operation.INSERT );
return false;
}
public boolean onPreUpdate(PreUpdateEvent event) {
- validate( event.getEntity(), event.getSession().getEntityMode(), Operation.UPDATE );
+ validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+ event.getSession().getFactory(), GroupsPerOperation.Operation.UPDATE );
return false;
}
public boolean onPreDelete(PreDeleteEvent event) {
- validate( event.getEntity(), event.getSession().getEntityMode(), Operation.DELETE );
+ validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+ event.getSession().getFactory(), GroupsPerOperation.Operation.DELETE );
return false;
}
- private <T> void validate(T object, EntityMode mode, Operation operation) {
+ private <T> void validate(T object, EntityMode mode, EntityPersister persister,
+ SessionFactoryImplementor sessionFactory, GroupsPerOperation.Operation operation) {
if ( object == null || mode != EntityMode.POJO ) return;
+ TraversableResolver tr = new HibernateTraversableResolver( persister, associationsPerEntityPersister, sessionFactory );
Validator validator = factory.usingContext()
- //.traversableResolver( tr )
+ .traversableResolver( tr )
.getValidator();
final Class<?>[] groups = groupsPerOperation.get( operation );
if ( groups.length > 0 ) {
@@ -117,7 +82,7 @@
//FIXME add Set<ConstraintViolation<?>>
throw new ConstraintViolationException(
"Invalid object at " + operation.getName() + " time for groups " + toString( groups ),
- (Set<ConstraintViolation>) unsafeViolations);
+ (Set<ConstraintViolation<?>>) unsafeViolations);
}
}
}
@@ -131,26 +96,6 @@
return toString.toString();
}
- private static enum Operation {
- INSERT("persist", "pre-persist"),
- UPDATE("update", "pre-update"),
- DELETE("remove", "pre-remove");
- private String exposedName;
- private String groupPropertyName;
- Operation(String exposedName, String groupProperty) {
- this.exposedName = exposedName;
- this.groupPropertyName = groupProperty;
- }
-
- public String getName() {
- return exposedName;
- }
-
- public String getGroupPropertyName() {
- return groupPropertyName;
- }
- }
-
}
Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,101 @@
+package org.hibernate.cfg.beanvalidation;
+
+import java.util.Properties;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import javax.validation.groups.Default;
+
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.HibernateException;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class GroupsPerOperation {
+
+ private static final String JPA_GROUP_PREFIX = "javax.persistence.validation.group.";
+ private static final String HIBERNATE_GROUP_PREFIX = "org.hibernate.validator.group.";
+ private static final Class<?>[] DEFAULT_GROUPS = new Class<?>[] { Default.class };
+ private static final Class<?>[] EMPTY_GROUPS = new Class<?>[] { };
+
+ private Map<Operation, Class<?>[]> groupsPerOperation = new HashMap<Operation, Class<?>[]>(4);
+
+ public GroupsPerOperation(Properties properties) {
+ setGroupsForOperation( Operation.INSERT, properties );
+ setGroupsForOperation( Operation.UPDATE, properties );
+ setGroupsForOperation( Operation.DELETE, properties );
+ setGroupsForOperation( Operation.DDL, properties );
+ }
+
+ private void setGroupsForOperation(Operation operation, Properties properties) {
+ Object property = properties.get( operation.getGroupPropertyName() );
+
+ Class<?>[] groups;
+ if ( property == null ) {
+ groups = operation == Operation.DELETE ? EMPTY_GROUPS : DEFAULT_GROUPS;
+ }
+ else {
+ if ( property instanceof String ) {
+ String stringProperty = (String) property;
+ String[] groupNames = stringProperty.split( "," );
+ if ( groupNames.length == 1 && groupNames[0].equals( "" ) ) {
+ groups = EMPTY_GROUPS;
+ }
+ else {
+ List<Class<?>> groupsList = new ArrayList<Class<?>>(groupNames.length);
+ for (String groupName : groupNames) {
+ String cleanedGroupName = groupName.trim();
+ if ( cleanedGroupName.length() > 0) {
+ try {
+ groupsList.add( ReflectHelper.classForName( cleanedGroupName ) );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new HibernateException( "Unable to load class " + cleanedGroupName, e );
+ }
+ }
+ }
+ groups = groupsList.toArray( new Class<?>[groupsList.size()] );
+ }
+ }
+ else if ( property instanceof Class<?>[] ) {
+ groups = (Class<?>[]) property;
+ }
+ else {
+ //null is bad and excluded by instanceof => exception is raised
+ throw new HibernateException( JPA_GROUP_PREFIX + operation.getGroupPropertyName() + " is of unknown type: String or Class<?>[] only");
+ }
+ }
+ groupsPerOperation.put( operation, groups );
+ }
+
+ public Class<?>[] get(Operation operation) {
+ return groupsPerOperation.get( operation );
+ }
+
+ public static enum Operation {
+ INSERT("persist", JPA_GROUP_PREFIX + "pre-persist"),
+ UPDATE("update", JPA_GROUP_PREFIX + "pre-update"),
+ DELETE("remove", JPA_GROUP_PREFIX + "pre-remove"),
+ DDL("ddl", HIBERNATE_GROUP_PREFIX + "ddl");
+
+
+ private String exposedName;
+ private String groupPropertyName;
+
+ Operation(String exposedName, String groupProperty) {
+ this.exposedName = exposedName;
+ this.groupPropertyName = groupProperty;
+ }
+
+ public String getName() {
+ return exposedName;
+ }
+
+ public String getGroupPropertyName() {
+ return groupPropertyName;
+ }
+ }
+
+}
Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,92 @@
+package org.hibernate.cfg.beanvalidation;
+
+import java.lang.annotation.ElementType;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.validation.TraversableResolver;
+
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.Hibernate;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class HibernateTraversableResolver implements TraversableResolver {
+ private Set<String> associations;
+
+ public HibernateTraversableResolver(
+ EntityPersister persister,
+ ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister,
+ SessionFactoryImplementor factory) {
+ this.associations = associationsPerEntityPersister.get( persister );
+ if (this.associations == null) {
+ this.associations = new HashSet<String>();
+ addAssociationsToTheSetForAllProperties( persister.getPropertyNames(), persister.getPropertyTypes(), "", factory );
+ associationsPerEntityPersister.put( persister, associations );
+ }
+ }
+
+ private void addAssociationsToTheSetForAllProperties(String[] names, Type[] types, String prefix, SessionFactoryImplementor factory) {
+ final int length = names.length;
+ for( int index = 0 ; index < length; index++ ) {
+ addAssociationsToTheSetForOneProperty( names[index], types[index], prefix, factory );
+ }
+ }
+
+ private void addAssociationsToTheSetForOneProperty(String name, Type type, String prefix, SessionFactoryImplementor factory) {
+
+ if ( type.isCollectionType() ) {
+ CollectionType collType = (CollectionType) type;
+ Type assocType = collType.getElementType( factory );
+ addAssociationsToTheSetForOneProperty(name, assocType, prefix, factory);
+ }
+ //ToOne association
+ else if ( type.isEntityType() || type.isAnyType() ) {
+ associations.add( prefix + name );
+ } else if ( type.isComponentType() ) {
+ AbstractComponentType componentType = (AbstractComponentType) type;
+ addAssociationsToTheSetForAllProperties(
+ componentType.getPropertyNames(),
+ componentType.getSubtypes(),
+ (prefix.equals( "" ) ? name : prefix + name) + ".",
+ factory);
+ }
+ }
+
+ private String getCleanPathWoBraket(String traversableProperty, String pathToTraversableObject) {
+ String path = pathToTraversableObject.equals( "" ) ?
+ traversableProperty :
+ pathToTraversableObject + "." + traversableProperty;
+ String[] paths = path.split( "\\[.*\\]" );
+ path = "";
+ for (String subpath : paths) {
+ path += subpath;
+ }
+ return path;
+ }
+
+ public boolean isReachable(Object traversableObject,
+ String traversableProperty,
+ Class<?> rootBeanType,
+ String pathToTraversableObject,
+ ElementType elementType) {
+ //lazy, don't load
+ return Hibernate.isInitialized( traversableObject )
+ && Hibernate.isPropertyInitialized( traversableObject, traversableProperty );
+ }
+
+ public boolean isCascadable(Object traversableObject,
+ String traversableProperty,
+ Class<?> rootBeanType,
+ String pathToTraversableObject,
+ ElementType elementType) {
+ String path = getCleanPathWoBraket( traversableProperty, pathToTraversableObject );
+ return ! associations.contains(path);
+ }
+}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java 2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -1,22 +1,49 @@
package org.hibernate.cfg.beanvalidation;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
-import java.util.Arrays;
import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Collection;
+import javax.validation.BeanDescriptor;
+import javax.validation.ConstraintDescriptor;
+import javax.validation.PropertyDescriptor;
+import javax.validation.Validation;
import javax.validation.ValidatorFactory;
-import javax.validation.Validation;
+import javax.validation.constraints.Digits;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
import org.hibernate.event.EventListeners;
+import org.hibernate.event.PreDeleteEventListener;
import org.hibernate.event.PreInsertEventListener;
import org.hibernate.event.PreUpdateEventListener;
-import org.hibernate.event.PreDeleteEventListener;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SingleTableSubclass;
+import org.hibernate.util.ReflectHelper;
/**
* @author Emmanuel Bernard
*/
class TypeSafeActivator {
+ private static final Logger logger = LoggerFactory.getLogger( TypeSafeActivator.class );
+
private static final String FACTORY_PROPERTY = "javax.persistence.validation.factory";
public static void activateBeanValidation(EventListeners eventListeners, Properties properties) {
@@ -51,7 +78,209 @@
}
}
- static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
+ public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
+ ValidatorFactory factory = getValidatorFactory( properties );
+ Class<?>[] groupsArray = new GroupsPerOperation( properties ).get( GroupsPerOperation.Operation.DDL );
+ Set<Class<?>> groups = new HashSet<Class<?>>( Arrays.asList( groupsArray ) );
+
+ for ( PersistentClass persistentClass : persistentClasses ) {
+ final String className = persistentClass.getClassName();
+
+ if ( className == null || className.length() == 0) continue;
+ Class<?> clazz;
+ try {
+ clazz = ReflectHelper.classForName( className, TypeSafeActivator.class );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new AssertionFailure( "Entity class not found", e);
+ }
+
+ try {
+ applyDDL( "", persistentClass, clazz, factory, groups, true );
+ }
+ catch (Exception e) {
+ logger.warn( "Unable to apply constraints on DDL for " + className, e );
+ }
+ }
+ }
+
+ private static void applyDDL(String prefix,
+ PersistentClass persistentClass,
+ Class<?> clazz,
+ ValidatorFactory factory,
+ Set<Class<?>> groups,
+ boolean activateNotNull) {
+ final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass( clazz );
+ //no bean level constraints can be applied, go to the properties
+
+ for ( PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties() ) {
+ Property property = findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
+ boolean hasNotNull = false;
+ if ( property != null ) {
+ hasNotNull = applyConstraints( propertyDesc.getConstraintDescriptors(), property, propertyDesc, groups, activateNotNull );
+ if ( property.isComposite() && propertyDesc.isCascaded() ) {
+ Class<?> componentClass = ( ( Component ) property.getValue() ).getComponentClass();
+
+ /*
+ * we can apply not null if the upper component let's us activate not null
+ * and if the property is not null.
+ * Otherwise, all sub columns should be left nullable
+ */
+ final boolean canSetNotNullOnColumns = activateNotNull && hasNotNull;
+ applyDDL( prefix + propertyDesc.getPropertyName() + ".",
+ persistentClass, componentClass, factory, groups,
+ canSetNotNullOnColumns
+ );
+ }
+ //FIXME add collection of components
+ }
+ }
+ }
+
+ private static boolean applyConstraints(Set<ConstraintDescriptor<?>> constraintDescriptors,
+ Property property,
+ PropertyDescriptor propertyDesc,
+ Set<Class<?>> groups, boolean canApplyNotNull) {
+ boolean hasNotNull = false;
+ for (ConstraintDescriptor<?> descriptor : constraintDescriptors) {
+ if ( groups != null && Collections.disjoint( descriptor.getGroups(), groups) ) continue;
+
+ if ( canApplyNotNull ) {
+ hasNotNull = hasNotNull || applyNotNull( property, descriptor );
+ }
+ applyDigits( property, descriptor );
+ applySize( property, descriptor, propertyDesc );
+ applyMin( property, descriptor );
+ applyMax( property, descriptor );
+
+ //pass an empty set as composing constraints inherit the main constraint and thus are matching already
+ hasNotNull = hasNotNull || applyConstraints(
+ descriptor.getComposingConstraints(),
+ property, propertyDesc, null,
+ canApplyNotNull );
+ }
+ return hasNotNull;
+ }
+
+ private static void applyMin(Property property, ConstraintDescriptor<?> descriptor) {
+ if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+ @SuppressWarnings( "unchecked" )
+ ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
+ long min = minConstraint.getAnnotation().value();
+ Column col = (Column) property.getColumnIterator().next();
+ col.setCheckConstraint( col.getName() + ">=" + min );
+ }
+ }
+
+ private static void applyMax(Property property, ConstraintDescriptor<?> descriptor) {
+ if ( Max.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+ @SuppressWarnings( "unchecked" )
+ ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
+ long max = maxConstraint.getAnnotation().value();
+ Column col = (Column) property.getColumnIterator().next();
+ col.setCheckConstraint( col.getName() + "<=" + max );
+ }
+ }
+
+ private static boolean applyNotNull(Property property, ConstraintDescriptor<?> descriptor) {
+ boolean hasNotNull = false;
+ if ( NotNull.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+ if ( ! ( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
+ //single table should not be forced to null
+ if ( !property.isComposite() ) { //composite should not add not-null on all columns
+ @SuppressWarnings( "unchecked" )
+ Iterator<Column> iter = (Iterator<Column>) property.getColumnIterator();
+ while ( iter.hasNext() ) {
+ iter.next().setNullable( false );
+ hasNotNull = true;
+ }
+ }
+ }
+ hasNotNull = true;
+ }
+ return hasNotNull;
+ }
+
+ private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
+ if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+ @SuppressWarnings( "unchecked" )
+ ConstraintDescriptor<Digits> digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
+ int integerDigits = digitsConstraint.getAnnotation().integer();
+ int fractionalDigits = digitsConstraint.getAnnotation().fraction();
+ Column col = (Column) property.getColumnIterator().next();
+ col.setPrecision( integerDigits + fractionalDigits );
+ col.setScale( fractionalDigits );
+ }
+ }
+
+ private static void applySize(Property property, ConstraintDescriptor<?> descriptor, PropertyDescriptor propertyDesc) {
+ if ( Size.class.equals( descriptor.getAnnotation().annotationType() )
+ && String.class.equals( propertyDesc.getType() ) ) {
+ @SuppressWarnings( "unchecked" )
+ ConstraintDescriptor<Size> sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
+ int max = sizeConstraint.getAnnotation().max();
+ Column col = (Column) property.getColumnIterator().next();
+ if ( max < Integer.MAX_VALUE ) col.setLength( max );
+ }
+ }
+
+ /**
+ * Retrieve the property by path in a recursive way, including IndetifierProperty in the loop
+ * If propertyName is null or empty, the IdentifierProperty is returned
+ */
+ private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
+ Property property = null;
+ Property idProperty = associatedClass.getIdentifierProperty();
+ String idName = idProperty != null ? idProperty.getName() : null;
+ try {
+ if ( propertyName == null
+ || propertyName.length() == 0
+ || propertyName.equals( idName ) ) {
+ //default to id
+ property = idProperty;
+ }
+ else {
+ if ( propertyName.indexOf( idName + "." ) == 0 ) {
+ property = idProperty;
+ propertyName = propertyName.substring( idName.length() + 1 );
+ }
+ StringTokenizer st = new StringTokenizer( propertyName, ".", false );
+ while ( st.hasMoreElements() ) {
+ String element = (String) st.nextElement();
+ if ( property == null ) {
+ property = associatedClass.getProperty( element );
+ }
+ else {
+ if ( ! property.isComposite() ) return null;
+ property = ( ( Component ) property.getValue() ).getProperty( element );
+ }
+ }
+ }
+ }
+ catch ( MappingException e) {
+ try {
+ //if we do not find it try to check the identifier mapper
+ if ( associatedClass.getIdentifierMapper() == null ) return null;
+ StringTokenizer st = new StringTokenizer( propertyName, ".", false );
+ while ( st.hasMoreElements() ) {
+ String element = (String) st.nextElement();
+ if ( property == null ) {
+ property = associatedClass.getIdentifierMapper().getProperty( element );
+ }
+ else {
+ if ( ! property.isComposite() ) return null;
+ property = ( (Component) property.getValue() ).getProperty( element );
+ }
+ }
+ }
+ catch (MappingException ee) {
+ return null;
+ }
+ }
+ return property;
+ }
+
+ private static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
ValidatorFactory factory = null;
if ( properties != null ) {
Object unsafeProperty = properties.get( FACTORY_PROPERTY );
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,103 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import javax.persistence.Id;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.AssertTrue;
+
+@Entity
+public class Address {
+ @NotNull
+ public static String blacklistedZipCode;
+
+ private String line1;
+ private String line2;
+ private String zip;
+ private String state;
+ @Size(max = 20)
+ @NotNull
+ private String country;
+ private long id;
+ private boolean internalValid = true;
+ @Min(-2) @Max(value=50)
+ public int floor;
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ @NotNull
+ public String getLine1() {
+ return line1;
+ }
+
+ public void setLine1(String line1) {
+ this.line1 = line1;
+ }
+
+ public String getLine2() {
+ return line2;
+ }
+
+ public void setLine2(String line2) {
+ this.line2 = line2;
+ }
+
+ @Size(max = 3)
+ @NotNull
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ @Size(max = 5)
+ @Pattern(regexp = "[0-9]+")
+ @NotNull
+ public String getZip() {
+ return zip;
+ }
+
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+
+ @AssertTrue
+ @Transient
+ public boolean isValid() {
+ return true;
+ }
+
+ @AssertTrue
+ @Transient
+ private boolean isInternalValid() {
+ return internalValid;
+ }
+
+ public void setInternalValid(boolean internalValid) {
+ this.internalValid = internalValid;
+ }
+
+ @Id
+ @Min(1)
+ @Max(2000)
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,34 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Embeddable;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Max;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Embeddable
+public class Button {
+
+ private String name;
+
+ private Integer size;
+
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Max( 10 )
+ public Integer getSize() {
+ return size;
+ }
+
+ public void setSize(Integer size) {
+ this.size = size;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,33 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Color {
+ private Integer id;
+ private String name;
+
+ @Id @GeneratedValue
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,60 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Property;
+import org.hibernate.test.annotations.TestCase;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class DDLTest extends TestCase {
+
+ public void testBasicDDL() {
+ PersistentClass classMapping = getCfg().getClassMapping( Address.class.getName() );
+ //new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) ).apply( classMapping );
+ Column stateColumn = (Column) classMapping.getProperty( "state" ).getColumnIterator().next();
+ assertEquals( stateColumn.getLength(), 3 );
+ Column zipColumn = (Column) classMapping.getProperty( "zip" ).getColumnIterator().next();
+ assertEquals( zipColumn.getLength(), 5 );
+ assertFalse( zipColumn.isNullable() );
+ }
+
+ public void testApplyOnIdColumn() throws Exception {
+ PersistentClass classMapping = getCfg().getClassMapping( Tv.class.getName() );
+ Column serialColumn = (Column) classMapping.getIdentifierProperty().getColumnIterator().next();
+ assertEquals( "Vaidator annotation not applied on ids", 2, serialColumn.getLength() );
+ }
+
+ public void testApplyOnManyToOne() throws Exception {
+ PersistentClass classMapping = getCfg().getClassMapping( TvOwner.class.getName() );
+ Column serialColumn = (Column) classMapping.getProperty( "tv" ).getColumnIterator().next();
+ assertEquals( "Validator annotations not applied on associations", false, serialColumn.isNullable() );
+ }
+
+ public void testSingleTableAvoidNotNull() throws Exception {
+ PersistentClass classMapping = getCfg().getClassMapping( Rock.class.getName() );
+ Column serialColumn = (Column) classMapping.getProperty( "bit" ).getColumnIterator().next();
+ assertTrue( "Notnull should not be applised on single tables", serialColumn.isNullable() );
+ }
+
+ public void testNotNullOnlyAppliedIfEmbeddedIsNotNullItself() throws Exception {
+ PersistentClass classMapping = getCfg().getClassMapping( Tv.class.getName() );
+ Property property = classMapping.getProperty( "tuner.frequency" );
+ Column serialColumn = (Column) property.getColumnIterator().next();
+ assertEquals( "Validator annotations are applied on tunner as it is @NotNull", false, serialColumn.isNullable() );
+
+ property = classMapping.getProperty( "recorder.time" );
+ serialColumn = (Column) property.getColumnIterator().next();
+ assertEquals( "Validator annotations are applied on tunner as it is @NotNull", true, serialColumn.isNullable() );
+ }
+
+ protected Class<?>[] getMappings() {
+ return new Class<?>[] {
+ Address.class,
+ Tv.class,
+ TvOwner.class,
+ Rock.class
+ };
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,35 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Display {
+ private Integer id;
+
+ private String brand;
+
+ @Id
+ @GeneratedValue
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @NotNull
+ public String getBrand() {
+ return brand;
+ }
+
+ public void setBrand(String brand) {
+ this.brand = brand;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,38 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Embeddable;
+import javax.persistence.OneToOne;
+import javax.persistence.CascadeType;
+import javax.persistence.ManyToOne;
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Embeddable
+public class DisplayConnector {
+
+ private int number;
+ private Display display;
+
+ @Min(1)
+ public int getNumber() {
+ return number;
+ }
+
+ public void setNumber(int number) {
+ this.number = number;
+ }
+
+ @ManyToOne(cascade = CascadeType.PERSIST)
+ @Valid
+ public Display getDisplay() {
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,191 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigDecimal;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ConstraintViolation;
+
+import org.hibernate.test.annotations.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class HibernateTraversableResolverTest extends TestCase {
+ public void testNonLazyAssocFieldWithConstraintsFailureExpected() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ screen.setPowerSupply( null );
+ try {
+ s.persist( screen );
+ s.flush();
+ fail("@NotNull on a non lazy association is not evaluated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ public void testEmbedded() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ PowerSupply ps = new PowerSupply();
+ screen.setPowerSupply( ps );
+ Button button = new Button();
+ button.setName( null );
+ button.setSize( 3 );
+ screen.setStopButton( button );
+ try {
+ s.persist( screen );
+ s.flush();
+ fail("@NotNull on empedded property is not evaluated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ ConstraintViolation<?> cv = e.getConstraintViolations().iterator().next();
+ assertEquals( Screen.class, cv.getRootBeanClass() );
+ assertEquals( "stopButton.name", cv.getPropertyPath() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ public void testToOneAssocNotValidated() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ PowerSupply ps = new PowerSupply();
+ ps.setPosition( "1" );
+ ps.setPower( new BigDecimal( 350 ) );
+ screen.setPowerSupply( ps );
+ try {
+ s.persist( screen );
+ s.flush();
+ fail("Associated objects should not be validated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+ assertEquals( PowerSupply.class, constraintViolation.getRootBeanClass() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ public void testCollectionAssocNotValidated() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ screen.setStopButton( new Button() );
+ screen.getStopButton().setName( "STOOOOOP" );
+ PowerSupply ps = new PowerSupply();
+ screen.setPowerSupply( ps );
+ Color c = new Color();
+ c.setName( "Blue" );
+ s.persist( c );
+ c.setName( null );
+ screen.getDisplayColors().add( c );
+ try {
+ s.persist( screen );
+ s.flush();
+ fail("Associated objects should not be validated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+ assertEquals( Color.class, constraintViolation.getRootBeanClass() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ public void testEmbeddedCollection() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ PowerSupply ps = new PowerSupply();
+ screen.setPowerSupply( ps );
+ DisplayConnector conn = new DisplayConnector();
+ conn.setNumber( 0 );
+ screen.getConnectors().add( conn );
+ try {
+ s.persist( screen );
+ s.flush();
+ fail("Collection of embedded objects should be validated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+ assertEquals( Screen.class, constraintViolation.getRootBeanClass() );
+ // connectors[0] should be connectors expect failure when bug is fixed in HV
+ assertEquals( "connectors[0].number", constraintViolation.getPropertyPath() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ public void testAssocInEmbeddedNotValidated() {
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+
+ Screen screen = new Screen();
+ screen.setStopButton( new Button() );
+ screen.getStopButton().setName( "STOOOOOP" );
+ PowerSupply ps = new PowerSupply();
+ screen.setPowerSupply( ps );
+ DisplayConnector conn = new DisplayConnector();
+ conn.setNumber( 1 );
+ screen.getConnectors().add( conn );
+ final Display display = new Display();
+ display.setBrand( "dell" );
+ conn.setDisplay( display );
+ s.persist( display );
+ s.flush();
+ try {
+ display.setBrand( null );
+ s.persist( screen );
+ s.flush();
+ fail("Collection of embedded objects should be validated");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+ assertEquals( Display.class, constraintViolation.getRootBeanClass() );
+ }
+
+ tx.rollback();
+ s.close();
+ }
+
+ @Override
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( "hibernate.validator.autoregister_listeners", "false" );
+ }
+
+ protected Class<?>[] getMappings() {
+ return new Class<?>[] {
+ Button.class,
+ Color.class,
+ Display.class,
+ DisplayConnector.class,
+ PowerSupply.class,
+ Screen.class
+ };
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,13 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Music {
+ @Id
+ public String name;
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,46 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigDecimal;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Column;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class PowerSupply {
+ private Integer id;
+ private BigDecimal power;
+ private String position;
+
+ @Id @GeneratedValue
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Min(100) @Max(250)
+ public BigDecimal getPower() {
+ return power;
+ }
+
+ public void setPower(BigDecimal power) {
+ this.power = power;
+ }
+
+ @Column(name="fld_pos")
+ public String getPosition() {
+ return position;
+ }
+
+ public void setPosition(String position) {
+ this.position = position;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,13 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Rock extends Music {
+ @NotNull
+ public Integer bit;
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,74 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.util.Set;
+import java.util.HashSet;
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.ManyToMany;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.CascadeType;
+import javax.validation.constraints.NotNull;
+import javax.validation.Valid;
+
+import org.hibernate.annotations.CollectionOfElements;
+import org.hibernate.annotations.Cascade;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Screen {
+ private Integer id;
+ private Button stopButton;
+ private PowerSupply powerSupply;
+ private Set<DisplayConnector> connectors = new HashSet<DisplayConnector>();
+ private Set<Color> displayColors = new HashSet<Color>();
+
+ @Id @GeneratedValue
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ //@NotNull
+ @Valid
+ public Button getStopButton() {
+ return stopButton;
+ }
+
+ public void setStopButton(Button stopButton) {
+ this.stopButton = stopButton;
+ }
+
+ @ManyToOne(cascade = CascadeType.PERSIST) @Valid
+ @NotNull
+ public PowerSupply getPowerSupply() {
+ return powerSupply;
+ }
+
+ public void setPowerSupply(PowerSupply powerSupply) {
+ this.powerSupply = powerSupply;
+ }
+
+ @CollectionOfElements @Valid
+ public Set<DisplayConnector> getConnectors() {
+ return connectors;
+ }
+
+ public void setConnectors(Set<DisplayConnector> connectors) {
+ this.connectors = connectors;
+ }
+
+ @ManyToMany(cascade = CascadeType.PERSIST)
+ public Set<Color> getDisplayColors() {
+ return displayColors;
+ }
+
+ public void setDisplayColors(Set<Color> displayColors) {
+ this.displayColors = displayColors;
+ }
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,50 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigInteger;
+import java.math.BigDecimal;
+import java.util.Date;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Embeddable;
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+import javax.validation.Valid;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Tv {
+
+ @Id
+ @Size(max = 2)
+ public String serial;
+ public int size;
+ @Size(max = 2)
+ public String name;
+ @Future
+ public Date expDate;
+ @Size(min = 0)
+ public String description;
+ @Min(1000)
+ public BigInteger lifetime;
+ @NotNull @Valid
+ public Tuner tuner;
+ @Valid
+ public Recorder recorder;
+
+ @Embeddable
+ public static class Tuner {
+ @NotNull
+ public String frequency;
+ }
+
+ @Embeddable
+ public static class Recorder {
+ @NotNull
+ public BigDecimal time;
+ }
+
+}
Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java 2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,21 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class TvOwner {
+ @Id
+ @GeneratedValue
+ public Integer id;
+
+ @ManyToOne
+ @NotNull
+ public Tv tv;
+}
\ No newline at end of file
15 years, 6 months
Hibernate SVN: r16515 - in core/trunk/core/src/main/java/org/hibernate: engine and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-05-06 11:20:18 -0400 (Wed, 06 May 2009)
New Revision: 16515
Modified:
core/trunk/core/src/main/java/org/hibernate/cfg/Environment.java
core/trunk/core/src/main/java/org/hibernate/cfg/Settings.java
core/trunk/core/src/main/java/org/hibernate/cfg/SettingsFactory.java
core/trunk/core/src/main/java/org/hibernate/engine/Nullability.java
Log:
HHH-3898 hibernate.check_nullability (default true)
Modified: core/trunk/core/src/main/java/org/hibernate/cfg/Environment.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/Environment.java 2009-05-06 01:57:32 UTC (rev 16514)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/Environment.java 2009-05-06 15:20:18 UTC (rev 16515)
@@ -503,6 +503,14 @@
*/
public static final String JACC_CONTEXTID = "hibernate.jacc_context_id";
+ /**
+ * Enable nullability checking.
+ * Raises an exception if a property marked as not-null is null.
+ * Default to true.
+ */
+ public static final String CHECK_NULLABILITY = "hibernate.check_nullability";
+
+
public static final String BYTECODE_PROVIDER = "hibernate.bytecode.provider";
public static final String JPAQL_STRICT_COMPLIANCE= "hibernate.query.jpaql_strict_compliance";
Modified: core/trunk/core/src/main/java/org/hibernate/cfg/Settings.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/Settings.java 2009-05-06 01:57:32 UTC (rev 16514)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/Settings.java 2009-05-06 15:20:18 UTC (rev 16515)
@@ -95,6 +95,7 @@
private boolean strictJPAQLCompliance;
private boolean namedQueryStartupCheckingEnabled;
private EntityTuplizerFactory entityTuplizerFactory;
+ private boolean checkNullability;
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
// private BytecodeProvider bytecodeProvider;
@@ -489,7 +490,15 @@
this.entityTuplizerFactory = entityTuplizerFactory;
}
-// void setComponentTuplizerFactory(ComponentTuplizerFactory componentTuplizerFactory) {
+ public boolean isCheckNullability() {
+ return checkNullability;
+ }
+
+ public void setCheckNullability(boolean checkNullability) {
+ this.checkNullability = checkNullability;
+ }
+
+ // void setComponentTuplizerFactory(ComponentTuplizerFactory componentTuplizerFactory) {
// this.componentTuplizerFactory = componentTuplizerFactory;
// }
Modified: core/trunk/core/src/main/java/org/hibernate/cfg/SettingsFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/SettingsFactory.java 2009-05-06 01:57:32 UTC (rev 16514)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/SettingsFactory.java 2009-05-06 15:20:18 UTC (rev 16515)
@@ -328,6 +328,11 @@
log.info( "Named query checking : " + enabledDisabled( namedQueryChecking ) );
settings.setNamedQueryStartupCheckingEnabled( namedQueryChecking );
+ boolean checkNullability = PropertiesHelper.getBoolean(Environment.CHECK_NULLABILITY, properties, true);
+ log.info( "Check Nullability in Core (should be disabled when Bean Validation is on): " + enabledDisabled(useStatistics) );
+ settings.setCheckNullability(checkNullability);
+
+
// String provider = properties.getProperty( Environment.BYTECODE_PROVIDER );
// log.info( "Bytecode provider name : " + provider );
// BytecodeProvider bytecodeProvider = buildBytecodeProvider( provider );
Modified: core/trunk/core/src/main/java/org/hibernate/engine/Nullability.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/Nullability.java 2009-05-06 01:57:32 UTC (rev 16514)
+++ core/trunk/core/src/main/java/org/hibernate/engine/Nullability.java 2009-05-06 15:20:18 UTC (rev 16515)
@@ -42,9 +42,11 @@
public final class Nullability {
private final SessionImplementor session;
-
+ private final boolean checkNullability;
+
public Nullability(SessionImplementor session) {
this.session = session;
+ this.checkNullability = session.getFactory().getSettings().isCheckNullability();
}
/**
* Check nullability of the class persister properties
@@ -60,60 +62,65 @@
final EntityPersister persister,
final boolean isUpdate)
throws PropertyValueException, HibernateException {
-
/*
- * Algorithm
- * Check for any level one nullability breaks
- * Look at non null components to
- * recursively check next level of nullability breaks
- * Look at Collections contraining component to
- * recursively check next level of nullability breaks
- *
- *
- * In the previous implementation, not-null stuffs where checked
- * filtering by level one only updateable
- * or insertable columns. So setting a sub component as update="false"
- * has no effect on not-null check if the main component had good checkeability
- * In this implementation, we keep this feature.
- * However, I never see any documentation mentioning that, but it's for
- * sure a limitation.
- */
+ * Typically when Bean Validation is on, we don't want to validate null values
+ * at the Hibernate Core level. Hence the checkNullability setting.
+ */
+ if ( checkNullability ) {
+ /*
+ * Algorithm
+ * Check for any level one nullability breaks
+ * Look at non null components to
+ * recursively check next level of nullability breaks
+ * Look at Collections contraining component to
+ * recursively check next level of nullability breaks
+ *
+ *
+ * In the previous implementation, not-null stuffs where checked
+ * filtering by level one only updateable
+ * or insertable columns. So setting a sub component as update="false"
+ * has no effect on not-null check if the main component had good checkeability
+ * In this implementation, we keep this feature.
+ * However, I never see any documentation mentioning that, but it's for
+ * sure a limitation.
+ */
- final boolean[] nullability = persister.getPropertyNullability();
- final boolean[] checkability = isUpdate ?
- persister.getPropertyUpdateability() :
- persister.getPropertyInsertability();
- final Type[] propertyTypes = persister.getPropertyTypes();
+ final boolean[] nullability = persister.getPropertyNullability();
+ final boolean[] checkability = isUpdate ?
+ persister.getPropertyUpdateability() :
+ persister.getPropertyInsertability();
+ final Type[] propertyTypes = persister.getPropertyTypes();
- for ( int i = 0; i < values.length; i++ ) {
-
- if ( checkability[i] && values[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
- final Object value = values[i];
- if ( !nullability[i] && value == null ) {
-
- //check basic level one nullablilty
- throw new PropertyValueException(
- "not-null property references a null or transient value",
- persister.getEntityName(),
- persister.getPropertyNames()[i]
- );
-
- }
- else if ( value != null ) {
-
- //values is not null and is checkable, we'll look deeper
- String breakProperties = checkSubElementsNullability( propertyTypes[i], value );
- if ( breakProperties != null ) {
+ for ( int i = 0; i < values.length; i++ ) {
+
+ if ( checkability[i] && values[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
+ final Object value = values[i];
+ if ( !nullability[i] && value == null ) {
+
+ //check basic level one nullablilty
throw new PropertyValueException(
- "not-null property references a null or transient value",
- persister.getEntityName(),
- buildPropertyPath( persister.getPropertyNames()[i], breakProperties )
- );
+ "not-null property references a null or transient value",
+ persister.getEntityName(),
+ persister.getPropertyNames()[i]
+ );
+
}
-
+ else if ( value != null ) {
+
+ //values is not null and is checkable, we'll look deeper
+ String breakProperties = checkSubElementsNullability( propertyTypes[i], value );
+ if ( breakProperties != null ) {
+ throw new PropertyValueException(
+ "not-null property references a null or transient value",
+ persister.getEntityName(),
+ buildPropertyPath( persister.getPropertyNames()[i], breakProperties )
+ );
+ }
+
+ }
}
+
}
-
}
}
15 years, 6 months
Hibernate SVN: r16514 - in core/branches/Branch_3_2_4_SP1_CP: test/org/hibernate/test and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2009-05-05 21:57:32 -0400 (Tue, 05 May 2009)
New Revision: 16514
Added:
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.hbm.xml
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.java
Modified:
core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/immutable/ImmutableTest.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
HHH-3662 : Merging read-only entities causes AssertionError
Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 01:34:24 UTC (rev 16513)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -1,4 +1,28 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.event.def;
import java.io.Serializable;
@@ -70,8 +94,8 @@
// TODO: cache the entity name somewhere so that it is available to this exception
// entity name will not be available for non-POJO entities
}
- if ( entry.getStatus() != Status.MANAGED ) {
- throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() );
+ if ( entry.getStatus() != Status.MANAGED && entry.getStatus() != Status.READ_ONLY ) {
+ throw new AssertionFailure( "Merged entity does not have status set to MANAGED or READ_ONLY; "+entry+" status="+entry.getStatus() );
}
}
}
Modified: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java 2009-05-06 01:34:24 UTC (rev 16513)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -107,6 +107,7 @@
import org.hibernate.test.proxy.ProxyTest;
import org.hibernate.test.querycache.QueryCacheTest;
import org.hibernate.test.readonly.ReadOnlyTest;
+import org.hibernate.test.readonly.ReadOnlyVersionedNodesTest;
import org.hibernate.test.reattachment.ReattachmentSuite;
import org.hibernate.test.rowid.RowIdTest;
import org.hibernate.test.sorted.SortTest;
@@ -205,6 +206,7 @@
suite.addTest( EmbeddedCompositeIdTest.suite() );
suite.addTest( ImmutableTest.suite() );
suite.addTest( ReadOnlyTest.suite() );
+ suite.addTest( ReadOnlyVersionedNodesTest.suite() );
suite.addTest( IdClassTest.suite() );
suite.addTest( ArrayTest.suite() );
suite.addTest( TernaryTest.suite() );
Modified: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 01:34:24 UTC (rev 16513)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -1,10 +1,37 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.immutable;
+import java.util.Iterator;
+
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
+import org.hibernate.HibernateException;
import org.hibernate.criterion.Projections;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
@@ -53,8 +80,84 @@
c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
s.delete(c);
assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
@@ -62,5 +165,165 @@
s.close();
}
+ public void testImmutableCollectionWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ try {
+ s.update( c );
+ fail( "should have failed because reassociated object has a dirty collection");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ }
+ finally {
+ t.rollback();
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableCollectionWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ s.merge( c );
+ try {
+ t.commit();
+ fail( "should have failed because an immutable collection was changed");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ t.rollback();
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
}
-
Modified: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 01:34:24 UTC (rev 16513)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -1,4 +1,28 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
import java.math.BigDecimal;
@@ -117,5 +141,38 @@
}
+ public void testMergeWithReadOnlyEntity() {
+
+ Session s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.save(dp);
+ t.commit();
+ s.close();
+
+ dp.setDescription( "description" );
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ t = s.beginTransaction();
+ DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ s.setReadOnly( dpManaged, true );
+ DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+ assertSame( dpManaged, dpMerged );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ assertNull( dpManaged.getDescription() );
+ s.delete( dpManaged );
+ t.commit();
+ s.close();
+
+ }
}
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -0,0 +1,481 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Gail Badner
+ */
+public class ReadOnlyVersionedNodesTest extends FunctionalTestCase {
+
+ public ReadOnlyVersionedNodesTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "readonly/VersionedNode.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ReadOnlyVersionedNodesTest.class );
+ }
+
+ public void testSetReadOnlyTrueAndFalse() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode node = new VersionedNode( "node", "node" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ s.setReadOnly( node, true );
+ node.setName( "node-name" );
+ s.getTransaction().commit();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ // the changed name is still in node
+ assertEquals( "node-name", node.getName() );
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ // the changed name is still in the session
+ assertEquals( "node-name", node.getName() );
+ s.refresh( node );
+ // after refresh, the name reverts to the original value
+ assertEquals( "node", node.getName() );
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.getTransaction().commit();
+
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, true );
+ node.setName( "diff-node-name" );
+ s.flush();
+ assertEquals( "diff-node-name", node.getName() );
+ s.refresh( node );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, false );
+ node.setName( "diff-node-name" );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "diff-node-name", node.getName() );
+ assertEquals( 1, node.getVersion() );
+ s.delete( node );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testAddNewChildToReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ parentManaged.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parentManaged.addChild( child );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertEquals( "parent", parent.getName() );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertNull( child );
+ s.delete( parent );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.merge( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ VersionedNode parentMerged = ( VersionedNode ) s.merge( parent );
+ assertSame( parentManaged, parentMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+
+ public void testAddNewParentToReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ childManaged.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( childManaged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( "child", child.getName() );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertNull( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.merge( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ VersionedNode childMerged = ( VersionedNode ) s.merge( child );
+ assertSame( childManaged, childMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void cleanupTest() throws Exception {
+ cleanup();
+ super.cleanupTest();
+ }
+
+ private void cleanup() {
+ Session s = sfi().openSession();
+ s.beginTransaction();
+
+ s.createQuery( "delete from VersionedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from VersionedNode" ).executeUpdate();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ protected void assertInsertCount(int expected) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( "unexpected insert count", expected, inserts );
+ }
+
+ protected void assertUpdateCount(int expected) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( "unexpected update counts", expected, updates );
+ }
+
+ protected void assertDeleteCount(int expected) {
+ int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+ assertEquals( "unexpected delete counts", expected, deletes );
+ }
+}
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.hbm.xml
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.hbm.xml (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.hbm.xml 2009-05-06 01:57:32 UTC (rev 16514)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.readonly">
+
+ <class name="VersionedNode" table="V_NODE">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="parent" class="VersionedNode" cascade="persist,merge,save-update,evict,delete"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict,delete">
+ <key column="parent"/>
+ <one-to-many class="VersionedNode"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/readonly/VersionedNode.java 2009-05-06 01:57:32 UTC (rev 16514)
@@ -0,0 +1,96 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * VersionedNode
+ *
+ * @author Gail Badner
+ */
+public class VersionedNode {
+ private String id;
+ private String name;
+ private long version;
+
+ private VersionedNode parent;
+ private Set children = new HashSet();
+
+ public VersionedNode() {
+ }
+
+ public VersionedNode(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public VersionedNode getParent() {
+ return parent;
+ }
+
+ public void setParent(VersionedNode parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public void addChild(VersionedNode child) {
+ child.setParent( this );
+ children.add( child );
+ }
+}
15 years, 7 months
Hibernate SVN: r16513 - in core/branches/Branch_3_2: test/org/hibernate/test and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2009-05-05 21:34:24 -0400 (Tue, 05 May 2009)
New Revision: 16513
Added:
core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.hbm.xml
core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.java
Modified:
core/branches/Branch_3_2/src/org/hibernate/event/def/DefaultMergeEventListener.java
core/branches/Branch_3_2/test/org/hibernate/test/AllTests.java
core/branches/Branch_3_2/test/org/hibernate/test/immutable/ImmutableTest.java
core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
HHH-3662 : Merging read-only entities causes AssertionError
Modified: core/branches/Branch_3_2/src/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 01:18:43 UTC (rev 16512)
+++ core/branches/Branch_3_2/src/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -1,4 +1,28 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.event.def;
import java.io.Serializable;
@@ -70,8 +94,8 @@
// TODO: cache the entity name somewhere so that it is available to this exception
// entity name will not be available for non-POJO entities
}
- if ( entry.getStatus() != Status.MANAGED ) {
- throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() );
+ if ( entry.getStatus() != Status.MANAGED && entry.getStatus() != Status.READ_ONLY ) {
+ throw new AssertionFailure( "Merged entity does not have status set to MANAGED or READ_ONLY; "+entry+" status="+entry.getStatus() );
}
}
}
Modified: core/branches/Branch_3_2/test/org/hibernate/test/AllTests.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/AllTests.java 2009-05-06 01:18:43 UTC (rev 16512)
+++ core/branches/Branch_3_2/test/org/hibernate/test/AllTests.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -188,6 +188,7 @@
import org.hibernate.test.proxy.ProxyTest;
import org.hibernate.test.querycache.QueryCacheTest;
import org.hibernate.test.readonly.ReadOnlyTest;
+import org.hibernate.test.readonly.ReadOnlyVersionedNodesTest;
import org.hibernate.test.reattachment.CollectionReattachmentTest;
import org.hibernate.test.reattachment.ProxyReattachmentTest;
import org.hibernate.test.rowid.RowIdTest;
@@ -304,6 +305,7 @@
suite.addTest( EmbeddedCompositeIdTest.suite() );
suite.addTest( ImmutableTest.suite() );
suite.addTest( ReadOnlyTest.suite() );
+ suite.addTest( ReadOnlyVersionedNodesTest.suite() );
suite.addTest( IdClassTest.suite() );
suite.addTest( ArrayTest.suite() );
suite.addTest( TernaryTest.suite() );
@@ -633,4 +635,4 @@
}
}
}
-}
\ No newline at end of file
+}
Modified: core/branches/Branch_3_2/test/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 01:18:43 UTC (rev 16512)
+++ core/branches/Branch_3_2/test/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -1,10 +1,37 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.immutable;
+import java.util.Iterator;
+
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
+import org.hibernate.HibernateException;
import org.hibernate.criterion.Projections;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
@@ -53,8 +80,84 @@
c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
s.delete(c);
assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
@@ -62,5 +165,165 @@
s.close();
}
+ public void testImmutableCollectionWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ try {
+ s.update( c );
+ fail( "should have failed because reassociated object has a dirty collection");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ }
+ finally {
+ t.rollback();
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableCollectionWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ s.merge( c );
+ try {
+ t.commit();
+ fail( "should have failed because an immutable collection was changed");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ t.rollback();
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
}
-
Modified: core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 01:18:43 UTC (rev 16512)
+++ core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -1,4 +1,28 @@
//$Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
import java.math.BigDecimal;
@@ -117,5 +141,38 @@
}
+ public void testMergeWithReadOnlyEntity() {
+
+ Session s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.save(dp);
+ t.commit();
+ s.close();
+
+ dp.setDescription( "description" );
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ t = s.beginTransaction();
+ DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ s.setReadOnly( dpManaged, true );
+ DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+ assertSame( dpManaged, dpMerged );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ assertNull( dpManaged.getDescription() );
+ s.delete( dpManaged );
+ t.commit();
+ s.close();
+
+ }
}
Added: core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -0,0 +1,481 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Gail Badner
+ */
+public class ReadOnlyVersionedNodesTest extends FunctionalTestCase {
+
+ public ReadOnlyVersionedNodesTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "readonly/VersionedNode.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ReadOnlyVersionedNodesTest.class );
+ }
+
+ public void testSetReadOnlyTrueAndFalse() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode node = new VersionedNode( "node", "node" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ s.setReadOnly( node, true );
+ node.setName( "node-name" );
+ s.getTransaction().commit();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ // the changed name is still in node
+ assertEquals( "node-name", node.getName() );
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ // the changed name is still in the session
+ assertEquals( "node-name", node.getName() );
+ s.refresh( node );
+ // after refresh, the name reverts to the original value
+ assertEquals( "node", node.getName() );
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.getTransaction().commit();
+
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, true );
+ node.setName( "diff-node-name" );
+ s.flush();
+ assertEquals( "diff-node-name", node.getName() );
+ s.refresh( node );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, false );
+ node.setName( "diff-node-name" );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "diff-node-name", node.getName() );
+ assertEquals( 1, node.getVersion() );
+ s.delete( node );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testAddNewChildToReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ parentManaged.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parentManaged.addChild( child );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertEquals( "parent", parent.getName() );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertNull( child );
+ s.delete( parent );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.merge( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ VersionedNode parentMerged = ( VersionedNode ) s.merge( parent );
+ assertSame( parentManaged, parentMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+
+ public void testAddNewParentToReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ childManaged.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( childManaged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( "child", child.getName() );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertNull( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.merge( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ VersionedNode childMerged = ( VersionedNode ) s.merge( child );
+ assertSame( childManaged, childMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void cleanupTest() throws Exception {
+ cleanup();
+ super.cleanupTest();
+ }
+
+ private void cleanup() {
+ Session s = sfi().openSession();
+ s.beginTransaction();
+
+ s.createQuery( "delete from VersionedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from VersionedNode" ).executeUpdate();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ protected void assertInsertCount(int expected) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( "unexpected insert count", expected, inserts );
+ }
+
+ protected void assertUpdateCount(int expected) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( "unexpected update counts", expected, updates );
+ }
+
+ protected void assertDeleteCount(int expected) {
+ int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+ assertEquals( "unexpected delete counts", expected, deletes );
+ }
+}
Added: core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.hbm.xml
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.hbm.xml (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.hbm.xml 2009-05-06 01:34:24 UTC (rev 16513)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.readonly">
+
+ <class name="VersionedNode" table="V_NODE">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="parent" class="VersionedNode" cascade="persist,merge,save-update,evict,delete"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict,delete">
+ <key column="parent"/>
+ <one-to-many class="VersionedNode"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Added: core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.java (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/readonly/VersionedNode.java 2009-05-06 01:34:24 UTC (rev 16513)
@@ -0,0 +1,96 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * VersionedNode
+ *
+ * @author Gail Badner
+ */
+public class VersionedNode {
+ private String id;
+ private String name;
+ private long version;
+
+ private VersionedNode parent;
+ private Set children = new HashSet();
+
+ public VersionedNode() {
+ }
+
+ public VersionedNode(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public VersionedNode getParent() {
+ return parent;
+ }
+
+ public void setParent(VersionedNode parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public void addChild(VersionedNode child) {
+ child.setParent( this );
+ children.add( child );
+ }
+}
15 years, 7 months
Hibernate SVN: r16512 - in core/branches/Branch_3_3: testsuite/src/test/java/org/hibernate/test/immutable and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2009-05-05 21:18:43 -0400 (Tue, 05 May 2009)
New Revision: 16512
Added:
core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml
core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
HHH-3662 : Merging read-only entities causes AssertionError
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 00:44:48 UTC (rev 16511)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 01:18:43 UTC (rev 16512)
@@ -93,8 +93,8 @@
// TODO: cache the entity name somewhere so that it is available to this exception
// entity name will not be available for non-POJO entities
}
- if ( entry.getStatus() != Status.MANAGED ) {
- throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() );
+ if ( entry.getStatus() != Status.MANAGED && entry.getStatus() != Status.READ_ONLY ) {
+ throw new AssertionFailure( "Merged entity does not have status set to MANAGED or READ_ONLY; "+entry+" status="+entry.getStatus() );
}
}
}
Modified: core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 00:44:48 UTC (rev 16511)
+++ core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 01:18:43 UTC (rev 16512)
@@ -1,8 +1,35 @@
//$Id: ImmutableTest.java 10977 2006-12-12 23:28:04Z steve.ebersole(a)jboss.com $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.immutable;
+import java.util.Iterator;
+
import junit.framework.Test;
+import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
@@ -53,8 +80,84 @@
c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
s.delete(c);
assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
@@ -62,5 +165,166 @@
s.close();
}
+ public void testImmutableCollectionWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ try {
+ s.update( c );
+ fail( "should have failed because reassociated object has a dirty collection");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ }
+ finally {
+ t.rollback();
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableCollectionWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ s.merge( c );
+ try {
+ t.commit();
+ fail( "should have failed because an immutable collection was changed");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ t.rollback();
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
}
Modified: core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 00:44:48 UTC (rev 16511)
+++ core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 01:18:43 UTC (rev 16512)
@@ -1,4 +1,28 @@
//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole(a)jboss.com $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
import java.math.BigDecimal;
@@ -148,5 +172,39 @@
s.getTransaction().commit();
s.close();
}
+
+ public void testMergeWithReadOnlyEntity() {
+
+ Session s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.save(dp);
+ t.commit();
+ s.close();
+
+ dp.setDescription( "description" );
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ t = s.beginTransaction();
+ DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ s.setReadOnly( dpManaged, true );
+ DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+ assertSame( dpManaged, dpMerged );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ assertNull( dpManaged.getDescription() );
+ s.delete( dpManaged );
+ t.commit();
+ s.close();
+
+ }
}
Added: core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
===================================================================
--- core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java (rev 0)
+++ core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java 2009-05-06 01:18:43 UTC (rev 16512)
@@ -0,0 +1,481 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Gail Badner
+ */
+public class ReadOnlyVersionedNodesTest extends FunctionalTestCase {
+
+ public ReadOnlyVersionedNodesTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "readonly/VersionedNode.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ReadOnlyVersionedNodesTest.class );
+ }
+
+ public void testSetReadOnlyTrueAndFalse() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode node = new VersionedNode( "node", "node" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ s.setReadOnly( node, true );
+ node.setName( "node-name" );
+ s.getTransaction().commit();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ // the changed name is still in node
+ assertEquals( "node-name", node.getName() );
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ // the changed name is still in the session
+ assertEquals( "node-name", node.getName() );
+ s.refresh( node );
+ // after refresh, the name reverts to the original value
+ assertEquals( "node", node.getName() );
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.getTransaction().commit();
+
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, true );
+ node.setName( "diff-node-name" );
+ s.flush();
+ assertEquals( "diff-node-name", node.getName() );
+ s.refresh( node );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, false );
+ node.setName( "diff-node-name" );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "diff-node-name", node.getName() );
+ assertEquals( 1, node.getVersion() );
+ s.delete( node );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testAddNewChildToReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ parentManaged.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parentManaged.addChild( child );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertEquals( "parent", parent.getName() );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertNull( child );
+ s.delete( parent );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.merge( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ VersionedNode parentMerged = ( VersionedNode ) s.merge( parent );
+ assertSame( parentManaged, parentMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+
+ public void testAddNewParentToReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ childManaged.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( childManaged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( "child", child.getName() );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertNull( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.merge( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ VersionedNode childMerged = ( VersionedNode ) s.merge( child );
+ assertSame( childManaged, childMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void cleanupTest() throws Exception {
+ cleanup();
+ super.cleanupTest();
+ }
+
+ private void cleanup() {
+ Session s = sfi().openSession();
+ s.beginTransaction();
+
+ s.createQuery( "delete from VersionedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from VersionedNode" ).executeUpdate();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ protected void assertInsertCount(int expected) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( "unexpected insert count", expected, inserts );
+ }
+
+ protected void assertUpdateCount(int expected) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( "unexpected update counts", expected, updates );
+ }
+
+ protected void assertDeleteCount(int expected) {
+ int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+ assertEquals( "unexpected delete counts", expected, deletes );
+ }
+}
Added: core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml
===================================================================
--- core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml (rev 0)
+++ core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml 2009-05-06 01:18:43 UTC (rev 16512)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.readonly">
+
+ <class name="VersionedNode" table="V_NODE">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="parent" class="VersionedNode" cascade="persist,merge,save-update,evict,delete"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict,delete">
+ <key column="parent"/>
+ <one-to-many class="VersionedNode"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Added: core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java
===================================================================
--- core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java (rev 0)
+++ core/branches/Branch_3_3/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java 2009-05-06 01:18:43 UTC (rev 16512)
@@ -0,0 +1,96 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * VersionedNode
+ *
+ * @author Gail Badner
+ */
+public class VersionedNode {
+ private String id;
+ private String name;
+ private long version;
+
+ private VersionedNode parent;
+ private Set children = new HashSet();
+
+ public VersionedNode() {
+ }
+
+ public VersionedNode(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public VersionedNode getParent() {
+ return parent;
+ }
+
+ public void setParent(VersionedNode parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public void addChild(VersionedNode child) {
+ child.setParent( this );
+ children.add( child );
+ }
+}
15 years, 7 months
Hibernate SVN: r16511 - in core/trunk: testsuite/src/test/java/org/hibernate/test/immutable and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2009-05-05 20:44:48 -0400 (Tue, 05 May 2009)
New Revision: 16511
Added:
core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml
core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java
Modified:
core/trunk/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
HHH-3662 : Merging read-only entities causes AssertionError
Modified: core/trunk/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-05 12:40:55 UTC (rev 16510)
+++ core/trunk/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java 2009-05-06 00:44:48 UTC (rev 16511)
@@ -93,8 +93,8 @@
// TODO: cache the entity name somewhere so that it is available to this exception
// entity name will not be available for non-POJO entities
}
- if ( entry.getStatus() != Status.MANAGED ) {
- throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() );
+ if ( entry.getStatus() != Status.MANAGED && entry.getStatus() != Status.READ_ONLY ) {
+ throw new AssertionFailure( "Merged entity does not have status set to MANAGED or READ_ONLY; "+entry+" status="+entry.getStatus() );
}
}
}
Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java 2009-05-05 12:40:55 UTC (rev 16510)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java 2009-05-06 00:44:48 UTC (rev 16511)
@@ -1,8 +1,35 @@
//$Id: ImmutableTest.java 10977 2006-12-12 23:28:04Z steve.ebersole(a)jboss.com $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.immutable;
+import java.util.Iterator;
+
import junit.framework.Test;
+import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
@@ -53,8 +80,84 @@
c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.update( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
s.delete(c);
assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
@@ -62,5 +165,166 @@
s.close();
}
+ public void testImmutableCollectionWithUpdate() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ try {
+ s.update( c );
+ fail( "should have failed because reassociated object has a dirty collection");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ }
+ finally {
+ t.rollback();
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableParentEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setCustomerName("foo bar");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableChildEntityWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ s.merge( c );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testImmutableCollectionWithMerge() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.getVariations().add( new ContractVariation(3, c) );
+ s.merge( c );
+ try {
+ t.commit();
+ fail( "should have failed because an immutable collection was changed");
+ }
+ catch ( HibernateException ex ) {
+ // expected
+ t.rollback();
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ Iterator it = c.getVariations().iterator();
+ cv1 = (ContractVariation) it.next();
+ assertEquals( cv1.getText(), "expensive" );
+ cv2 = (ContractVariation) it.next();
+ assertEquals( cv2.getText(), "more expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
}
Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-05 12:40:55 UTC (rev 16510)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java 2009-05-06 00:44:48 UTC (rev 16511)
@@ -1,4 +1,28 @@
//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole(a)jboss.com $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
import java.math.BigDecimal;
@@ -148,5 +172,39 @@
s.getTransaction().commit();
s.close();
}
+
+ public void testMergeWithReadOnlyEntity() {
+
+ Session s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.save(dp);
+ t.commit();
+ s.close();
+
+ dp.setDescription( "description" );
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ t = s.beginTransaction();
+ DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ s.setReadOnly( dpManaged, true );
+ DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+ assertSame( dpManaged, dpMerged );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+ assertNull( dpManaged.getDescription() );
+ s.delete( dpManaged );
+ t.commit();
+ s.close();
+
+ }
}
Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java 2009-05-06 00:44:48 UTC (rev 16511)
@@ -0,0 +1,481 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Gail Badner
+ */
+public class ReadOnlyVersionedNodesTest extends FunctionalTestCase {
+
+ public ReadOnlyVersionedNodesTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "readonly/VersionedNode.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ReadOnlyVersionedNodesTest.class );
+ }
+
+ public void testSetReadOnlyTrueAndFalse() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode node = new VersionedNode( "node", "node" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ s.setReadOnly( node, true );
+ node.setName( "node-name" );
+ s.getTransaction().commit();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ // the changed name is still in node
+ assertEquals( "node-name", node.getName() );
+
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ // the changed name is still in the session
+ assertEquals( "node-name", node.getName() );
+ s.refresh( node );
+ // after refresh, the name reverts to the original value
+ assertEquals( "node", node.getName() );
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.getTransaction().commit();
+
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, true );
+ node.setName( "diff-node-name" );
+ s.flush();
+ assertEquals( "diff-node-name", node.getName() );
+ s.refresh( node );
+ assertEquals( "node", node.getName() );
+ s.setReadOnly( node, false );
+ node.setName( "diff-node-name" );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() );
+ assertEquals( "diff-node-name", node.getName() );
+ assertEquals( 1, node.getVersion() );
+ s.delete( node );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testAddNewChildToReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ parentManaged.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parentManaged.addChild( child );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertEquals( "parent", parent.getName() );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertNull( child );
+ s.delete( parent );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.merge( parent );
+ s.setReadOnly( parent, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode parent = new VersionedNode( "parent", "parent" );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ parent.setName( "new parent name" );
+ VersionedNode child = new VersionedNode( "child", "child");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ s.setReadOnly( parentManaged, true );
+ VersionedNode parentMerged = ( VersionedNode ) s.merge( parent );
+ assertSame( parentManaged, parentMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( parent.getName(), "parent" );
+ assertEquals( 1, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ assertSame( parent, child.getParent() );
+ assertSame( child, parent.getChildren().iterator().next() );
+ assertEquals( 0, child.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+
+ public void testAddNewParentToReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ childManaged.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( childManaged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( "child", child.getName() );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ assertNull( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 0, parent.getVersion() );
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ child = ( VersionedNode ) s.merge( child );
+ s.setReadOnly( child, true );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedNode child = new VersionedNode( "child", "child" );
+ s.persist( child );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ child.setName( "new child name" );
+ VersionedNode parent = new VersionedNode( "parent", "parent");
+ parent.addChild( child );
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ s.setReadOnly( childManaged, true );
+ VersionedNode childMerged = ( VersionedNode ) s.merge( child );
+ assertSame( childManaged, childMerged );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() );
+ child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() );
+ assertEquals( child.getName(), "child" );
+ assertNull( child.getParent() );
+ assertEquals( 0, child.getVersion() );
+ assertNotNull( parent );
+ assertEquals( 0, parent.getChildren().size() );
+ assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated?
+ s.delete( parent );
+ s.delete( child );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void cleanupTest() throws Exception {
+ cleanup();
+ super.cleanupTest();
+ }
+
+ private void cleanup() {
+ Session s = sfi().openSession();
+ s.beginTransaction();
+
+ s.createQuery( "delete from VersionedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from VersionedNode" ).executeUpdate();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ protected void assertInsertCount(int expected) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( "unexpected insert count", expected, inserts );
+ }
+
+ protected void assertUpdateCount(int expected) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( "unexpected update counts", expected, updates );
+ }
+
+ protected void assertDeleteCount(int expected) {
+ int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+ assertEquals( "unexpected delete counts", expected, deletes );
+ }
+}
Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml 2009-05-06 00:44:48 UTC (rev 16511)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.readonly">
+
+ <class name="VersionedNode" table="V_NODE">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="parent" class="VersionedNode" cascade="persist,merge,save-update,evict,delete"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict,delete">
+ <key column="parent"/>
+ <one-to-many class="VersionedNode"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java 2009-05-06 00:44:48 UTC (rev 16511)
@@ -0,0 +1,96 @@
+//$Id: $
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
+ *
+ * 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.readonly;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * VersionedNode
+ *
+ * @author Gail Badner
+ */
+public class VersionedNode {
+ private String id;
+ private String name;
+ private long version;
+
+ private VersionedNode parent;
+ private Set children = new HashSet();
+
+ public VersionedNode() {
+ }
+
+ public VersionedNode(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public VersionedNode getParent() {
+ return parent;
+ }
+
+ public void setParent(VersionedNode parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public void addChild(VersionedNode child) {
+ child.setParent( this );
+ children.add( child );
+ }
+}
15 years, 7 months