mongo example with new mongock added

This commit is contained in:
unknown
2020-07-17 01:10:42 +04:00
parent 07e27ac35a
commit d47ea85f45
36 changed files with 831 additions and 16 deletions
@@ -30,6 +30,11 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
@@ -0,0 +1,17 @@
package ru.otus.spring.rest;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.*;
@WebMvcTest
@ExtendWith(SpringExtension.class)
class PersonControllerTest {
@org.junit.jupiter.api.Test
void listPage() {
}
}
@@ -1,6 +1,8 @@
package ru.otus.example.ormdemo.models;
import lombok.*;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import javax.persistence.*;
import java.util.List;
@@ -10,6 +12,8 @@ import java.util.List;
@AllArgsConstructor
@Entity // Указывает, что данный класс является сущностью
@Table(name = "otus_students") // Задает имя таблицы, на которую будет отображаться сущность
@NamedEntityGraph(name = "otus-student-avatars-entity-graph",
attributeNodes = {@NamedAttributeNode("avatar")})
public class OtusStudent {
@Id // Позволяет указать какое поле является идентификатором
@GeneratedValue(strategy = GenerationType.IDENTITY) // Стратегия генерации идентификаторов
@@ -30,6 +34,7 @@ public class OtusStudent {
@JoinColumn(name = "student_id")
private List<EMail> emails;
@Fetch(FetchMode.SUBSELECT)
// Указывает на связь между таблицами "многие ко многим"
@ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
// Задает таблицу связей между таблицами для хранения родительской и связанной сущностью
@@ -4,10 +4,7 @@ import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import ru.otus.example.ormdemo.models.OtusStudent;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -26,31 +23,55 @@ public class OtusStudentRepositoryJpaImpl implements OtusStudentRepositoryJpa {
@Override
public OtusStudent save(OtusStudent student) {
return null;
if (student.getId() == 0) {
Query query = em.createQuery("insert into OtusStudent (name) " +
"select :name from OtusStudent s");
query.setParameter("name", student.getName());
query.executeUpdate();
return student;
} else {
return em.merge(student);
}
}
@Override
public Optional<OtusStudent> findById(long id) {
return Optional.empty();
public Optional<OtusStudent> findById (long id) {
return Optional.ofNullable(em.find(OtusStudent.class, id));
}
@Override
public List<OtusStudent> findAll() {
return Collections.emptyList();
EntityGraph<?> entityGraph = em.getEntityGraph("otus-student-avatars-entity-graph");
TypedQuery<OtusStudent> query = em.createQuery("select s from OtusStudent s join fetch s.emails", OtusStudent.class);
query.setHint("javax.persistence.fetchgraph", entityGraph);
return query.getResultList();
}
@Override
public List<OtusStudent> findByName(String name) {
return Collections.emptyList();
TypedQuery<OtusStudent> query = em.createQuery
("select s from OtusStudent s where s.name = :name", OtusStudent.class);
query.setParameter("name", name);
return query.getResultList();
}
@Override
public void updateNameById(long id, String name) {
Query query = em.createQuery("update OtusStudent s " +
"set s.name = :name " +
"where s.id = :id");
query.setParameter("name", name);
query.setParameter("id", id);
query.executeUpdate();
}
@Override
public void deleteById(long id) {
Query query = em.createQuery("delete " +
"from OtusStudent s " +
"where s.id = :id");
query.setParameter("id", id);
query.executeUpdate();
}
}
@@ -27,7 +27,7 @@ class OtusStudentRepositoryJpaImplTest {
private static final long FIRST_STUDENT_ID = 1L;
private static final String FIRST_STUDENT_NAME = "student_01";
private static final int EXPECTED_QUERIES_COUNT = 31;
private static final int EXPECTED_QUERIES_COUNT = 2;
private static final String STUDENT_AVATAR_URL = "где-то там";
private static final String STUDENT_EMAIL = "any@mail.com";
@@ -43,6 +43,7 @@ class OtusStudentRepositoryJpaImplTest {
@DisplayName(" должен загружать информацию о нужном студенте по его id")
@Test
void shouldFindExpectedStudentById() {
val optionalActualStudent = repositoryJpa.findById(FIRST_STUDENT_ID);
val expectedStudent = em.find(OtusStudent.class, FIRST_STUDENT_ID);
assertThat(optionalActualStudent).isPresent().get()
@@ -37,8 +37,6 @@ public class OtusStudent {
@JoinColumn(name = "student_id")
private List<EMail> emails;
@Fetch(FetchMode.SELECT)
@BatchSize(size = 5)
// Указывает на связь между таблицами "многие ко многим"
@ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
// Задает таблицу связей между таблицами для хранения родительской и связанной сущностью
@@ -47,6 +47,8 @@ class OtusStudentRepositoryJpaImplTest {
System.out.println("\n\n\n\n----------------------------------------------------------------------------------------------------------");
val students = repositoryJpa.findAll();
val student = repositoryJpa.findById(1L);
assertThat(students).isNotNull().hasSize(EXPECTED_NUMBER_OF_STUDENTS)
.allMatch(s -> !s.getName().equals(""))
.allMatch(s -> s.getCourses() != null && s.getCourses().size() > 0)
@@ -257,6 +257,7 @@ class EmployeeRepositoryImplTest {
BigDecimal newSalary = oldSalary.multiply(new BigDecimal(2));
em.detach(employee3);
employeeRepository.doubleEmployeesSalary(oldSalary);
//em.refresh(employee3);
employee3 = em.find(Employee.class, THIRD_EMPLOYEE_ID);
assertThat(employee3.getSalary()).isEqualTo(newSalary);
@@ -271,7 +272,7 @@ class EmployeeRepositoryImplTest {
assertThat(employee8).isNotNull();
employeeRepository.deleteEmployeesWithoutDepartment();
em.clear();
em.flush();
employee2 = em.find(Employee.class, SECOND_EMPLOYEE_ID);
employee8 = em.find(Employee.class, EIGTH_EMPLOYEE_ID);
@@ -0,0 +1,29 @@
HELP.md
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/
### VS Code ###
.vscode/
@@ -0,0 +1,7 @@
## Пример работы с MongoDB
В примере демонстрируется:
* *два подхода к хранению вложенных сущностей*
* *инициализация базы данными с помощью инструмента миграций Mongock*
* *использование AbstractMongoEventListener для выполнения каскадных операций*
* *работа с массивами с помощью агрегаций и Criteria api*
+130
View File
@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.otus.example</groupId>
<artifactId>mongo-db-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mongo-db-demo</name>
<description>Demo project for MongoDB</description>
<properties>
<java.version>11</java.version>
<maven.compiler.sourcre>11</maven.compiler.sourcre>
<maven.compiler.target>11</maven.compiler.target>
<mongock.version>4.1.2.BETA</mongock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.cloudyrock.mongock/mongock-spring-v5 -->
<dependency>
<groupId>com.github.cloudyrock.mongock</groupId>
<artifactId>mongock-spring-v5</artifactId>
<version>${mongock.version}</version>
</dependency>
<dependency>
<groupId>com.github.cloudyrock.mongock</groupId>
<artifactId>mongodb-springdata-v3-driver</artifactId>
<version>${mongock.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Тестирование-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
@@ -0,0 +1,16 @@
package ru.otus.example.mongodbdemo;
import com.github.cloudyrock.spring.v5.EnableMongock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@EnableMongock
@SpringBootApplication
@EnableConfigurationProperties
public class MongoDbDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MongoDbDemoApplication.class, args);
}
}
@@ -0,0 +1,43 @@
package ru.otus.example.mongodbdemo.changelogs;
import com.github.cloudyrock.mongock.ChangeLog;
import com.github.cloudyrock.mongock.ChangeSet;
import com.mongodb.client.MongoDatabase;
import lombok.val;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.model.Teacher;
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
import ru.otus.example.mongodbdemo.repositories.TeacherRepository;
@ChangeLog(order = "001")
public class InitMongoDBDataChangeLog {
private Knowledge springDataKnowledge;
private Knowledge mongockKnowledge;
private Knowledge aggregationApiKnowledge;
@ChangeSet(order = "000", id = "dropDB", author = "stvort", runAlways = true)
public void dropDB(MongoDatabase database){
database.drop();
}
@ChangeSet(order = "001", id = "initKnowledges", author = "stvort", runAlways = true)
public void initKnowledges(KnowledgeRepository repository){
springDataKnowledge = repository.save(new Knowledge("Spring Data"));
mongockKnowledge = repository.save(new Knowledge("Mongock"));
aggregationApiKnowledge = repository.save(new Knowledge("AggregationApi"));
}
@ChangeSet(order = "002", id = "initStudents", author = "stvort", runAlways = true)
public void initStudents(StudentRepository repository){
repository.save(new Student("Student #1", springDataKnowledge, mongockKnowledge));
}
@ChangeSet(order = "003", id = "Teacher", author = "stvort", runAlways = true)
public void initTeachers(TeacherRepository repository){
val teacher = new Teacher("Teacher #1", springDataKnowledge, mongockKnowledge, aggregationApiKnowledge);
repository.save(teacher);
}
}
@@ -0,0 +1,24 @@
package ru.otus.example.mongodbdemo.events;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
import org.springframework.stereotype.Component;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
@Component
@RequiredArgsConstructor
public class MongoKnowledgeCascadeDeleteEventsListener extends AbstractMongoEventListener<Knowledge> {
private final StudentRepository studentRepository;
@Override
public void onAfterDelete(AfterDeleteEvent<Knowledge> event) {
super.onAfterDelete(event);
val source = event.getSource();
val id = source.get("_id").toString();
studentRepository.removeExperienceArrayElementsById(id);
}
}
@@ -0,0 +1,27 @@
package ru.otus.example.mongodbdemo.events;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.stereotype.Component;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
import java.util.Objects;
@Component
@RequiredArgsConstructor
public class MongoStudentCascadeSaveEventsListener extends AbstractMongoEventListener<Student> {
private final KnowledgeRepository knowledgeRepository;
@Override
public void onBeforeConvert(BeforeConvertEvent<Student> event) {
super.onBeforeConvert(event);
val student = event.getSource();
if (student.getExperience() != null) {
student.getExperience().stream().filter(e -> Objects.isNull(e.getId())).forEach(knowledgeRepository::save);
}
}
}
@@ -0,0 +1,21 @@
package ru.otus.example.mongodbdemo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document
public class Knowledge {
@Id
private String id;
private String name;
public Knowledge(String name) {
this.name = name;
}
}
@@ -0,0 +1,29 @@
package ru.otus.example.mongodbdemo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Arrays;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document
public class Student {
@Id
private String id;
private String name;
@DBRef
private List<Knowledge> experience;
public Student(String name, Knowledge... experience) {
this.name = name;
this.experience = Arrays.asList(experience);
}
}
@@ -0,0 +1,28 @@
package ru.otus.example.mongodbdemo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Arrays;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document
public class Teacher {
@Id
private String id;
private String name;
private List<Knowledge> experience;
public Teacher(String name, Knowledge... experience) {
this.name = name;
this.experience = Arrays.asList(experience);
}
}
@@ -0,0 +1,7 @@
package ru.otus.example.mongodbdemo.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import ru.otus.example.mongodbdemo.model.Knowledge;
public interface KnowledgeRepository extends MongoRepository<Knowledge, String> {
}
@@ -0,0 +1,8 @@
package ru.otus.example.mongodbdemo.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import ru.otus.example.mongodbdemo.model.Student;
public interface StudentRepository extends MongoRepository<Student, String>, StudentRepositoryCustom {
}
@@ -0,0 +1,12 @@
package ru.otus.example.mongodbdemo.repositories;
import ru.otus.example.mongodbdemo.model.Knowledge;
import java.util.List;
public interface StudentRepositoryCustom {
List<Knowledge> getStudentExperienceById(String studentId);
long getExperienceArrayLengthByStudentId(String id);
void removeExperienceArrayElementsById(String id);
}
@@ -0,0 +1,69 @@
package ru.otus.example.mongodbdemo.repositories;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.utils.RawResultPrinter;
import java.util.List;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.ObjectOperators.ObjectToArray.valueOfToArray;
import static org.springframework.data.mongodb.core.query.Criteria.where;
@RequiredArgsConstructor
public class StudentRepositoryCustomImpl implements StudentRepositoryCustom {
@Data
private class ArraySizeProjection {
private int size;
}
private final MongoTemplate mongoTemplate;
private final RawResultPrinter rawResultPrinter;
@Override
public List<Knowledge> getStudentExperienceById(String studentId) {
Aggregation aggregation = newAggregation(
match(Criteria.where("id").is(studentId))
, unwind("experience")
, project().andExclude("_id").and(valueOfToArray("experience")).as("experience_map")
, project().and("experience_map").arrayElementAt(1).as("experience_id_map")
, project().and("experience_id_map.v").as("experience_id")
, lookup("knowledge", "experience_id", "_id", "experience")
, project().and("experience._id").as("_id").and("experience.name").as("name")
);
Document rawResults = mongoTemplate.aggregate(aggregation, Student.class, Knowledge.class).getRawResults();
rawResultPrinter.prettyPrintRawResult(rawResults);
return mongoTemplate.aggregate(aggregation, Student.class, Knowledge.class).getMappedResults();
}
@Override
public long getExperienceArrayLengthByStudentId(String studentId) {
val aggregation = Aggregation.newAggregation(
match(where("id").is(studentId)),
project().andExclude("_id").and("experience").size().as("size"));
val arraySizeProjection = mongoTemplate.aggregate(aggregation, Student.class, ArraySizeProjection.class).getUniqueMappedResult();
return arraySizeProjection == null ? 0 : arraySizeProjection.getSize();
}
@Override
public void removeExperienceArrayElementsById(String id) {
val query = Query.query(Criteria.where("$id").is(new ObjectId(id)));
val update = new Update().pull("experience", query);
mongoTemplate.updateMulti(new Query(), update, Student.class);
}
}
@@ -0,0 +1,7 @@
package ru.otus.example.mongodbdemo.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import ru.otus.example.mongodbdemo.model.Teacher;
public interface TeacherRepository extends MongoRepository<Teacher, String>, TeacherRepositoryCustom {
}
@@ -0,0 +1,9 @@
package ru.otus.example.mongodbdemo.repositories;
import ru.otus.example.mongodbdemo.model.Knowledge;
import java.util.List;
public interface TeacherRepositoryCustom {
List<Knowledge> getTeacherExperienceById(String teacherId);
}
@@ -0,0 +1,28 @@
package ru.otus.example.mongodbdemo.repositories;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Teacher;
import java.util.List;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
@RequiredArgsConstructor
public class TeacherRepositoryCustomImpl implements TeacherRepositoryCustom {
private final MongoTemplate mongoTemplate;
@Override
public List<Knowledge> getTeacherExperienceById(String teacherId) {
val aggregation = newAggregation(
match(Criteria.where("id").is(teacherId))
, unwind("experience")
, project().andExclude("_id").and("experience.id").as("_id").and("experience.name").as("name")
);
return mongoTemplate.aggregate(aggregation, Teacher.class, Knowledge.class).getMappedResults();
}
}
@@ -0,0 +1,8 @@
package ru.otus.example.mongodbdemo.utils;
import org.bson.Document;
public interface RawResultPrinter {
@SuppressWarnings("unchecked")
void prettyPrintRawResult(Document document);
}
@@ -0,0 +1,18 @@
package ru.otus.example.mongodbdemo.utils;
import org.bson.Document;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class RawResultPrinterImpl implements RawResultPrinter {
@SuppressWarnings("unchecked")
@Override
public void prettyPrintRawResult(Document document) {
List<Document> results = (List<Document>) document.get("results");
String resultsAsString = results.stream().map(Document::toString).collect(Collectors.joining("\n"));
System.out.println(resultsAsString);
}
}
@@ -0,0 +1,9 @@
spring:
data:
mongodb:
uri: mongodb://localhost
port: 27017
database: awesomeMongo
logging:
level:
root: ERROR
@@ -0,0 +1,14 @@
package ru.otus.example.mongodbdemo.repositories;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import ru.otus.example.mongodbdemo.utils.RawResultPrinterImpl;
@DataMongoTest
@EnableConfigurationProperties
@ComponentScan({"ru.otus.example.mongodbdemo.config", "ru.otus.example.mongodbdemo.repositories"})
@Import(RawResultPrinterImpl.class)
public abstract class AbstractRepositoryTest {
}
@@ -0,0 +1,29 @@
package ru.otus.example.mongodbdemo.repositories;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ru.otus.example.mongodbdemo.model.Knowledge;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("TeacherRepository должен ")
class TeacherRepositoryTest extends AbstractRepositoryTest {
@Autowired
private TeacherRepository teacherRepository;
@DisplayName(" возвращать корректный список знаний преподавателя")
@Test
void shouldReturnCorrectKnowledgeList(){
val teachers = teacherRepository.findAll();
val teacher = teachers.get(0);
val experience = teacher.getExperience();
assertThat(experience).isNotNull().hasSize(3);
val actualExperience = teacherRepository.getTeacherExperienceById(teacher.getId());
assertThat(actualExperience).containsExactlyInAnyOrder(experience.toArray(new Knowledge[0]));
}
}
@@ -0,0 +1,58 @@
package ru.otus.example.mongodbdemo.repositories.plain;
import lombok.val;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.AbstractOptionalAssert;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
import java.util.List;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("KnowledgeRepository при отсутствии listener-ов в контексте ")
class KnowledgeRepositoryWithoutListenerTest extends AbstractRepositoryTest {
@Autowired
private KnowledgeRepository knowledgeRepository;
@Autowired
private StudentRepository studentRepository;
@DisplayName("при удалении Knowledge не должен удалять его из опыта студента")
@Test
void shouldLeaveKnowledgeInStudentExperienceWhenRemovingKnowledge() {
// Загрузка студента и его первого знания
val students = studentRepository.findAll();
val student = students.get(0);
val experience = student.getExperience();
val firstKnowledge = experience.get(0);
// Удаление знания из коллекции знаний
knowledgeRepository.delete(firstKnowledge);
// Загружаем студента заново и проверяем, что знание действительно удалено (размер стал меньше на 1)
val expectedExperienceArrayLength = experience.size() - 1;
val actualStudentOptional = studentRepository.findById(student.getId());
assertThat(actualStudentOptional)
.isNotEmpty().get()
.matches(s -> s.getExperience() != null && s.getExperience().size() == expectedExperienceArrayLength);
// Загружаем размер массива с помощью аггрегаций и проверяем, что на самом деле размер массива в БД не изменился
val actualExperienceArrayLength = studentRepository.getExperienceArrayLengthByStudentId(student.getId());
assertThat(actualExperienceArrayLength).isNotEqualTo(expectedExperienceArrayLength);
}
}
@@ -0,0 +1,27 @@
package ru.otus.example.mongodbdemo.repositories.plain;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.MappingException;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
import static org.assertj.core.api.Assertions.*;
@DisplayName("StudentRepository при отсутствии listener-ов в контексте ")
class StudentRepositoryWithoutListenerTest extends AbstractRepositoryTest {
@Autowired
private StudentRepository studentRepository;
@DisplayName("должен кидать MappingException во время сохранения студента с отсутствующими в БД знаниями")
@Test
void shouldThrowMappingExceptionWhenSaveStudentWithNewKnowledge(){
val student = new Student("Student #2", new Knowledge("Knowledge #3"));
assertThatThrownBy(() -> studentRepository.save(student)).isInstanceOf(MappingException.class);
}
}
@@ -0,0 +1,49 @@
package ru.otus.example.mongodbdemo.repositories.withlisteners;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
import ru.otus.example.mongodbdemo.repositories.KnowledgeRepository;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("KnowledgeRepository при наличии listener-ов в контексте ")
@ComponentScan("ru.otus.example.mongodbdemo.events")
class KnowledgeRepositoryWithListenerTest extends AbstractRepositoryTest {
@Autowired
private KnowledgeRepository knowledgeRepository;
@Autowired
private StudentRepository studentRepository;
@DisplayName("при удалении Knowledge должен удалить его из опыта студента")
@Test
void shouldRemoveKnowledgeFromStudentExperienceWhenRemovingKnowledge() {
// Загрузка студента и его первого знания
val students = studentRepository.findAll();
val student = students.get(0);
val experience = student.getExperience();
val firstKnowledge = experience.get(0);
// Удаление знания из коллекции знаний
knowledgeRepository.delete(firstKnowledge);
// Загружаем студента заново и проверяем, что знание действительно удалено (размер стал меньше на 1)
val expectedExperienceArrayLength = experience.size() - 1;
val actualStudentOptional = studentRepository.findById(student.getId());
assertThat(actualStudentOptional)
.isNotEmpty().get()
.matches(s -> s.getExperience() != null && s.getExperience().size() == expectedExperienceArrayLength);
// Загружаем размер массива с помощью аггрегаций и проверяем, что размер массива в БД тоже изменился
val actualExperienceArrayLength = studentRepository.getExperienceArrayLengthByStudentId(student.getId());
assertThat(actualExperienceArrayLength).isEqualTo(expectedExperienceArrayLength);
}
}
@@ -0,0 +1,41 @@
package ru.otus.example.mongodbdemo.repositories.withlisteners;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import ru.otus.example.mongodbdemo.model.Knowledge;
import ru.otus.example.mongodbdemo.model.Student;
import ru.otus.example.mongodbdemo.repositories.AbstractRepositoryTest;
import ru.otus.example.mongodbdemo.repositories.StudentRepository;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("StudentRepository при наличии listener-ов в контексте ")
@ComponentScan("ru.otus.example.mongodbdemo.events")
class StudentRepositoryWithListenersTest extends AbstractRepositoryTest {
@Autowired
private StudentRepository studentRepository;
@DisplayName("должен корректно сохранять студента с отсутствующими в БД знаниями")
@Test
void shouldCorrectSaveStudentWithNewKnowledge(){
val student = new Student("Student #2", new Knowledge("Knowledge #3"));
val actual = studentRepository.save(student);
assertThat(actual.getId()).isNotNull();
assertThat(actual.getName()).isEqualTo(student.getName());
}
@DisplayName("должен возвращать корректный список знаний студента")
@Test
void shouldReturnCorrectStudentKnowledgeList(){
val student = studentRepository.findAll().get(0);
List<Knowledge> knowledgeList = studentRepository.getStudentExperienceById(student.getId());
assertThat(knowledgeList).containsExactlyInAnyOrderElementsOf(student.getExperience());
}
}
@@ -0,0 +1,18 @@
spring:
data:
mongodb:
port: 0
database: test
#uri: mongodb://localhost
#port: 27017
#database: awesomeMongo
mongock:
change-logs-scan-package:
- ru.otus.example.mongodbdemo.changelogs
logging:
level:
root: ERROR
+2 -2
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
@@ -20,7 +20,7 @@
<java.version>11</java.version>
<maven.compiler.sourcre>11</maven.compiler.sourcre>
<maven.compiler.target>11</maven.compiler.target>
<mongock.version>2.0.2</mongock.version>
<mongock.version>3.2.8</mongock.version>
</properties>
<dependencies>