В данной статье речь пойдет о фреймворке 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 Boot приложение и подключить к нему зависимости для Postgresql, JPA, WEB. Скачанный архив нужно распаковать в удобную папку и открыть приложение с помощью любимой идее. На этот раз я буду пользоваться intellij idea. Как я неоднократно упоминал – выбор инструмента программирования не имеет значения. Главное, чтобы Вам было удобно.
И так, приложение создано и открыто.
На рисунке выше – структура моего приложения. В нем создан только один класс, который является точкой входа в приложение. Его код ни чем не примечательный и таковым и останется.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringjpapostgresApplication {
public static void main(String[] args) {
SpringApplication.run(SpringjpapostgresApplication.class, args);
}
}
Файл pom.xml:
<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>
<groupId>com.javamaster</groupId>
<artifactId>springjpapostgres</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springjpapostgres</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1206-jdbc42</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Теперь пришло время настроить нашу Postgresql базу данных. Я создам простую базу данных, которую назову usersmanagement. В ней будет две таблицы: users и address. У нас будет очень таки стандартная предметная область: управление пользователями. Пользователи будут иметь некоторые атрибуты и адрес.
CREATE TABLE public.address
(
id SERIAL PRIMARY KEY NOT NULL,
city TEXT,
street TEXT,
home_number VARCHAR(5)
);
CREATE UNIQUE INDEX address_id_uindex ON public.address (id);
CREATE TABLE public.users
(
id SERIAL NOT NULL,
name TEXT,
email VARCHAR(20),
address_id INT PRIMARY KEY,
CONSTRAINT users_address_id_fk FOREIGN KEY (address_id) REFERENCES address (id)
);
CREATE UNIQUE INDEX users_id_uindex ON public.users (id);
Теперь нужно создать в нашем приложении файл настроек доступа к базе данных. В Spring Boot это делается очень просто: нужно в папке проекта найти директорию resources. В ней должен быть файл application.properties. Если его нет – нужно создать:
Теперь, в данном файле нужно указать путь к базе, имя пользователя и пароль:
spring.datasource.username=postgres
spring.datasource.password=postgres
Далее пропишем классы сущностей базы данных. Для этого создадим пакет entity и поместим в него два класса: Address иUsers.
import javax.persistence.*;
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String city;
@Column
private String street;
@Column(name = "home_number")
private String homeNumber;
//getters and setters
}
import javax.persistence.*;
@Entity
@Table(name = "users")
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private String email;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "address_id")
private Address address;
}
//getters and setters
Теперь пришло время воспользоваться Spring JPA и написать методы доступа к базе данных. Для того, чтобы воспользоваться нужно создать свой интерфейс и унаследовать его от готового интерфейса JpaRepository <T, ID> где T – это сущность для доступа к которой будут использоваться методы, а ID – тип первичного ключа. На примере станет яснее.
Создадим сперва пакет и дадим ему имя repository. В нем будем создавать наши интерфейсы для доступа к сущностям в базе данных. В него поместим интерфейс UsersRepository и AddressRepository. Далее проделаем над интерфейсами процедуру описанную выше и получаем код который ниже.
import com.javamaster.springjpapostgres.entity.Address;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AddressRepository extends JpaRepository<Address, Long> {
}
import com.javamaster.springjpapostgres.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UsersRepository extends JpaRepository<Users, Long> {
}
Когда мы унаследовались от JpaRepository нам стали доступны стандартные методы над сущностью: сохранение, удаление, получение всех, получение по айди. В этих интерфейсах мы можем написать свои методы доступа к сущности: например получить по определенному полю или полям. Здесь также есть возможность написать собственный запрос на SQL.
Сейчас на примере попытаюсь показать большинство возможностей.
import com.javamaster.springjpapostgres.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface UsersRepository extends JpaRepository<Users, Long> {
List<Users> findAllByName(String name);//просто правильное название метода даст возможность
//избежать запросов на SQL
@Query("select u from Users u where u.email like '%@gmail.com%'")//если этого мало можно написать
//собственный запрос на языке похожем на SQL
List<Users> findWhereEmailIsGmail();
@Query(value = "select * from users where name like '%smith%'", nativeQuery = true)
//если и этого мало - можно написать запрос на чистом SQL и все это будет работать
List<Users> findWhereNameStartsFromSmith();
}
Код и комментарии говорят сами за себя. Пример выше приведен только в качестве демонстрации широких возможностей Sping JPA. Давайте теперь попробуем воспользоваться нашими методами и напишем слой сервисов в нашем приложении.
Для этого создадим пакет serivce и поместим в него классы UsersService, AddressService.
import com.javamaster.springjpapostgres.entity.Address;
import com.javamaster.springjpapostgres.repository.AddressRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AddressService {
@Autowired
private final AddressRepository addressRepository;
public AddressService(AddressRepository addressRepository){
this.addressRepository = addressRepository;
}
public void createAddress(Address address){
addressRepository.save(address);
}
//далее уже допишите сами)
}
import com.javamaster.springjpapostgres.entity.Users;
import com.javamaster.springjpapostgres.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private final UsersRepository usersRepository;
public UserService(UsersRepository usersRepository){
this.usersRepository = usersRepository;
}
public void createUsers(Users users) {
usersRepository.save(users);
}
public List<Users> findAll(){
return usersRepository.findAll();
}
public Users findById(Long userId){
return usersRepository.findById(userId).orElse(null);
}
public List<Users> findAllByName(String name){
return usersRepository.findAllByName(name);
}
public List<Users> findWhereEmailIsGmail(){
return usersRepository.findWhereEmailIsGmail();
}
public List<Users> findWhereNameStartsFromSmith(){
return usersRepository.findWhereNameStartsFromSmith();
}
}
Как уже было сказано: стандартные методы работы над сущностью доступны из под коробки. Разработчику нужно только достать их и использовать в своем сервисе.
Для тестирования наших методов работы с базой данных я написал простой метод, который вызывается при запуске Spring Boot приложения. Добавил я его в свой главный класс запуска приложения. Вот как теперь выглядит класс SpringjpapostgresApplication:
import com.javamaster.springjpapostgres.entity.Address;
import com.javamaster.springjpapostgres.entity.Users;
import com.javamaster.springjpapostgres.service.AddressService;
import com.javamaster.springjpapostgres.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
@SpringBootApplication
public class SpringjpapostgresApplication {
@Autowired
private UserService userService;
public static void main(String[] args) {
SpringApplication.run(SpringjpapostgresApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
private void testJpaMethods(){
Address address = new Address();
address.setCity("Kiev");
address.setHomeNumber("4");
address.setStreet("Main Street");
Address address1 = new Address();
address1.setCity("Lviv");
Users users = new Users();
users.setAddress(address);
users.setEmail("someEmail@gmail.com");
users.setName("Smith");
userService.createUsers(users);
Users users1 = new Users();
users1.setName("Jon Dorian");
users1.setEmail("gmailEmail@gmail.com");
users1.setAddress(address1);
userService.createUsers(users1);
userService.findAll().forEach(it-> System.out.println(it));
userService.findAllByName("Smith").forEach(it-> System.out.println(it));
userService.findWhereEmailIsGmail().forEach(it-> System.out.println(it));
userService.findWhereNameStartsFromSmith().forEach(it-> System.out.println(it));
}
}
Результат вывода в консоль:
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.
import javax.persistence.*;
@Entity
@Table(name = "users")
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private String email;
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "address_id")
private Address address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", address=" + address +
'}';
}
}
Теперь все должно работать.
Я не зря выбрал для данного примера модули для веб приложения. В следующий раз постараюсь показать REST API, а сервисы послужат нам хорошим примером.
Теперь Вы знаете основы Spring JPA и сможете начать пользоваться этим замечательным инструментом, который значительно экономит время программиста для написания слоя доступа к базе данных.
Пример кода находится по ссылке: https://github.com/caligula95/springjpapostgres
Ссылка на менторство по java. Подразумевает создание спринг веб приложения с нуля.
спасибо! познавательно
Подскажите пжл как передать в аннотацию параметр, не совсем понимаю как мне использовать оператор IN
авыаыв
ппрар
@Query(“select u from Users u where u.email like :=email”)//если этого мало можно написать
//собственный запрос на языке похожем на SQL
List findWhereEmailIsGmail(@Param String email);
Спасибо огромное! Очень полезно
Редко оставляю отзывы, но эта статья меня выручила!
Здравствуйте! Спасибо за отзыв)