JavaRush /Java блогы /Random-KK /Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқ...
Artur
Деңгей
Tallinn

Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу. 2-бөлім

Топта жарияланған
Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу. 1-бөлім Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу.  2 - 1 бөлім Сонымен, соңғы бөлімде біз PostgresSQL дерекқорын компьютерге орнатуды, pgAdmin-де дерекқорды құруды, сондай-ақ ондағы кестелерді қолмен және бағдарламалық түрде құруды және жоюды үйрендік. Бұл бөлімде біз бағдарламамызды осы мәліметтер базасымен және кестелермен жұмыс істеуді үйренетіндей етіп қайта жазамыз. Неге біз? Өйткені мен өзім сіздермен осы материалдан үйреніп жатырмын. Содан кейін біз тапсырманы шешіп қана қоймай, сонымен қатар тәжірибелі бағдарламашылардың кеңесі арқылы жолда пайда болатын қателерді түзетеміз. Былайша айтқанда, біз командада жұмыс істеуді үйренеміз ;) Алдымен com.javarush.lectures.rest_exampleпапкаға жаңа пакет жасап, оны шақырайық repository. Бұл пакетте біз жаңа интерфейс жасаймыз ClientRepository:
package com.javarush.lectures.rest_example.repository;

import com.javarush.lectures.rest_example.model.Client;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ClientRepository extends JpaRepository<Client, Integer>  {
}
Бұл интерфейс біздің дерекқорлармен және кестелермен «сиқырлы» әрекеттеседі. Неліктен сиқырлы? Өйткені бізге оның орындалуын жазудың қажеті жоқ, ал көктемгі шеңбер оны бізге береді. Сізге мұндай интерфейсті жасау керек, және сіз бұл «сиқырды» пайдалана аласыз. Келесі қадам сыныпты Clientкелесідей өңдеу болып табылады:
package com.javarush.lectures.rest_example.model;

import javax.persistence.*;

@Entity
@Table(name = "clients")
public class Client {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name")
    private String name;

    @Column(name = "email")
    private String email;

    @Column(name = "phone")
    private String phone;

    //… getters and setters
}
Бұл сыныпта біз тек кейбір annotationларды қосу ғана болды. Оларды қарастырайық:
  • @Entity - бұл бұршақтың (сыныптың) нысан екенін көрсетеді.
  • @Table - осы нысанда көрсетілетін кестенің атын көрсетеді.
  • @Id - баған идентификаторы (негізгі кілт - ағымдағы кестедегі деректердің бірегейлігін қамтамасыз ету үшін пайдаланылатын мән. Ескерту: Андрей )
  • @Column - нысан сипатымен салыстырылатын бағанның атын көрсетеді.
  • @GeneratedValue - бұл сипат көрсетілген стратегияға сәйкес жасалатынын көрсетеді.
Кесте өрістерінің атаулары сыныптағы айнымалылар атауларымен сәйкес келмеуі керек. Мысалы, егер бізде айнымалы болса firstName, онда кестедегі өрісті атаймыз first_name. Бұл annotationларды тікелей өрістерге де, олардың қабылдаушыларына да орнатуға болады. Бірақ егер сіз осы әдістердің бірін таңдасаңыз, онда осы стильді бүкіл бағдарламаңызда сақтауға тырысыңыз. Мен тізімдерді қысқарту үшін бірінші әдісті қолдандым. Дерекқорлармен жұмыс істеуге арналған annotationлардың толық тізімін мына жерден табуға болады . Енді сыныпқа барайық ClientServiceImplжәне оны келесідей қайта жазайық:
package com.javarush.lectures.rest_example.service;

import com.javarush.lectures.rest_example.model.Client;
import com.javarush.lectures.rest_example.repository.ClientRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ClientServiceImpl implements ClientService {

    @Autowired
    private ClientRepository clientRepository;

    @Override
    public void create(Client client) {
        clientRepository.save(client);
    }

    @Override
    public List<Client>  readAll() {
        return clientRepository.findAll();
    }

    @Override
    public Client read(int id) {
        return clientRepository.getOne(id);
    }

    @Override
    public boolean update(Client client, int id) {
        if (clientRepository.existsById(id)) {
            client.setId(id);
            clientRepository.save(client);
            return true;
        }

        return false;
    }

    @Override
    public boolean delete(int id) {
        if (clientRepository.existsById(id)) {
            clientRepository.deleteById(id);
            return true;
        }
        return false;
    }
}
Тізімнен көріп отырғаныңыздай, біз тек қажет емес жолдарды жойдық:
// Хранorще клиентов
private static final Map<Integer, Client>  CLIENT_REPOSITORY_MAP = new HashMap<>();

// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
Біз оның орнына интерфейсімізді жарияладық ClientRepository, сонымен қатар Spring бұл тәуелділікті классымызға автоматты түрде қосатындай етіп оның үстіне @Autowired annotationсын орналастырдық. Біз сондай-ақ барлық жұмысты осы интерфейске, дәлірек айтсақ, көктем қосатын оны іске асыруға тапсырдық. Соңғы және ең қызықты кезеңге көшейік - біздің қосымшамызды тестілеу. Пошташы бағдарламасын ашайық (оны пайдалану жолын осы жерден қараңыз ) және мына мекенжайға GET сұрауын жіберейік: http://localhost:8080/clients. Біз бұл жауапты аламыз:
[
    {
        "id": 1,
        "name": "Vassily Petrov",
        "email": "vpetrov@jr.com",
        "phone": "+7 (191) 322-22-33)"
    },
    {
        "id": 2,
        "name": "Pjotr Vasechkin",
        "email": "pvasechkin@jr.com",
        "phone": "+7 (191) 223-33-22)"
    }
]
Біз POST сұрауын жібереміз:
{
  "name" : "Amigo",
  "email" : "amigo@jr.com",
  "phone" : "+7 (191) 746-43-23"
}
Және... біз бағдарламадағы бірінші қатені байқадық:
{
    "timestamp": "2020-03-06T13:21:12.180+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
    "path": "/clients"
}
Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу.  2 - 2 бөлім Біз журналдарды қарап, келесі қатені табамыз:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
Біз сол POST сұрауын қайтадан жібереміз, нәтиже бірдей, бірақ бұл айырмашылықпен: Ключ "(id)=(2)" уже существует. Біз бір сұрауды үшінші рет жібереміз және біз Күй: 201 Жасалған. Біз қайтадан GET сұрауын жібереміз және жауап аламыз:
[
    {
        "id": 1,
        "name": "Vassily Petrov",
        "email": "vpetrov@jr.com",
        "phone": "+7 (191) 322-22-33)"
    },
    {
        "id": 2,
        "name": "Pjotr Vasechkin",
        "email": "pvasechkin@jr.com",
        "phone": "+7 (191) 223-33-22)"
    },
    {
        "id": 3,
        "name": "Amigo",
        "email": "amigo@jr.com",
        "phone": "+7 (191) 746-43-23"
    }
]
Бұл біздің бағдарламаның бұл кестенің алдын ала толтырылғанын елемейтінін және біреуден бастап қайтадан идентификаторды тағайындайтынын көрсетеді. Қате - бұл жұмыс сәті, үмітіңізді үзбеңіз, бұл жиі болады. Сондықтан мен тәжірибелі әріптестерге көмек сұраймын: «Құрметті әріптестер, бағдарлама қалыпты жұмыс істеуі үшін мұны қалай түзетуге болатынын түсініктемелерде кеңес беріңіз». Көмек көп ұзамай келді, ал Стас Пасинков түсініктемелерде маған қай бағытты іздеу керектігін айтты. Бұл үшін оған ерекше рахмет! Бірақ сабақта мен өріске Clientannotationның стратегиясын қате көрсеттім . Бұл стратегия MySQL үшін қолайлы. Егер біз Oracle немесе PostrgeSQL-мен жұмыс істейтін болсақ, онда басқа стратегияны орнату керек. Бастапқы кілттерге арналған стратегиялар туралы қосымша ақпаратты осы жерден оқи аласыз . Мен GenerationType.SEQUENCE стратегиясын таңдадым. Оны жүзеге асыру үшін бізге initDB.sql файлын және, әрине, Client класының id өрісінің annotationларын сәл қайта жазу керек болады. initDB.sql файлын қайта жазыңыз: @GeneratedValue(strategy = GenerationType.IDENTITY)idSpring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу.  2 - 3 бөлім
CREATE TABLE IF NOT EXISTS clients
(
    id    INTEGER PRIMARY KEY ,
    name  VARCHAR(200) NOT NULL ,
    email VARCHAR(254) NOT NULL ,
    phone VARCHAR(50)  NOT NULL
);
CREATE SEQUENCE clients_id_seq START WITH 3 INCREMENT BY 1;
Не өзгерді: кестенің id бағанының түрі өзгерді, бірақ бұл туралы кейінірек. Төменде біз clients_id_seq жаңа тізбегін жасайтын жолды қостық, оның үштен басталуы керек екенін көрсетеді (өйткені populateDB.sql файлындағы соңғы идентификатор 2) және өсудің бір рет болуы керектігін көрсетеді. Идентификатор бағанының түріне қайта оралайық. Мұнда біз INTEGER көрсеттік, өйткені егер біз SERIAL қалдырсақ, жүйелілік автоматты түрде, clients_id_seq деген атпен жасалады, бірақ біреуден басталады (бұл бағдарлама қатесіне әкелді). Дегенмен, енді кестені жойғыңыз келсе, келесі пәрмендерді пайдаланып pgAdmin интерфейсі арқылы немесе .sql файлы арқылы бұл ретті қосымша жоюыңыз қажет:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Бірақ кестені бастапқы толтыру үшін populateDB.sql сияқты файлды пайдаланбасаңыз, онда сіз бастапқы кілт үшін SERIAL немесе BIGSERIAL түрлерін пайдалана аласыз және ретті қолмен жасаудың қажеті жоқ, сондықтан жою қажет емес. ол бөлек. Тізбектер туралы толығырақ веб-сайттан оқи аласыз. PostgreSQL құжаттамасы . idКласс өрісінің annotationларына өтіп Client, оларды келесідей пішімдейік:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
Біз не істедік: реттілік генераторын жасау үшін жаңа annotationны орнаттық @SequenceGenerator, оған атау бердік clientsIdSeq, бұл тізбек үшін генератор екенін көрсеттік clients_id_seqжәне атрибут қостық. allocationSize = 1 Бұл қосымша төлсипат, бірақ мұны жасамасақ, бағдарламаны іске қосқан кезде біз келесі қатені аламыз:

