Это короткий непрерывный проект о том, как создать простой сервер REST API при загрузке Spring.
Для начала краткий обзор ООП, поскольку эти концепции будут иметь жизненно важное значение в этом небольшом проекте и упражнении.
Резюме ООП
Если вам нужно подвести итоги, ознакомьтесь с этим обзором наиболее распространенных концепций ООП https://www.educative.io/edpresso/what-is-objectorhibited-programming
Проект
Обобщив все эти полезные концепции, пора применить их на практике.
Я проведу вас через создание простого REST API, который предоставляет ресурсы через Интернет, а затем дам вам упражнение, которое потребует от вас использования тех же концепций.
Создайте новый проект весенней загрузки на основе Gradle и добавьте следующие зависимости в файл build.gradle:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' runtimeOnly 'com.h2database:h2' // Depending on which database you prefer runtimeOnly 'org.postgresql:postgresql'
Создание моделей
Создайте пакет для моделей, в который будут помещены все классы моделей. У меня есть модельный университет, у которого есть атрибуты: имя, год основания и местонахождение. Вам необходимо создать геттеры и сеттеры для атрибутов. Это будет выглядеть так:
Следующим шагом является создание модели для студентов с именем, фамилией и датой рождения в качестве атрибутов.
Отношения
Теперь у нас есть два класса, которые не знают друг о друге. Нам нужно объявить отношения между Студентом и Университетом. Студент может быть только в одном университете, но в университете много студентов. Следовательно, это отношения «один ко многим».
На модели университета есть список студентов, связанных с университетом, как показано ниже.
private List<Student> students; public List<Student> getStudents() { return students; } public void setStudents(List<Student> students) { this.students = students; }
В модели «Студент» есть объект «университет», чтобы указать, куда зачислен конкретный студент. Это должно выглядеть следующим образом.
private University university; public Student(String firstName, String lastName, University university) { this.firstName = firstName; this.lastName = lastName; this.university = university; } public University getUniversity() { return university; } public void setUniversity(University university) { this.university = university; }
Сохранение данных
Чтобы сохранить данные, вы аннотируете объявление класса как объект, а также имеете аннотацию table, в которой вы указываете имя таблицы в базе данных.
@Entity @Table(name = "students") public class Student { // ..... the rest of the content }
Каждая сущность должна иметь первичный ключ, для этого аннотируйте атрибут id следующим образом:
@Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id;
Что касается других атрибутов, аннотируйте только аннотацию столбца и укажите имя столбца (используя snake_case). Например:
@Column(name = "first_name") private String firstName;
В модели университета аннотируйте атрибут студента с помощью OneToMany следующим образом:
@OneToMany(mappedBy = "university") private List<Student> students;
Вы должны отобразить отношения в обоих направлениях, чтобы вы могли получить доступ к отношениям из обеих моделей. В модели Student будет столбец university_id, указывающий на университет, в который зачислен студент. Это будет выглядеть следующим образом:
@ManyToOne @JsonIgnore @JoinColumn(name = "university_id") private University university;
Обратите внимание, что здесь есть аннотация JsonIgnore, чтобы избежать бесконечного объекта JSON, поскольку студент вызовет объект University, с которым он связан, а объект университета вызовет все студенческие объекты, с которыми он ассоциируется… .. это продолжается и продолжается.
База данных
Скорее всего, у вас возникла ошибка не удается разрешить в именах таблиц и столбцов. Чтобы избавиться от этого, добавьте в проект источник данных. Если вы не знакомы с этим, ознакомьтесь с этим руководством.
После добавления базы данных добавьте свои учетные данные БД в файл application.properties, который вы можете найти в папке src / main / resources. Это будет выглядеть примерно так:
server.port=8080 spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:postgresql://localhost:5432/rest spring.datasource.username=usernamedropdrop spring.datasource.password=password
Репозитории
Теперь у нас есть таблицы со столбцами, но как они подключаются к базе данных для выполнения функций CRUD? Вот тут-то и пригодятся репозитории.
Создайте репозиторий интерфейса для каждой из сущностей, расширяющих JpaRepository, как показано ниже:
public interface UniversityRepository extends JpaRepository<University, Long> { }
Не забудьте поместить репозитории в их пакет.
Услуги
Далее следует создать служебные интерфейсы, которые определяют методы, которые мы будем использовать для функций CRUD. Служба университета будет выглядеть следующим образом:
public interface UniversityService { List<University> findAll(); University findById(Long id); void delete(Long id); University createUniversity(University university); University update(Long id, University university); }
Создайте аналогичный интерфейс для модели Student.
Теперь, чтобы подключить наши функции CRUD к базе данных, мы реализуем интерфейс службы и внедряем репозиторий, чтобы использовать его унаследованные функции. Не забудьте аннотировать класс реализации сервиса аннотацией сервиса. UniversityServiceImpl будет выглядеть следующим образом:
Обратите внимание на вызов класса NotFoundException. Создайте класс либо в собственном пакете исключений, либо в корневом пакете. Это будет выглядеть так:
Реализуйте то же самое для интерфейса StudentService.
Контроллеры
Создайте пакет для контроллеров и создайте класс UniversityController. Здесь мы будем открывать конечные точки.
В контроллере аннотируйте объявление класса как RestController, а также добавьте аннотацию RequestMapping со значением как университеты. Это сообщает Spring, что этот класс будет ожидать запросов по сети и будет обрабатывать запросы, направленные на yourbasedomain / University, в данном случае « http: // localhost: 8080 / университеты », если ваше приложение настроено для работы через порт 8080.
Чтобы использовать функциональные возможности, реализованные в классе реализации службы, введите класс UniversityService и потребуйте его в конструкторе.
Теперь UniversityController выглядит следующим образом:
@RestController @RequestMapping(value = "universities") public class UniversityController { private final UniversityService universityService; public UniversityController(UniversityService universityService) { this.universityService = universityService; } }
Различные типы запросов структурированы следующим образом:
ПОЛУЧИТЬ
@GetMapping public List<University> findAll(){ return universityService.findAll(); }
POST
@PostMapping public University createUniversity(@RequestBody University university) { return universityService.createUniversity(university); }
ПАТЧ
Обратите внимание, что этот URL-адрес запроса будет похож на http: // localhost: 8080 / University / {id} с методом PATCH
@PatchMapping(value = "{id}") public University updateUniversity(@PathVariable Long id, @RequestBody University university) { return universityService.update(id, university); }
УДАЛИТЬ
@DeleteMapping(value = "{id}") public void deleteUniversity(@PathVariable Long id) { universityService.delete(id); }
Сделайте то же самое для StudentController.
Проверка
Возможно, вы заметили, что мы не проводили тщательной проверки поступающих запросов. Важно проверять поступающие полезные данные, чтобы вы могли корректно обрабатывать вероятные ошибки, а также не создавать единообразных записей.
Мы реализуем валидацию из модели и будем использовать ее при получении запроса в контроллере.
В модели создайте интерфейс с именем Create, как показано ниже:
@Entity @Table(name = "students") public class Student { // ..... the rest of the content public interface Create{} }
Мы будем использовать интерфейс создания, чтобы проверить наличие необходимых атрибутов при создании. Для этого добавьте аннотацию NotNull перед атрибутами следующим образом:
public class University { // ... the rest of the content @NotNull(groups = Create.class) @Column(name = "name") private String name; @NotNull(groups = Create.class) @Column(name = "location") private String location; // ... the rest of the content }
В контроллере в методе createUniversity добавьте аннотацию Validated и вызовите интерфейс Create из класса University, как показано ниже:
@PostMapping public University createUniversity(@Validated(University.Create.class) @RequestBody University university) { return universityService.createUniversity(university); }
Сделайте то же самое для модели ученика. Добавляйте аннотацию NotNull только к атрибутам, которые должны быть там.
Имеет смысл создать студента в рамках конкретного университета, в который они поступают. Чтобы обеспечить это, добавьте метод createStudent в UniversityService и не забудьте реализовать его и добавить конечную точку POST «/ University / {id} / student »
UniversityServiceImpl выглядит следующим образом:
private final StudentService studentService; public UniversityServiceImpl(UniversityRepository universityRepository, StudentService studentService ) { this.universityRepository = universityRepository; this.studentService = studentService; } // ...the rest of the content @Override public Student createStudent(Long universityId, Student student) { University university = findById(universityId); student.setUniversity(university); return studentService.createStudent(student); }
Обратите внимание, что мы передаем студенческий сервис вместо репозитория.
Конечная точка на UniversityController будет выглядеть следующим образом:
@PostMapping(value = "{id}/students") public Student createStudent(@PathVariable Long id, @Validated(Student.Create.class) @RequestBody Student student){ return universityService.createStudent(id, student); }
Тестирование
Вы можете протестировать открытые конечные точки с помощью Почтальона. Убедитесь, что вы указали все параметры.
Обратите внимание, что если вы отправляете запрос без обязательных атрибутов, он возвращает ошибку 400 BAD REQUEST с сообщением о том, что конкретный атрибут не может быть нулевым.
Упражнение
Вы можете разветвить репо из здесь, а затем продолжить выполнение упражнения, описанного ниже.
Представьте еще одну сущность, Курс. Студент может записаться на несколько курсов, и на курсе может быть много студентов.
Предоставьте конечные точки:
- Разрешить студентам просматривать доступные курсы.
- Зарегистрируйтесь на курс, используя идентификатор курса.
Убедитесь, что вы обслуживаете:
- Проверка при создании записи
- Отношения "многие ко многим" между студентом и курсом
Подсказка: для отношений "многие ко многим" требуется сводная таблица, также известная как таблица соединений, в которой вам нужно объявить столбец соединения и столбец обратного соединения, то есть course_id и student_id соответственно.
@ManyToMany @JoinTable(name= “student_courses”, joinColumns = @JoinColumn(name = “course_id”), inverseJoinColumns = @JoinColumn(name=”student_id”) ) @JsonIgnore private Set<Student> students = new HashSet<>();