Skip to content

Commit 2afd8ae

Browse files
Paul Sterlmp911de
Paul Sterl
authored andcommitted
DATAMONGO-1393 - Fix ValidatingMongoEventListener when using lazy proxies.
ValidatingMongoEventListener checks now whether the validated object is a lazy-loading proxy. In such a case the proxy is unwrapped and the validation is performed with the underlying target object. Original pull request: #350. CLA: 168220160322083905 (Paul Sterl)
1 parent 28d56ec commit 2afd8ae

File tree

7 files changed

+227
-8
lines changed

7 files changed

+227
-8
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
25+
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
2526
import org.springframework.util.Assert;
2627

2728
import com.mongodb.DBObject;
@@ -31,6 +32,8 @@
3132
* before entities are saved in database.
3233
*
3334
* @author Maciej Walkowiak
35+
* @author Oliver Gierke
36+
* @author Paul Sterl
3437
*/
3538
public class ValidatingMongoEventListener extends AbstractMongoEventListener<Object> {
3639

@@ -57,11 +60,12 @@ public ValidatingMongoEventListener(Validator validator) {
5760
public void onBeforeSave(Object source, DBObject dbo) {
5861

5962
LOG.debug("Validating object: {}", source);
60-
Set violations = validator.validate(source);
63+
final Object target = source instanceof LazyLoadingProxy ? ((LazyLoadingProxy) source).getTarget() : source;
64+
final Set violations = validator.validate(target);
6165

6266
if (!violations.isEmpty()) {
6367

64-
LOG.info("During object: {} validation violations found: {}", source, violations);
68+
LOG.info("During object: {} validation violations found: {}", target, violations);
6569
throw new ConstraintViolationException(violations);
6670
}
6771
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2016 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+
* http://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 javax.validation.constraints.NotNull;
19+
import javax.validation.constraints.Size;
20+
21+
import org.bson.types.ObjectId;
22+
import org.springframework.data.annotation.Id;
23+
import org.springframework.data.mongodb.core.mapping.Document;
24+
25+
/**
26+
* @see DATAMONGO-1393
27+
* @author Paul Sterl
28+
*/
29+
@Document(collection = "ADDRESS")
30+
public class Address {
31+
32+
@Id
33+
private ObjectId id;
34+
35+
@NotNull @Size(min = 2)
36+
private String name;
37+
38+
public Address() {
39+
super();
40+
}
41+
public Address(String name) {
42+
this();
43+
this.name = name;
44+
}
45+
public ObjectId getId() {
46+
return id;
47+
}
48+
public void setId(ObjectId id) {
49+
this.id = id;
50+
}
51+
public String getName() {
52+
return name;
53+
}
54+
public void setName(String name) {
55+
this.name = name;
56+
}
57+
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/User.java

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,28 +15,93 @@
1515
*/
1616
package org.springframework.data.mongodb.core.mapping.event;
1717

18+
import java.util.ArrayList;
19+
import java.util.List;
20+
1821
import javax.validation.constraints.Min;
1922
import javax.validation.constraints.Size;
2023

24+
import org.bson.types.ObjectId;
25+
import org.springframework.data.annotation.Id;
26+
import org.springframework.data.mongodb.core.mapping.DBRef;
27+
import org.springframework.data.mongodb.core.mapping.Document;
28+
import org.springframework.data.mongodb.core.mapping.event.User.AddressRelation.Type;
29+
2130
/**
2231
* Class used to test JSR-303 validation
2332
* {@link org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener}
2433
*
2534
* @see DATAMONGO-36
2635
* @author Maciej Walkowiak
36+
* @author Paul Sterl
2737
*/
38+
@Document(collection = "USER")
2839
public class User {
40+
41+
public static class AddressRelation {
42+
public enum Type {
43+
PRIVATE,
44+
WORK
45+
}
46+
private Type type = Type.PRIVATE;
47+
@DBRef(lazy = true)
48+
private Address address;
49+
// needed otherwise spring doesn't create the proxy for some reason ...
50+
public AddressRelation() {
51+
super();
52+
}
53+
public AddressRelation(Type type, Address address) {
54+
this();
55+
this.type = type;
56+
this.address = address;
57+
}
58+
public Type getType() {
59+
return type;
60+
}
61+
public void setType(Type type) {
62+
this.type = type;
63+
}
64+
public Address getAddress() {
65+
return address;
66+
}
67+
public void setAddress(Address address) {
68+
this.address = address;
69+
}
70+
}
71+
72+
@Id
73+
private ObjectId id;
2974

3075
@Size(min = 10)
3176
private String name;
3277

3378
@Min(18)
3479
private Integer age;
80+
81+
private List<AddressRelation> addresses = new ArrayList<AddressRelation>();
3582

83+
public User() {
84+
super();
85+
}
3686
public User(String name, Integer age) {
87+
this();
3788
this.name = name;
3889
this.age = age;
3990
}
91+
public User(String name, Integer age, Address...addresses) {
92+
this(name, age);
93+
for (Address address : addresses) {
94+
this.addresses.add(new AddressRelation(Type.WORK, address));
95+
}
96+
}
97+
98+
public ObjectId getId() {
99+
return id;
100+
}
101+
102+
public void setId(ObjectId id) {
103+
this.id = id;
104+
}
40105

41106
public String getName() {
42107
return name;
@@ -45,4 +110,10 @@ public String getName() {
45110
public Integer getAge() {
46111
return age;
47112
}
113+
public List<AddressRelation> getAddresses() {
114+
return addresses;
115+
}
116+
public void setAddresses(List<AddressRelation> addresses) {
117+
this.addresses = addresses;
118+
}
48119
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2014 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,13 +18,17 @@
1818
import static org.hamcrest.core.IsEqual.*;
1919
import static org.junit.Assert.*;
2020

21+
import java.util.UUID;
22+
2123
import javax.validation.ConstraintViolationException;
2224

2325
import org.junit.ClassRule;
2426
import org.junit.Test;
2527
import org.junit.runner.RunWith;
2628
import org.springframework.beans.factory.annotation.Autowired;
27-
import org.springframework.data.mongodb.core.MongoTemplate;
29+
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
30+
import org.springframework.data.mongodb.core.mapping.event.repo.AddressRepository;
31+
import org.springframework.data.mongodb.core.mapping.event.repo.UserRepository;
2832
import org.springframework.data.mongodb.test.util.MongoVersionRule;
2933
import org.springframework.data.util.Version;
3034
import org.springframework.test.context.ContextConfiguration;
@@ -37,22 +41,24 @@
3741
* @author Maciej Walkowiak
3842
* @author Oliver Gierke
3943
* @author Christoph Strobl
44+
* @author Paul Sterl
4045
*/
4146
@RunWith(SpringJUnit4ClassRunner.class)
4247
@ContextConfiguration
4348
public class ValidatingMongoEventListenerTests {
4449

4550
public static @ClassRule MongoVersionRule version = MongoVersionRule.atLeast(new Version(2, 6));
4651

47-
@Autowired MongoTemplate mongoTemplate;
52+
@Autowired AddressRepository addressRepo;
53+
@Autowired UserRepository userRepo;
4854

4955
@Test
5056
public void shouldThrowConstraintViolationException() {
5157

5258
User user = new User("john", 17);
5359

5460
try {
55-
mongoTemplate.save(user);
61+
userRepo.save(user);
5662
fail();
5763
} catch (ConstraintViolationException e) {
5864
assertThat(e.getConstraintViolations().size(), equalTo(2));
@@ -61,6 +67,34 @@ public void shouldThrowConstraintViolationException() {
6167

6268
@Test
6369
public void shouldNotThrowAnyExceptions() {
64-
mongoTemplate.save(new User("john smith", 18));
70+
userRepo.save(new User("john smith", 18));
71+
}
72+
73+
/**
74+
* @see DATAMONGO-1393
75+
*/
76+
@Test
77+
public void validationOfLazyProxy() {
78+
Address address = addressRepo.insert(new Address(UUID.randomUUID().toString()));
79+
User user = userRepo.insert(new User("Foo Bar Name 123456", 18, address));
80+
81+
user = userRepo.findOne(user.getId());
82+
assertTrue("Spring should use now a LazyLoadingProxy.", user.getAddresses().get(0).getAddress() instanceof LazyLoadingProxy);
83+
address = user.getAddresses().get(0).getAddress();
84+
address.setName("New Fancy Address Name");
85+
addressRepo.save(address);
86+
}
87+
/**
88+
* @see DATAMONGO-1393
89+
*/
90+
@Test(expected = ConstraintViolationException.class)
91+
public void validationErrorTest() {
92+
Address address = addressRepo.insert(new Address(UUID.randomUUID().toString()));
93+
User user = userRepo.insert(new User("Foo Bar Name 123456", 18, address));
94+
95+
user = userRepo.findOne(user.getId());
96+
address = user.getAddresses().get(0).getAddress();
97+
address.setName(null);
98+
addressRepo.save(address);
6599
}
66100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2016 by the original author(s).
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+
* http://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.repo;
17+
18+
import org.bson.types.ObjectId;
19+
import org.springframework.data.mongodb.core.mapping.event.Address;
20+
import org.springframework.data.mongodb.repository.MongoRepository;
21+
22+
/**
23+
* @author Paul Sterl
24+
*/
25+
public interface AddressRepository extends MongoRepository<Address, ObjectId> {
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2016 by the original author(s).
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+
* http://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.repo;
17+
18+
import org.bson.types.ObjectId;
19+
import org.springframework.data.mongodb.core.mapping.event.User;
20+
import org.springframework.data.mongodb.repository.MongoRepository;
21+
22+
/**
23+
* @author Paul Sterl
24+
*/
25+
public interface UserRepository extends MongoRepository<User, ObjectId> {
26+
}

spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests-context.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<mongo:db-factory dbname="validation" />
1111

1212
<mongo:mapping-converter base-package="org.springframework.data.mongodb.core" />
13+
<mongo:repositories base-package="org.springframework.data.mongodb.core.mapping.event.repo" />
1314

1415
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
1516
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />

0 commit comments

Comments
 (0)