org.hibernate.MappingException: The increment size of the [clients_id_seq] sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]
Бұл туралы Андрей пайдаланушысы түсініктемелерде не жазады: allocationSize, ең алдымен, «жаңа идентификатор» үшін күту режимінің дерекқорға сапарын азайтуға арналған. Егер мән == 1 болса, әрбір жаңа нысан үшін күту күйіне өтіңіз, оны дерекқорға сақтамас бұрын, идентификатор үшін дерекқорға «іске қосылады». Мән > 1 болса (мысалы, 5), күту режимі «жаңа» идентификатор үшін дерекқорға сирек хабарласады (мысалы, 5 рет) және байланысқан кезде күту режимі дерекқордан осы нөмірді сақтауды сұрайды (біздің жағдай, 5) мәндер. Сіз сипаттаған қате күту режимі 50 әдепкі идентификаторды алғыңыз келетінін көрсетеді, бірақ дерекқорда сіз осы нысан үшін тек 1-ші идентификаторға сәйкес идентификатор беруге дайын екеніңізді көрсеттіңіз . Тағы бір қатені пайдаланушы Николя Кудряшов ұстады : http://localhost:8080/clients/1 түпнұсқа мақаласынан сұрауды орындасаңыз, қате қайтарылады:
{
    "timestamp": "2020-04-02T19:20:16.073+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.javarush.lectures.rest_example.model.Client$HibernateProxy$ZA2m7agZ[\"hibernateLazyInitializer\"])",
    "path": "/clients/1"
}
Бұл қате Күту күйінің жалқау инициализациясына қатысты және одан құтылу үшін Клиент сыныбына қосымша annotation қосу керек:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Сөйтіп:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Енді бағдарламамызды іске қосайық (егер соңғы рет сол жерде қалса, клиенттер кестесін дерекқордан жойғаннан кейін) және application.properties файлынан 3 жолға түсініктеме берейік:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Өткен жолы біз тек соңғы жолға түсініктеме бердік, бірақ... Біз кестені жасап, толтырып қойғандықтан, бұл маған дәлірек көрінді. Тестілеуге көшейік, GET, POST, PUT және DELETE сұрауларын Postman арқылы орындаймыз, сонда қателер жойылып, бәрі жақсы жұмыс істеп тұрғанын көреміз. Міне, жұмыс бітті. Енді біз қысқаша қорытындылап, білгендерімізді қарастыра аламыз:
  • Компьютеріңізге PostgreSQL орнатыңыз
  • pgAdmin ішінде дерекқорларды жасаңыз
  • Кестелерді қолмен және бағдарламалы түрде жасау және жою
  • .sql файлдары арқылы кестелерді толтырыңыз
  • Біз Spring Framework «сиқырлы» JpaRepository интерфейсі туралы аздап білдік
  • Біз мұндай бағдарламаны жасау кезінде туындауы мүмкін кейбір қателер туралы білдік
  • Біз әріптестерден кеңес сұраудан ұялмауымыз керек екенін түсіндік
  • Біз JavaRush қауымдастығының әрқашан көмекке келетін күш екенін растадық;)
