Skip to content

Commit 7e50e0b

Browse files
mp911degregturn
authored andcommitted
Add example showing PropertyValueConverter usage.
Closes #637
1 parent 83cd9e2 commit 7e50e0b

File tree

9 files changed

+345
-3
lines changed

9 files changed

+345
-3
lines changed

mongodb/example/README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
This project contains examples for:
44

5-
* Derived finder and geo spatial queries using a `Repository` interface.
5+
* Derived query method and geospatial queries using a `Repository` interface.
6+
* Usage of property-specific converters.
67
* `EntityCallback` API usage for before convert/save interaction.
7-
* Result projections for DTOs and interface types.
8-
* Query metadata.
8+
* Result projections for DTOs and interface types.
9+
* Query metadata.
910
* Unwrapping entities into the parent document.
1011

mongodb/example/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
</build>
4747

4848
<dependencies>
49+
<dependency>
50+
<groupId>com.fasterxml.jackson.core</groupId>
51+
<artifactId>jackson-databind</artifactId>
52+
</dependency>
4953
<dependency>
5054
<groupId>org.springframework.data.examples</groupId>
5155
<artifactId>spring-data-mongodb-example-utils</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2014-2021 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 example.springdata.mongodb.converters;
17+
18+
import lombok.Data;
19+
import lombok.RequiredArgsConstructor;
20+
21+
import java.util.Map;
22+
23+
import org.springframework.data.geo.Point;
24+
25+
import com.fasterxml.jackson.annotation.JsonProperty;
26+
27+
/**
28+
* A domain object to capture addresses.
29+
*
30+
* @author Mark Paluch
31+
*/
32+
@Data
33+
@RequiredArgsConstructor
34+
public class Address {
35+
36+
private final Point location;
37+
private String street;
38+
private String zipCode;
39+
40+
Address(@JsonProperty("location") Map<String, Double> location, @JsonProperty("street") String street,
41+
@JsonProperty("zipCode") String zipCode) {
42+
43+
this.location = new Point(location.get("x"), location.get("y"));
44+
this.street = street;
45+
this.zipCode = zipCode;
46+
}
47+
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2022 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 example.springdata.mongodb.converters;
17+
18+
import org.bson.Document;
19+
20+
import org.springframework.data.geo.Point;
21+
import org.springframework.data.mongodb.core.convert.MongoConversionContext;
22+
import org.springframework.data.mongodb.core.convert.MongoValueConverter;
23+
24+
/**
25+
* {@link MongoValueConverter} to write only the {@link Address#location} into a MongoDB {@link Document}.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
class AddressDocumentConverter implements MongoValueConverter<Address, Document> {
30+
31+
@Override
32+
public Address read(Document value, MongoConversionContext context) {
33+
34+
Point point = new Point(context.read(value.get("x"), Double.class), context.read(value.get("y"), Double.class));
35+
36+
return new Address(point);
37+
}
38+
39+
@Override
40+
public Document write(Address value, MongoConversionContext context) {
41+
42+
Document document = new Document();
43+
document.put("x", value.getLocation().getX());
44+
document.put("y", value.getLocation().getY());
45+
46+
return document;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2022 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 example.springdata.mongodb.converters;
17+
18+
import org.springframework.data.mongodb.core.convert.MongoConversionContext;
19+
import org.springframework.data.mongodb.core.convert.MongoValueConverter;
20+
21+
import com.fasterxml.jackson.core.JsonProcessingException;
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
24+
/**
25+
* {@link MongoValueConverter} to write {@link Address} into JSON through Jackson.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
class AddressJsonConverter implements MongoValueConverter<Address, String> {
30+
31+
private final ObjectMapper mapper = new ObjectMapper();
32+
33+
@Override
34+
public Address read(String value, MongoConversionContext context) {
35+
36+
try {
37+
return mapper.readValue(value, Address.class);
38+
} catch (JsonProcessingException e) {
39+
throw new IllegalStateException("Cannot unmarshal Address JSON", e);
40+
}
41+
}
42+
43+
@Override
44+
public String write(Address value, MongoConversionContext context) {
45+
try {
46+
return mapper.writeValueAsString(value);
47+
} catch (JsonProcessingException e) {
48+
throw new IllegalStateException("Cannot marshal Address into JSON", e);
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2014-2021 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 example.springdata.mongodb.converters;
17+
18+
import org.springframework.boot.autoconfigure.SpringBootApplication;
19+
20+
/**
21+
* Test configuration.
22+
*
23+
* @author Mark Paluch
24+
*/
25+
@SpringBootApplication
26+
class ApplicationConfiguration {
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2014-2021 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 example.springdata.mongodb.converters;
17+
18+
import lombok.Data;
19+
20+
import org.springframework.data.convert.ValueConverter;
21+
import org.springframework.data.mongodb.core.mapping.Document;
22+
import org.springframework.util.Assert;
23+
24+
/**
25+
* An entity to represent a customer.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
@Data
30+
@Document
31+
public class Customer {
32+
33+
private String id, firstname, lastname;
34+
35+
@ValueConverter(AddressJsonConverter.class) private Address primary;
36+
37+
@ValueConverter(AddressDocumentConverter.class) private Address secondary;
38+
39+
/**
40+
* Creates a new {@link Customer} with the given firstname and lastname.
41+
*
42+
* @param firstname must not be {@literal null} or empty.
43+
* @param lastname must not be {@literal null} or empty.
44+
*/
45+
public Customer(String firstname, String lastname) {
46+
47+
Assert.hasText(firstname, "Firstname must not be null or empty!");
48+
Assert.hasText(lastname, "Lastname must not be null or empty!");
49+
50+
this.firstname = firstname;
51+
this.lastname = lastname;
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2014-2021 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 example.springdata.mongodb.converters;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import example.springdata.mongodb.util.MongoContainers;
21+
22+
import org.bson.Document;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
28+
import org.springframework.data.geo.Point;
29+
import org.springframework.data.mongodb.core.MongoOperations;
30+
import org.springframework.data.mongodb.core.query.Query;
31+
import org.springframework.test.context.DynamicPropertyRegistry;
32+
import org.springframework.test.context.DynamicPropertySource;
33+
34+
import org.testcontainers.containers.MongoDBContainer;
35+
import org.testcontainers.junit.jupiter.Container;
36+
import org.testcontainers.junit.jupiter.Testcontainers;
37+
38+
/**
39+
* Integration tests for {@link Customer} conversions.
40+
*
41+
* @author Mark Paluch
42+
*/
43+
@Testcontainers
44+
@DataMongoTest
45+
class CustomerIntegrationTests {
46+
47+
@Container //
48+
private static MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();
49+
50+
@DynamicPropertySource
51+
static void setProperties(DynamicPropertyRegistry registry) {
52+
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
53+
}
54+
55+
@Autowired MongoOperations operations;
56+
57+
@BeforeEach
58+
void setUp() {
59+
operations.remove(Customer.class).all();
60+
}
61+
62+
/**
63+
* Applies {@link org.springframework.data.mongodb.core.convert.MongoValueConverter} to individual properties.
64+
* {@code primary} and {@code secondary} are configured with different converters of which the first one uses JSON
65+
* conversion and the second converter interacts directly with a MongoDB {@link Document}.
66+
*/
67+
@Test
68+
void appliesConverters() {
69+
70+
var primaryAddress = new Address(new Point(4.1, 5.6));
71+
primaryAddress.setStreet("308 Negra Arroyo Lane");
72+
primaryAddress.setZipCode("87104");
73+
74+
var secondaryAddress = new Address(new Point(6.1, 7.6));
75+
secondaryAddress.setStreet("3828 Piermont Drive");
76+
secondaryAddress.setZipCode("87111");
77+
78+
var customer = new Customer("Walter", "White");
79+
customer.setPrimary(primaryAddress);
80+
customer.setSecondary(secondaryAddress);
81+
82+
operations.insert(customer);
83+
84+
Document document = operations.findOne(new Query(), Document.class, "customer");
85+
86+
assertThat(document.get("primary"))
87+
.isEqualTo("{\"location\":{\"x\":4.1,\"y\":5.6},\"street\":\"308 Negra Arroyo Lane\",\"zipCode\":\"87104\"}");
88+
assertThat(document.get("secondary")).isEqualTo(
89+
new Document("x", secondaryAddress.getLocation().getX()).append("y", secondaryAddress.getLocation().getY()));
90+
91+
Customer loadedCustomer = operations.findOne(new Query(), Customer.class, "customer");
92+
93+
// Primary address marshalled as JSON
94+
assertThat(loadedCustomer.getPrimary()).isNotNull();
95+
assertThat(loadedCustomer.getPrimary().getStreet()).isEqualTo(primaryAddress.getStreet());
96+
assertThat(loadedCustomer.getPrimary().getZipCode()).isEqualTo(primaryAddress.getZipCode());
97+
assertThat(loadedCustomer.getPrimary().getLocation()).isEqualTo(primaryAddress.getLocation());
98+
99+
// Secondary address stores only location as the converter considers only points
100+
assertThat(loadedCustomer.getSecondary()).isNotNull();
101+
assertThat(loadedCustomer.getSecondary().getStreet()).isNull();
102+
assertThat(loadedCustomer.getSecondary().getLocation()).isEqualTo(secondaryAddress.getLocation());
103+
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Package showing converter usage with MongoDB entities.
3+
*/
4+
package example.springdata.mongodb.converters;

0 commit comments

Comments
 (0)