mirror of
https://github.com/apache/openjpa.git
synced 2025-02-20 17:05:15 +00:00
OPENJPA-1984 - Skip non-root object validation when performing remove(root object). Let's validation provider to perform root object validation so that the contraint violation can report the correct root bean.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1096791 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d1d356c151
commit
9ea0354f1b
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.integration.validation;
|
||||
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* Image entity which makes use of several BV constraints.
|
||||
*/
|
||||
@Entity
|
||||
public class Image {
|
||||
|
||||
private int id;
|
||||
private String fileName;
|
||||
private Location location;
|
||||
|
||||
@Id
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@NotNull(message="Image file name must not be null.")
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
@Valid
|
||||
@Embedded
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[Image:id=" + id + ",filename=" + fileName + "," + location + ']';
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.integration.validation;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* Location embeddable with several BV constraints applied.
|
||||
*/
|
||||
@Embeddable
|
||||
public class Location {
|
||||
|
||||
@NotNull(message="City must be specified.")
|
||||
private String city;
|
||||
|
||||
private String street;
|
||||
|
||||
private String state;
|
||||
|
||||
@NotNull(message="Country must be specified.")
|
||||
@Size(message="Country must be 50 characters or less.", max=50)
|
||||
@Column(length=50)
|
||||
private String country;
|
||||
|
||||
@Size(message="Zip code must be 10 characters or less.", max=10)
|
||||
@Pattern(message="Zip code must be 5 digits or use the 5+4 format.",
|
||||
regexp="^\\d{5}(([\\-]|[\\+])\\d{4})?$")
|
||||
@Column(length=10)
|
||||
private String zipCode;
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setZipCode(String zipCode) {
|
||||
this.zipCode = zipCode;
|
||||
}
|
||||
|
||||
public String getZipCode() {
|
||||
return zipCode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[Location:city=" + city + ",street=" + street + ",state=" + state + ",country=" + country + ",zipCode="
|
||||
+ zipCode + ']';
|
||||
}
|
||||
}
|
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.integration.validation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase;
|
||||
|
||||
public class TestConstraintViolation extends AbstractPersistenceTestCase {
|
||||
|
||||
EntityManagerFactory emf2 = null;
|
||||
Log log = null;
|
||||
|
||||
public void setUp() {
|
||||
Map<String, String> props = new HashMap<String, String>();
|
||||
props.put("javax.persistence.validation.group.pre-remove", "javax.validation.groups.Default");
|
||||
|
||||
// This test case uses a different persistence xml file because validation require 2.0 xsd.
|
||||
emf2 = OpenJPAPersistence.createEntityManagerFactory("ConstraintViolationTestPU",
|
||||
"org/apache/openjpa/integration/validation/persistence.xml", props);
|
||||
log = ((OpenJPAEntityManagerFactorySPI)emf2).getConfiguration().getLog("Tests");
|
||||
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
Image img = em.find(Image.class, 1);
|
||||
if (img != null) {
|
||||
em.getTransaction().begin();
|
||||
em.remove(img);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
em.close();
|
||||
}
|
||||
|
||||
public void testPersistNormalValidation() {
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
|
||||
// Persist with successful validations
|
||||
Location loc = new Location();
|
||||
loc.setCity("Rochester");
|
||||
loc.setStreet(null);
|
||||
loc.setState("MN");
|
||||
loc.setZipCode("55901");
|
||||
loc.setCountry("USA");
|
||||
|
||||
Image img = new Image();
|
||||
img.setId(1);
|
||||
img.setFileName("Winter_01.gif");
|
||||
img.setLocation(loc);
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("------------------------------------------------");
|
||||
log.trace("** Persist with successful validations");
|
||||
em.persist(img);
|
||||
} catch (ConstraintViolationException cve) {
|
||||
// Transaction was marked for rollback, roll it back and start a new TX
|
||||
Set<ConstraintViolation<?>> cvs = cve.getConstraintViolations();
|
||||
for (ConstraintViolation<?> cv : cvs) {
|
||||
log.trace("Message: " + cv.getMessage());
|
||||
log.trace("RootBean: " + cv.getRootBean());
|
||||
log.trace("LeafBean: " + cv.getLeafBean());
|
||||
log.trace("PropertyPath: " + cv.getPropertyPath());
|
||||
log.trace("Invalid value: " + cv.getInvalidValue());
|
||||
}
|
||||
fail();
|
||||
} finally {
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
emf2.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testPersistImageNullValidation() {
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
|
||||
// Persist with null filename in Image
|
||||
Location loc = new Location();
|
||||
loc.setCity("Rochester");
|
||||
loc.setStreet("3605 Hwy 52 N");
|
||||
loc.setState("MN");
|
||||
loc.setZipCode("55901");
|
||||
loc.setCountry("USA");
|
||||
|
||||
Image img = new Image();
|
||||
img.setId(1);
|
||||
img.setFileName(null);
|
||||
img.setLocation(loc);
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("------------------------------------------------");
|
||||
log.trace("** Persist with null filename in Image");
|
||||
em.persist(img);
|
||||
fail();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
// Transaction was marked for rollback, roll it back and start a new TX
|
||||
Set<ConstraintViolation<?>> cvs = cve.getConstraintViolations();
|
||||
assertEquals(1, cvs.size());
|
||||
for (ConstraintViolation<?> cv : cvs) {
|
||||
log.trace("Message: " + cv.getMessage());
|
||||
log.trace("RootBean: " + cv.getRootBean());
|
||||
log.trace("LeafBean: " + cv.getLeafBean());
|
||||
log.trace("PropertyPath: " + cv.getPropertyPath());
|
||||
log.trace("Invalid value: " + cv.getInvalidValue());
|
||||
|
||||
assertEquals("Image file name must not be null.", cv.getMessage());
|
||||
assertEquals("Image", cv.getRootBeanClass().getSimpleName());
|
||||
assertEquals("Image", cv.getLeafBean().getClass().getSimpleName());
|
||||
assertTrue(cv.getLeafBean().getClass() == cv.getRootBeanClass());
|
||||
assertEquals("fileName", cv.getPropertyPath().toString());
|
||||
assertNull(cv.getInvalidValue());
|
||||
}
|
||||
} finally {
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
emf2.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testPersistEmbedCityNullValidation() {
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
|
||||
// Persist with null city name in location
|
||||
Location loc = new Location();
|
||||
loc.setCity(null);
|
||||
loc.setStreet("3605 Hwy 52 N");
|
||||
loc.setState("MN");
|
||||
loc.setZipCode("55901");
|
||||
loc.setCountry("USA");
|
||||
|
||||
Image img = new Image();
|
||||
img.setId(1);
|
||||
img.setFileName("Winter_01.gif");
|
||||
img.setLocation(loc);
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("------------------------------------------------");
|
||||
log.trace("** Persist with null city name in location" );
|
||||
em.persist(img);
|
||||
fail();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
// Transaction was marked for rollback, roll it back and start a new TX
|
||||
Set<ConstraintViolation<?>> cvs = cve.getConstraintViolations();
|
||||
assertEquals(1, cvs.size());
|
||||
for (ConstraintViolation<?> cv : cvs) {
|
||||
log.trace("Message: " + cv.getMessage());
|
||||
log.trace("RootBean: " + cv.getRootBean());
|
||||
log.trace("LeafBean: " + cv.getLeafBean());
|
||||
log.trace("PropertyPath: " + cv.getPropertyPath());
|
||||
log.trace("Invalid value: " + cv.getInvalidValue());
|
||||
|
||||
assertEquals("City must be specified.", cv.getMessage());
|
||||
assertEquals("Image", cv.getRootBeanClass().getSimpleName());
|
||||
// The violation occurred on a leaf bean (embeddable)
|
||||
assertEquals("Location", cv.getLeafBean().getClass().getSimpleName());
|
||||
assertTrue(cv.getLeafBean().getClass() != cv.getRootBeanClass());
|
||||
assertEquals("location.city", cv.getPropertyPath().toString());
|
||||
assertNull(cv.getInvalidValue());
|
||||
}
|
||||
} finally {
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
emf2.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveEmbedCityNullValidation() {
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
|
||||
// Remove with null city name in location
|
||||
Location loc = new Location();
|
||||
loc.setCity("Rochester");
|
||||
loc.setStreet("3605 Hwy 52 N");
|
||||
loc.setState("MN");
|
||||
loc.setZipCode("55901");
|
||||
loc.setCountry("USA");
|
||||
|
||||
Image img = new Image();
|
||||
img.setId(1);
|
||||
img.setFileName("Winter_01.gif");
|
||||
img.setLocation(loc);
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("------------------------------------------------");
|
||||
log.trace("** Create normal Image/location" );
|
||||
em.persist(img);
|
||||
em.getTransaction().commit();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
fail();
|
||||
}
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("** set null city name in location and remove" );
|
||||
img.getLocation().setCity(null);
|
||||
em.remove(img);
|
||||
fail();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
// Transaction was marked for rollback, roll it back and
|
||||
// start a new TX
|
||||
Set<ConstraintViolation<?>> cvs = cve.getConstraintViolations();
|
||||
assertEquals(1, cvs.size());
|
||||
for (ConstraintViolation<?> cv : cvs) {
|
||||
log.trace("Message: " + cv.getMessage());
|
||||
log.trace("RootBean: " + cv.getRootBean());
|
||||
log.trace("LeafBean: " + cv.getLeafBean());
|
||||
log.trace("PropertyPath: " + cv.getPropertyPath());
|
||||
log.trace("Invalid value: " + cv.getInvalidValue());
|
||||
|
||||
assertEquals("City must be specified.", cv.getMessage());
|
||||
assertEquals("Image", cv.getRootBeanClass().getSimpleName());
|
||||
// The violation occurred on a leaf bean (embeddable)
|
||||
assertEquals("Location", cv.getLeafBean().getClass().getSimpleName());
|
||||
assertTrue(cv.getLeafBean().getClass() != cv.getRootBeanClass());
|
||||
assertEquals("location.city", cv.getPropertyPath().toString());
|
||||
assertNull(cv.getInvalidValue());
|
||||
}
|
||||
} finally {
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
emf2.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testFlushEmbedCityNullValidation() {
|
||||
EntityManager em = emf2.createEntityManager();
|
||||
|
||||
// set invalid zipCode in location and flush testing pre-update
|
||||
Location loc = new Location();
|
||||
loc.setCity("Rochester");
|
||||
loc.setStreet("3605 Hwy 52 N");
|
||||
loc.setState("MN");
|
||||
loc.setZipCode("55901");
|
||||
loc.setCountry("USA");
|
||||
|
||||
Image img = new Image();
|
||||
img.setId(1);
|
||||
img.setFileName("Winter_01.gif");
|
||||
img.setLocation(loc);
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("------------------------------------------------");
|
||||
log.trace("** Create normal Image/location" );
|
||||
em.persist(img);
|
||||
em.getTransaction().commit();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
fail();
|
||||
}
|
||||
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
log.trace("** set invalid zipCode and flush testing pre-update" );
|
||||
img.getLocation().setZipCode("abcde");
|
||||
em.flush();
|
||||
fail();
|
||||
} catch (ConstraintViolationException cve) {
|
||||
// Transaction was marked for rollback, roll it back and start a new TX
|
||||
Set<ConstraintViolation<?>> cvs = cve.getConstraintViolations();
|
||||
assertEquals(1, cvs.size());
|
||||
for (ConstraintViolation<?> cv : cvs) {
|
||||
log.trace("Message: " + cv.getMessage());
|
||||
log.trace("RootBean: " + cv.getRootBean());
|
||||
log.trace("LeafBean: " + cv.getLeafBean());
|
||||
log.trace("PropertyPath: " + cv.getPropertyPath());
|
||||
log.trace("Invalid value: " + cv.getInvalidValue());
|
||||
|
||||
assertEquals("Zip code must be 5 digits or use the 5+4 format.", cv.getMessage());
|
||||
assertEquals("Image", cv.getRootBeanClass().getSimpleName());
|
||||
// The violation occurred on a leaf bean (embeddable)
|
||||
assertEquals("Location", cv.getLeafBean().getClass().getSimpleName());
|
||||
assertTrue(cv.getLeafBean().getClass() != cv.getRootBeanClass());
|
||||
assertEquals("location.zipCode", cv.getPropertyPath().toString());
|
||||
assertEquals("abcde", cv.getInvalidValue());
|
||||
}
|
||||
} finally {
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
emf2.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -210,4 +210,16 @@
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Persistence unit for testing ConstraintViolation object -->
|
||||
<persistence-unit name="ConstraintViolationTestPU">
|
||||
<class>org.apache.openjpa.integration.validation.Image</class>
|
||||
<class>org.apache.openjpa.integration.validation.Location</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<validation-mode>CALLBACK</validation-mode>
|
||||
<properties>
|
||||
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" />
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
</persistence>
|
||||
|
@ -96,6 +96,7 @@ import org.apache.openjpa.util.StoreException;
|
||||
import org.apache.openjpa.util.UnsupportedException;
|
||||
import org.apache.openjpa.util.UserException;
|
||||
import org.apache.openjpa.util.WrappedException;
|
||||
import org.apache.openjpa.validation.ValidatingLifecycleEventManager;
|
||||
|
||||
/**
|
||||
* Concrete {@link Broker}. The broker handles object-level behavior,
|
||||
@ -2759,7 +2760,18 @@ public class BrokerImpl
|
||||
throw newDetachedException(obj, "delete");
|
||||
if ((action & OpCallbacks.ACT_CASCADE) != 0) {
|
||||
if (!sm.isEmbedded() || !sm.getDereferencedEmbedDependent()) {
|
||||
sm.cascadeDelete(call);
|
||||
if (ValidatingLifecycleEventManager.class.isAssignableFrom(_lifeEventManager.getClass())) {
|
||||
ValidatingLifecycleEventManager _validatingLCEventManager =
|
||||
(ValidatingLifecycleEventManager) _lifeEventManager;
|
||||
boolean saved = _validatingLCEventManager.setValidationEnabled(false);
|
||||
try {
|
||||
sm.cascadeDelete(call);
|
||||
} finally {
|
||||
_validatingLCEventManager.setValidationEnabled(saved);
|
||||
}
|
||||
} else {
|
||||
sm.cascadeDelete(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
sm.delete();
|
||||
|
@ -37,6 +37,7 @@ public class ValidatingLifecycleEventManager extends LifecycleEventManager
|
||||
|
||||
private OpenJPAConfiguration _conf = null;
|
||||
private Validator _validator = null;
|
||||
protected boolean _validationEnabled = true;
|
||||
|
||||
/**
|
||||
* Constructor which accepts a reference to the validator to use. If null,
|
||||
@ -118,7 +119,7 @@ public class ValidatingLifecycleEventManager extends LifecycleEventManager
|
||||
|
||||
// If a validator is provided and the source object should be validated,
|
||||
// validate it and return any exceptions
|
||||
if (_validator != null && _validator.validating(source, type)) {
|
||||
if (_validationEnabled && _validator != null && _validator.validating(source, type)) {
|
||||
ValidationException vex = _validator.validate(source, type);
|
||||
if (vex != null) {
|
||||
if (evx == null || evx.length == 0) {
|
||||
@ -137,4 +138,14 @@ public class ValidatingLifecycleEventManager extends LifecycleEventManager
|
||||
}
|
||||
return evx;
|
||||
}
|
||||
|
||||
public boolean isValidationEnabled() {
|
||||
return _validationEnabled;
|
||||
}
|
||||
|
||||
public boolean setValidationEnabled(boolean enabled) {
|
||||
boolean val = _validationEnabled;
|
||||
_validationEnabled = enabled;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user