Әзірге осы жерде аяқтай аламыз. Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу.  2 - 4 бөлімОсы материалды оқуға уақыт бөлгендердің барлығына рахмет. Пікірлеріңізді, бақылауларыңызды, толықтыруларыңызды және сындарлы сыныңызды көруге қуаныштымын. Мүмкін сіз осы мәселеге неғұрлым талғампаз шешімдерді ұсына аласыз, мен бұл мақалаға UPD «кілт сөзі» арқылы қосуға уәде беремін, әрине, сізді автор ретінде атап өтемін. Жалпы, сізге осы мақала және материалды ұсынудың осы стилі ұнады ма, және жалпы, JR туралы мақалалар жазуды жалғастыру керек пе деп жазыңыз. Міне, толықтырулар: UPD1: пайдаланушы Юстинианcom.javarush.lectures.rest_example Java тіліндегі атау конвенцияларын бұзбау үшін пакеттің атын com.javarush.lectures.rest.exampleжәне жобаның атын өзгертуді қатты ұсынды . UPD2 пайдаланушысы Александр ПьяновClientRepository сыныптағы өрісті инициализациялау үшін ClientServiceImplannotationдан гөрі конструкторды пайдалануды ұсынды @Autowired. Бұл сирек жағдайларда алуға болатындығымен түсіндіріледі NullPointerException, және жалпы алғанда, бұл ең жақсы тәжірибе және мен онымен келісемін. Логикалық тұрғыдан, егер өріс an objectінің бастапқы функционалдығы үшін қажет болса, оны конструкторда инициализациялаған дұрыс, өйткені конструкторсыз класс an objectіге біріктірілмейді, сондықтан бұл өріс кезеңде инициализацияланады. an objectінің құрылуы. Мен түзетулері бар code фрагментін қосамын (немен ауыстыру керек):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Бірінші бөлімге сілтеме: Spring Boot жүйесінде RESTful қызметіне PostgreSQL дерекқорын қосу. 1-бөлім PS Егер сіздердің кез келгеніңіз осы білім беру қосымшасын әзірлеуді жалғастырғыңыз келсе, мен осы мақалаға нұсқаулықтарыңызға сілтеме қосуға қуаныштымын. Мүмкін бір күні бұл бағдарлама сіздің портфолиоңызға жұмыс қосуға болатын нақты іскери қосымшаға ұқсайтын нәрсеге айналуы мүмкін. PPS Осы қарапайым мақалаға қатысты мен бұл қалам сынамасын біздің қымбатты қыздарға, әйелдерге және ханымдарға арнауды шештім. Кім біледі, мүмкін қазір Java, JavaRush, табиғатта бағдарламалау болмас еді, егер бұл әйел болмаса . Мерекелеріңізбен, қымбатты ақылды халқымыз! 8 наурыз мерекесі құтты болсын! Бақытты және әдемі болыңыз!
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION