Add MongoDB example for linking documents.

Closes #631
This commit is contained in:
Christoph Strobl
2021-10-04 13:45:37 +02:00
committed by Greg L. Turnquist
parent a171e33444
commit af124509c8
21 changed files with 1029 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.dbref;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Christoph Strobl
*/
@SpringBootApplication
class ApplicationConfiguration {}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.dbref;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
/**
* @author Christoph Strobl
*/
class Employee {
@Id
private String id;
private String name;
@DBRef(lazy = true) // lazy to avoid stackoverflow on load
private Manager manager;
public Employee() {
}
public Employee(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 Manager getManager() {
return manager;
}
public void setManager(Manager manager) {
this.manager = manager;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.dbref;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
/**
* @author Christoph Strobl
*/
class Manager {
@Id //
private String id;
private String name;
@DBRef //
private List<Employee> employees;
public Manager() {
}
public Manager(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 List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.jpastyle;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Christoph Strobl
*/
@SpringBootApplication
class ApplicationConfiguration {
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.jpastyle;
import org.springframework.data.annotation.Id;
/**
* @author Christoph Strobl
*/
class Employee {
@Id
private String id;
private String name;
/**
* Only hold the id of the manager to link to it.
*/
private String managerId;
public Employee() {
}
public Employee(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 String getManagerId() {
return managerId;
}
public void setManagerId(String managerId) {
this.managerId = managerId;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.jpastyle;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
/**
* @author Christoph Strobl
*/
class Manager {
@Id //
private String id;
private String name;
/**
* Uses the {@literal this.id} to look up documents in the {@literal employee} collection
* that have a matching {@literal managerId} field value.
*/
@ReadOnlyProperty
@DocumentReference(lookup="{'managerId':?#{#self._id} }")
private List<Employee> employees;
public Manager() {
}
public Manager(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 List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.query;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Christoph Strobl
*/
@SpringBootApplication
class ApplicationConfiguration {}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.query;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
/**
* @author Christoph Strobl
*/
class Employee {
@Id
private String id;
private String name;
@DocumentReference(lazy = true) // lazy to avoid stackoverflow on load
private Manager manager;
public Employee() {
}
public Employee(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 Manager getManager() {
return manager;
}
public void setManager(Manager manager) {
this.manager = manager;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.query;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
/**
* @author Christoph Strobl
*/
class Manager {
@Id //
private String id;
private String name;
/**
* Uses the value stored in the {@literal employees} field of the {@literal manager} document to look up
* documents in the {@literal employee} collection that have a matching {@literal name} field value.
*/
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") //
private List<Employee> employees;
public Manager() {
}
public Manager(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 List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.simple;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Christoph Strobl
*/
@SpringBootApplication
class ApplicationConfiguration {}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.simple;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
/**
* @author Christoph Strobl
*/
class Employee {
@Id
private String id;
private String name;
@DocumentReference(lazy = true) // lazy to avoid stackoverflow on load
private Manager manager;
public Employee() {
}
public Employee(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 Manager getManager() {
return manager;
}
public void setManager(Manager manager) {
this.manager = manager;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.simple;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
/**
* @author Christoph Strobl
*/
class Manager {
@Id //
private String id;
private String name;
/**
* Uses the value stored in the {@literal employees} field of the {@literal manager} document to look up
* documents in the {@literal employee} collection that have a matching {@literal _id} field value.
*/
@DocumentReference //
private List<Employee> employees;
public Manager() {
}
public Manager(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 List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.dbref;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.List;
import com.mongodb.DBRef;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
/**
* @author Christoph Strobl
*/
@DataMongoTest
public class DBRefTests {
@Autowired MongoOperations operations;
@Test
void saveAndLoadDbRef() {
Manager manager = new Manager("jabba-the-hutt", "jabba");
operations.save(manager);
Employee employee1 = new Employee("greedo-tetsu-jr", "greedo");
employee1.setManager(manager);
operations.save(employee1);
operations.update(Manager.class) //
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee1)) //
.first();
Employee employee2 = new Employee("boba-fett", "boba");
employee2.setManager(manager);
operations.save(employee2);
operations.update(Manager.class) //
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee2)) //
.first();
operations.execute(Manager.class, collection -> {
Document rawManager = collection.find(new Document("_id", manager.getId())).first();
assertThat(rawManager.get("employees", List.class)) //
.containsExactly( //
new DBRef("employee", "greedo-tetsu-jr"), //
new DBRef("employee", "boba-fett") //
);
return "OK";
});
Manager loaded = operations.query(Manager.class)
.matching(where("id").is(manager.getId()))
.firstValue();
assertThat(loaded.getEmployees()) //
.allMatch(it -> it instanceof Employee) //
.extracting("name").containsExactly("greedo", "boba");
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.jpastyle;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoOperations;
/**
* @author Christoph Strobl
*/
@DataMongoTest
public class JpaStyleDocRefTests {
@Autowired MongoOperations operations;
/**
* Load linked documents where where the actual reference is stored in the obverse side of the association.
*/
@Test
void saveAndLoadJpaStyleRelation() {
Manager manager = new Manager("jabba-the-hutt", "jabba");
operations.save(manager);
Employee employee1 = new Employee("greedo-tetsu-jr", "greedo");
employee1.setManagerId(manager.getId()); // establish the link to the manager document
operations.save(employee1);
// no need to update he manager document after save of employee
Employee employee2 = new Employee("boba-fett", "boba");
employee2.setManagerId(manager.getId()); // establish the link to the manager document
operations.save(employee2);
// no need to update he manager document after save of employee
operations.execute(Manager.class, collection -> {
Document rawManager = collection.find(new Document("_id", manager.getId())).first();
assertThat(rawManager).doesNotContainKey("employees");
return "OK";
});
Manager loaded = operations.query(Manager.class)
.matching(where("id").is(manager.getId()))
.firstValue();
assertThat(loaded.getEmployees()) //
.allMatch(it -> it instanceof Employee) //
.extracting("name").containsExactly("greedo", "boba");
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.query;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.List;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
/**
* @author Christoph Strobl
*/
@DataMongoTest
public class QueryDocRefTests {
@Autowired MongoOperations operations;
/**
* Load linked documents where where the reference is stored on the inverse and evaluated against a non id property on
* the obverse side of the association.
*/
@Test
void saveAndLoadDocRefViaQuery() {
Manager manager = new Manager("jabba-the-hutt", "jabba");
operations.save(manager);
Employee employee1 = new Employee("greedo-tetsu-jr", "greedo");
employee1.setManager(manager); // back-link to the manager document
operations.save(employee1);
operations.update(Manager.class) // establish the link to the employee document via employee.name
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee1.getName())) //
.first();
Employee employee2 = new Employee("boba-fett", "boba");
employee2.setManager(manager); // back-link to the manager document
operations.save(employee2);
operations.update(Manager.class) // establish the link to the employee document via employee.name
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee2.getName())) //
.first();
operations.execute(Manager.class, collection -> {
Document rawManager = collection.find(new Document("_id", manager.getId())).first();
assertThat(rawManager.get("employees", List.class)) //
.containsExactly("greedo", "boba");
return "OK";
});
Manager loaded = operations.query(Manager.class)
.matching(where("id").is(manager.getId()))
.firstValue();
assertThat(loaded.getEmployees()) //
.allMatch(it -> it instanceof Employee) //
.extracting("name").containsExactly("greedo", "boba");
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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 example.springdata.mongodb.linking.docref.simple;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.List;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
/**
* @author Christoph Strobl
*/
@DataMongoTest
public class SimpleDocRefTests {
@Autowired MongoOperations operations;
/**
* Load linked documents where where the reference is stored on the inverse and evaluated against the id property on
* the obverse side of the association.
*/
@Test
void saveAndLoadDocRef() {
Manager manager = new Manager("jabba-the-hutt", "jabba");
operations.save(manager);
Employee employee1 = new Employee("greedo-tetsu-jr", "greedo");
employee1.setManager(manager); // back-link to the manager document
operations.save(employee1);
operations.update(Manager.class) // establish the link to the employee document via employee.id
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee1)) //
.first();
Employee employee2 = new Employee("boba-fett", "boba");
employee2.setManager(manager); // back-link to the manager document
operations.save(employee2);
operations.update(Manager.class) // establish the link to the employee document via employee.id
.matching(where("id").is(manager.getId())) //
.apply(new Update().push("employees").value(employee2)) //
.first();
operations.execute(Manager.class, collection -> {
Document rawManager = collection.find(new Document("_id", manager.getId())).first();
assertThat(rawManager.get("employees", List.class)) //
.containsExactly("greedo-tetsu-jr", "boba-fett");
return "OK";
});
Manager loaded = operations.query(Manager.class)
.matching(where("id").is(manager.getId()))
.firstValue();
assertThat(loaded.getEmployees()) //
.allMatch(it -> it instanceof Employee) //
.extracting("name").containsExactly("greedo", "boba");
}
}

View File

@@ -0,0 +1,3 @@
# Random port for embedded MongoDB
spring.data.mongodb.port=0
spring.mongodb.embedded.version=3.6.0