REST-JPA-Main

Spring Boot — пример с Postgres и JPA

В данной статье речь пойдет о фреймворке Spring, а точнее о Spring Boot. Я покажу, как быстро в считанные минуты запустить простое приложение и подключить к нему базу данных postgresql. Мы сделаем пару примеров на простые операции с базой данных.

Для того, чтобы не терять времени и говорить на одном языке я предполагаю, что читатель уже знаком с таким понятием как Spring, знает, что такое JPA, Hibernate и что такое Maven.  Если все это для Вас ново — предлагаю для ознакомления статьи в порядке важности: Spring MVC первое веб приложение — о спринг фреймворке и архитектурах веб приложения; Spring Boot простое приложение — о спринг бут кратко, но понятно; Что такое hibernate — о работе с базами данных.

Для создания приложения и подключения всех необходимых зависимостей воспользуемся инструментом SPRING INITIALIZR — это просто сайт, где Вы можете выбрать все необходимые зависимости для приложения и система выдаст готовое приложение доступное для скачивания.

Для начала нужно перейти на сайт https://start.spring.io/ ввести group и artifact своего приложения. Идентично тому, как мы вводим groupId и artifactId когда создаем Maven приложение. Далее в строке  нужно ввести зависимости, которые нужно добавить в приложение. После этого нажимаем большую кнопку Generate project и должна начаться загрузка архива с приложением:

spring initializr

Для тех, кому привычнее создавать приложения в обычной манере — можно не пользоваться этим инструментом. Главное создать простое Spring Boot приложение и подключить к нему зависимости для Postgresql, JPA, WEB. Скачанный архив нужно распаковать в удобную папку и открыть приложение с помощью любимой идее. На этот раз я буду пользоваться intellij idea. Как я неоднократно упоминал — выбор инструмента программирования не имеет значения. Главное, чтобы Вам было удобно.

И так, приложение создано и открыто.

структура приложения

На рисунке выше — структура моего приложения. В нем создан только один класс, который является точкой входа в приложение. Его код ни чем не примечательный и таковым и останется.

Код    
  1. package com.javamaster.springjpapostgres;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5.  
  6. @SpringBootApplication
  7. public class SpringjpapostgresApplication {
  8.  
  9.     public static void main(String[] args) {
  10.         SpringApplication.run(SpringjpapostgresApplication.class, args);
  11.     }
  12. }

Файл pom.xml:

Код    
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.    <modelVersion>4.0.0</modelVersion>
  5.  
  6.    <groupId>com.javamaster</groupId>
  7.    <artifactId>springjpapostgres</artifactId>
  8.    <version>0.0.1-SNAPSHOT</version>
  9.    <packaging>jar</packaging>
  10.  
  11.    <name>springjpapostgres</name>
  12.    <description>Demo project for Spring Boot</description>
  13.  
  14.    <parent>
  15.       <groupId>org.springframework.boot</groupId>
  16.       <artifactId>spring-boot-starter-parent</artifactId>
  17.       <version>2.0.3.RELEASE</version>
  18.       <relativePath/> <!-- lookup parent from repository -->
  19.    </parent>
  20.  
  21.    <properties>
  22.       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  23.       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  24.       <java.version>1.8</java.version>
  25.    </properties>
  26.  
  27.    <dependencies>
  28.       <dependency>
  29.          <groupId>org.springframework.boot</groupId>
  30.          <artifactId>spring-boot-starter-data-jpa</artifactId>
  31.       </dependency>
  32.       <dependency>
  33.          <groupId>org.springframework.boot</groupId>
  34.          <artifactId>spring-boot-starter-web</artifactId>
  35.       </dependency>
  36.  
  37.  
  38.       <dependency>
  39.          <groupId>org.postgresql</groupId>
  40.          <artifactId>postgresql</artifactId>
  41.          <version>9.4-1206-jdbc42</version>
  42.       </dependency>
  43.  
  44.       <dependency>
  45.          <groupId>org.springframework.boot</groupId>
  46.          <artifactId>spring-boot-starter-test</artifactId>
  47.          <scope>test</scope>
  48.       </dependency>
  49.    </dependencies>
  50.  
  51.    <build>
  52.       <plugins>
  53.          <plugin>
  54.             <groupId>org.springframework.boot</groupId>
  55.             <artifactId>spring-boot-maven-plugin</artifactId>
  56.          </plugin>
  57.       </plugins>
  58.    </build>
  59.  
  60.  
  61. </project>

