Skip to content

Commit 8ec1d0e

Browse files
committed
spring-projectsgh-4901: introduce ValidatingEntityCallback
1 parent 23ad636 commit 8ec1d0e

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/ValidatingEntityCallback.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.core.mapping.event;
1717

18+
import jakarta.validation.ConstraintViolation;
1819
import jakarta.validation.ConstraintViolationException;
1920
import jakarta.validation.Validator;
2021
import java.util.Set;
@@ -25,7 +26,7 @@
2526
import org.springframework.util.Assert;
2627

2728
/**
28-
* javax.validation dependant entities validator.
29+
* JSR-303 dependant entities validator.
2930
* <p>
3031
* When it is registered as Spring component its automatically invoked
3132
* after any {@link AbstractMongoEventListener} and before entities are saved in database.
@@ -39,7 +40,7 @@ public class ValidatingEntityCallback implements BeforeSaveCallback<Object>, Ord
3940

4041
private static final Log LOG = LogFactory.getLog(ValidatingEntityCallback.class);
4142

42-
// TODO: discuss with spring team, if a handler encapsulating validation logic makes more sense (similar to "AuditingHandler")
43+
// TODO: create a validation handler (similar to "AuditingHandler") an reference it from "ValidatingMongoEventListener" and "ValidatingMongoEventListener"
4344
private final Validator validator;
4445

4546
/**
@@ -48,7 +49,6 @@ public class ValidatingEntityCallback implements BeforeSaveCallback<Object>, Ord
4849
* @param validator must not be {@literal null}.
4950
*/
5051
public ValidatingEntityCallback(Validator validator) {
51-
5252
Assert.notNull(validator, "Validator must not be null");
5353
this.validator = validator;
5454
}
@@ -60,12 +60,11 @@ public Object onBeforeSave(Object entity, Document document, String collection)
6060
if (LOG.isDebugEnabled()) {
6161
LOG.debug(String.format("Validating object: %s", entity));
6262
}
63-
Set violations = validator.validate(entity);
63+
Set<ConstraintViolation<Object>> violations = validator.validate(entity);
6464

6565
if (!violations.isEmpty()) {
66-
6766
if (LOG.isDebugEnabled()) {
68-
LOG.info(String.format("During object: %s validation violations found: %s", event.getSource(), violations));
67+
LOG.info(String.format("During object: %s validation violations found: %s", entity, violations));
6968
}
7069
throw new ConstraintViolationException(violations);
7170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.mapping.event;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20+
21+
import jakarta.validation.ConstraintViolationException;
22+
import jakarta.validation.Validation;
23+
import jakarta.validation.ValidatorFactory;
24+
import jakarta.validation.constraints.Min;
25+
import jakarta.validation.constraints.NotNull;
26+
import org.bson.Document;
27+
import org.junit.jupiter.api.BeforeEach;
28+
import org.junit.jupiter.api.Test;
29+
30+
/**
31+
* Unit test for {@link ValidatingEntityCallback}.
32+
*
33+
* @author Rene Felgenträger
34+
*/
35+
class ValidatingEntityCallbackTests {
36+
37+
private ValidatingEntityCallback callback;
38+
39+
@BeforeEach
40+
public void setUp() {
41+
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
42+
callback = new ValidatingEntityCallback(factory.getValidator());
43+
}
44+
}
45+
46+
@Test
47+
void invalidModel_throwsException() {
48+
Coordinates coordinates = new Coordinates(-1, -1);
49+
50+
assertThatExceptionOfType(ConstraintViolationException.class)
51+
.isThrownBy(() -> callback.onBeforeSave(coordinates, coordinates.toDocument(), "coordinates"))
52+
.satisfies(e -> assertThat(e.getConstraintViolations()).hasSize(2));
53+
}
54+
55+
@Test
56+
void validModel_noException() {
57+
Coordinates coordinates = new Coordinates(0, 0);
58+
Object entity = callback.onBeforeSave(coordinates, coordinates.toDocument(), "coordinates");
59+
assertThat(entity).isEqualTo(coordinates);
60+
}
61+
62+
record Coordinates(@NotNull @Min(0) Integer x, @NotNull @Min(0) Integer y) {
63+
64+
Document toDocument() {
65+
return Document.parse("""
66+
{
67+
"x": %d,
68+
"y": %d
69+
}
70+
""".formatted(x, y));
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)