Теперь пришло время настроить нашу Postgresql базу данных. Я создам простую базу данных, которую назову usersmanagement. В ней будет две таблицы: users и address. У нас будет очень таки стандартная предметная область: управление пользователями. Пользователи будут иметь некоторые атрибуты и адрес.

Код    
  1. create database usersmanagement;
  2.  
  3. CREATE TABLE public.address
  4. (
  5.     id SERIAL PRIMARY KEY NOT NULL,
  6.     city TEXT,
  7.     street TEXT,
  8.     home_number VARCHAR(5)
  9. );
  10. CREATE UNIQUE INDEX address_id_uindex ON public.address (id);
  11.  
  12. CREATE TABLE public.users
  13. (
  14.     id SERIAL NOT NULL,
  15.     name TEXT,
  16.     email VARCHAR(20),
  17.     address_id INT PRIMARY KEY,
  18.     CONSTRAINT users_address_id_fk FOREIGN KEY (address_id) REFERENCES address (id)
  19. );
  20. CREATE UNIQUE INDEX users_id_uindex ON public.users (id);

Теперь нужно создать в нашем приложении файл настроек доступа к базе данных. В Spring Boot это делается очень просто: нужно в папке проекта найти директорию resources. В ней должен быть файл application.properties. Если его нет — нужно создать:

путь к application.properties

Теперь, в данном файле нужно указать путь к базе, имя пользователя и пароль:

Код    
  1. spring.datasource.url=jdbc:postgresql://localhost:5432/usersmanagement
  2. spring.datasource.username=postgres
  3. spring.datasource.password=postgres

Далее пропишем классы сущностей базы данных. Для этого создадим пакет entity и поместим в него два класса: Address иUsers.

Код    
  1. package com.javamaster.springjpapostgres.entity;
  2.  
  3. import javax.persistence.*;
  4.  
  5. @Table(name = "address")
  6. public class Address {
  7.  
  8.     @Id
  9.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  10.     private Long id;
  11.  
  12.     @Column
  13.     private String city;
  14.  
  15.     @Column
  16.     private String street;
  17.  
  18.     @Column(name = "home_number")
  19.     private String homeNumber;
  20.  
  21. //getters and setters
  22. }
Код    
  1. package com.javamaster.springjpapostgres.entity;
  2.  
  3. import javax.persistence.*;
  4.  
  5. @Table(name = "users")
  6. public class Users {
  7.  
  8.     @Id
  9.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  10.     private Long id;
  11.  
  12.     @Column
  13.     private String name;
  14.  
  15.     @Column
  16.     private String email;
  17.  
  18.     @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  19.     @JoinColumn(name = "address_id")
  20.     private Address address;
  21.  
  22. }
  23. //getters and setters

Теперь пришло время воспользоваться Spring JPA и написать методы доступа к базе данных. Для того, чтобы воспользоваться нужно создать свой интерфейс и унаследовать его от готового интерфейса JpaRepository <T, ID> где T — это сущность для доступа к которой будут использоваться методы, а ID — тип первичного ключа. На примере станет яснее.

Создадим сперва пакет и дадим ему имя repository. В нем будем создавать наши интерфейсы для доступа к сущностям в базе данных. В него поместим интерфейс UsersRepository и AddressRepository. Далее проделаем над интерфейсами процедуру описанную выше и получаем код который ниже.

Код    
  1. package com.javamaster.springjpapostgres.repository;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Address;
  4. import org.springframework.data.jpa.repository.JpaRepository;
  5.  
  6. public interface AddressRepository extends JpaRepository<Address, Long> {
  7. }
Код    
  1. package com.javamaster.springjpapostgres.repository;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Users;
  4. import org.springframework.data.jpa.repository.JpaRepository;
  5.  
  6. public interface UsersRepository extends JpaRepository<Users, Long> {
  7. }

Когда мы унаследовались от JpaRepository нам стали доступны стандартные методы над сущностью: сохранение, удаление, получение всех, получение по айди. В этих интерфейсах мы можем написать свои методы доступа к сущности: например получить по определенному полю или полям. Здесь также есть возможность написать собственный запрос на SQL.

Сейчас на примере попытаюсь показать большинство возможностей.

Код    
  1. package com.javamaster.springjpapostgres.repository;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Users;
  4. import org.springframework.data.jpa.repository.JpaRepository;
  5. import org.springframework.data.jpa.repository.Query;
  6.  
  7. import java.util.List;
  8.  
  9. public interface UsersRepository extends JpaRepository<Users, Long> {
  10.  
  11.     List<Users> findAllByName(String name);//просто правильное название метода даст возможность
  12.     //избежать запросов на SQL
  13.  
  14.     @Query("select u from Users u where u.email like '%@gmail.com%'")//если этого мало можно написать
  15.     //собственный запрос на языке похожем на SQL
  16.     List<Users> findWhereEmailIsGmail();
  17.    
  18.     @Query(value = "select * from users where name like '%smith%'", nativeQuery = true)
  19.     //если и этого мало - можно написать запрос на чистом SQL и все это будет работать
  20.     List<Users> findWhereNameStartsFromSmith();
  21. }

Код и комментарии говорят сами за себя. Пример выше приведен только в качестве демонстрации широких возможностей Sping JPA. Давайте теперь попробуем воспользоваться нашими методами и напишем слой сервисов в нашем приложении.

Для этого создадим пакет serivce и поместим в него классы UsersService, AddressService.

Код    
  1. package com.javamaster.springjpapostgres.service;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Address;
  4. import com.javamaster.springjpapostgres.repository.AddressRepository;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7.  
  8. @Service
  9. public class AddressService {
  10.  
  11.     @Autowired
  12.     private final AddressRepository addressRepository;
  13.  
  14.     public AddressService(AddressRepository addressRepository){
  15.         this.addressRepository = addressRepository;
  16.     }
  17.  
  18.     public void createAddress(Address address){
  19.         addressRepository.save(address);
  20.     }
  21. //далее уже допишите сами)
  22. }

 

Код    
  1. package com.javamaster.springjpapostgres.service;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Users;
  4. import com.javamaster.springjpapostgres.repository.UsersRepository;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7.  
  8. import java.util.List;
  9.  
  10. @Service
  11. public class UserService {
  12.  
  13.     @Autowired
  14.     private final UsersRepository usersRepository;
  15.  
  16.     public UserService(UsersRepository usersRepository){
  17.         this.usersRepository = usersRepository;
  18.     }
  19.  
  20.     public void createUsers(Users users) {
  21.         usersRepository.save(users);
  22.     }
  23.  
  24.     public List<Users> findAll(){
  25.         return usersRepository.findAll();
  26.     }
  27.  
  28.     public Users findById(Long userId){
  29.         return usersRepository.findById(userId).orElse(null);
  30.     }
  31.  
  32.     public List<Users> findAllByName(String name){
  33.         return usersRepository.findAllByName(name);
  34.     }
  35.  
  36.     public List<Users> findWhereEmailIsGmail(){
  37.         return usersRepository.findWhereEmailIsGmail();
  38.     }
  39.  
  40.     public List<Users> findWhereNameStartsFromSmith(){
  41.         return usersRepository.findWhereNameStartsFromSmith();
  42.     }
  43. }

Как уже было сказано: стандартные методы работы над сущностью доступны из под коробки. Разработчику нужно только достать их и использовать в своем сервисе.

Для тестирования наших методов работы с базой данных я написал простой метод, который вызывается при запуске Spring Boot приложения. Добавил я его в свой главный класс запуска приложения. Вот как теперь выглядит класс SpringjpapostgresApplication:

Код    
  1. package com.javamaster.springjpapostgres;
  2.  
  3. import com.javamaster.springjpapostgres.entity.Address;
  4. import com.javamaster.springjpapostgres.entity.Users;
  5. import com.javamaster.springjpapostgres.service.AddressService;
  6. import com.javamaster.springjpapostgres.service.UserService;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.SpringApplication;
  9. import org.springframework.boot.autoconfigure.SpringBootApplication;
  10. import org.springframework.boot.context.event.ApplicationReadyEvent;
  11. import org.springframework.context.event.EventListener;
  12.  
  13. @SpringBootApplication
  14. public class SpringjpapostgresApplication {
  15.  
  16.    @Autowired
  17.    private UserService userService;
  18.  
  19.    public static void main(String[] args) {
  20.       SpringApplication.run(SpringjpapostgresApplication.class, args);
  21.    }
  22.  
  23.    @EventListener(ApplicationReadyEvent.class)
  24.    private void testJpaMethods(){
  25.       Address address = new Address();
  26.       address.setCity("Kiev");
  27.       address.setHomeNumber("4");
  28.       address.setStreet("Main Street");
  29.       Address address1 = new Address();
  30.       address1.setCity("Lviv");
  31.       Users users = new Users();
  32.       users.setAddress(address);
  33.       users.setEmail("someEmail@gmail.com");
  34.       users.setName("Smith");
  35.       userService.createUsers(users);
  36.       Users users1 = new Users();
  37.       users1.setName("Jon Dorian");
  38.       users1.setEmail("gmailEmail@gmail.com");
  39.       users1.setAddress(address1);
  40.       userService.createUsers(users1);
  41.  
  42.       userService.findAll().forEach(it-> System.out.println(it));
  43.  
  44.       userService.findAllByName("Smith").forEach(it-> System.out.println(it));
  45.  
  46.       userService.findWhereEmailIsGmail().forEach(it-> System.out.println(it));
  47.  
  48.       userService.findWhereNameStartsFromSmith().forEach(it-> System.out.println(it));
  49.    }
  50. }

Результат вывода в консоль:

Users{id=5, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=8, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=6, name=’Jon Dorian’, email=’gmailEmail@gmail.com’, address=Address{id=9, city=’Lviv’, street=’null’, homeNumber=’null’}}
Users{id=7, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=10, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=8, name=’Jon Dorian’, email=’gmailEmail@gmail.com’, address=Address{id=11, city=’Lviv’, street=’null’, homeNumber=’null’}}
Users{id=5, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=8, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=7, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=10, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=5, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=8, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=6, name=’Jon Dorian’, email=’gmailEmail@gmail.com’, address=Address{id=9, city=’Lviv’, street=’null’, homeNumber=’null’}}
Users{id=7, name=’Smith’, email=’someEmail@gmail.com’, address=Address{id=10, city=’Kiev’, street=’Main Street’, homeNumber=’4′}}
Users{id=8, name=’Jon Dorian’, email=’gmailEmail@gmail.com’, address=Address{id=11, city=’Lviv’, street=’null’, homeNumber=’null’}}

Если Вы следовали вышеописанным пунктам, то должны при запуске получить ошибку:

org.hibernate.LazyInitializationException: could not initialize proxy [com.javamaster.springjpapostgres.entity.Address#8] — no Session. Это очень распространенная ошибка особенно среди новичков. Так как мы в наших сущностях указали FetchType.LAZY это будет означать, что Адрес не будет автоматически доставаться из базы при выборе Пользователя. С точки зрения производительности — это очень хорошая практика. Старайтесь избегать FetchType.EAGER в реальный проектах, если хотите сократить количество запросов в базу данных. Для того, чтобы избежать вышеописанной ошибки нужно над методами сервисов добавить аннотацию @Transactional. Это должно решить проблему. Мне было лень добавлять эту аннотацию, поэтому я изменил FetchType.LAZY на FetchType.EAGER в сущность Users.

Код    
  1. package com.javamaster.springjpapostgres.entity;
  2.  
  3. import javax.persistence.*;
  4.  
  5. @Table(name = "users")
  6. public class Users {
  7.  
  8.     @Id
  9.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  10.     private Long id;
  11.  
  12.     @Column
  13.     private String name;
  14.  
  15.     @Column
  16.     private String email;
  17.  
  18.     @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
  19.     @JoinColumn(name = "address_id")
  20.     private Address address;
  21.  
  22.     public Long getId() {
  23.         return id;
  24.     }
  25.  
  26.     public void setId(Long id) {
  27.         this.id = id;
  28.     }
  29.  
  30.     public String getName() {
  31.         return name;
  32.     }
  33.  
  34.     public void setName(String name) {
  35.         this.name = name;
  36.     }
  37.  
  38.     public String getEmail() {
  39.         return email;
  40.     }
  41.  
  42.     public void setEmail(String email) {
  43.         this.email = email;
  44.     }
  45.  
  46.     public Address getAddress() {
  47.         return address;
  48.     }
  49.  
  50.     public void setAddress(Address address) {
  51.         this.address = address;
  52.     }
  53.  
  54.     @Override
  55.     public String toString() {
  56.         return "Users{" +
  57.                 "id=" + id +
  58.                 ", name='" + name + '\'' +
  59.                 ", email='" + email + '\'' +
  60.                 ", address=" + address +
  61.                 '}';
  62.     }
  63. }

Теперь все должно работать.

Я не зря выбрал для данного примера модули для веб приложения. В следующий раз постараюсь показать REST API, а сервисы послужат нам хорошим примером.

Теперь Вы знаете основы Spring JPA и сможете начать пользоваться этим замечательным инструментом, который значительно экономит время программиста для написания слоя доступа к базе данных.

Пример кода находится по ссылке: https://github.com/caligula95/springjpapostgres

One thought on “Spring Boot — пример с Postgres и JPA

Добавить комментарий