JavaRush /Java blogi /Random-UZ /Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar b...
Artur
Daraja
Tallinn

Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish. 2-qism

Guruhda nashr etilgan
Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish. 1-qism Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish.  2-1-qism Shunday qilib, oxirgi qismda biz PostgresSQL ma'lumotlar bazasini kompyuterga qanday o'rnatishni, pgAdmin-da ma'lumotlar bazasini yaratishni, shuningdek, unda jadvallarni qo'lda va dasturiy ravishda yaratish va o'chirishni bilib oldik. Ushbu qismda biz dasturimizni qayta yozamiz, shunda u ushbu ma'lumotlar bazasi va jadvallar bilan ishlashni o'rganadi. Nega biz? Chunki men o'zim siz bilan ushbu materialdan o'rganyapman. Va keyin biz nafaqat vazifani hal qilamiz, balki tajribali dasturchilarning maslahati bilan yo'lda paydo bo'ladigan xatolarni tuzatamiz. Shunday qilib aytganda, biz jamoada ishlashni o'rganamiz;) Avval com.javarush.lectures.rest_examplepapkada yangi paket yaratamiz va uni chaqiramiz repository. Ushbu paketda biz yangi interfeys yaratamiz 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>  {
}
Ushbu interfeys bizning ma'lumotlar bazalarimiz va jadvallarimiz bilan "sehrli" o'zaro ta'sir qiladi. Nega sehrli? Chunki biz uni amalga oshirishni yozishimiz shart emas va Spring Framework buni bizga taqdim etadi. Siz shunchaki bunday interfeysni yaratishingiz kerak va siz allaqachon bu "sehr" dan foydalanishingiz mumkin. ClientKeyingi qadam sinfni quyidagicha tahrirlashdir :
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
}
Bu sinfda qilganimiz faqat ba'zi izohlar qo'shish edi. Keling, ularni ko'rib chiqaylik:
  • @Entity - bu fasol (sinf) ob'ekt ekanligini ko'rsatadi.
  • @Table - ushbu ob'ektda ko'rsatiladigan jadval nomini bildiradi.
  • @Id - ustun identifikatori (asosiy kalit - joriy jadvaldagi ma'lumotlarning yagonaligini ta'minlash uchun foydalaniladigan qiymat. Eslatma: Andrey )
  • @Column - ob'ekt xususiyatiga mos keladigan ustun nomini ko'rsatadi.
  • @GeneratedValue - bu xususiyat belgilangan strategiyaga muvofiq yaratilishini bildiradi.
Jadval maydonlarining nomlari sinfdagi o'zgaruvchilar nomlariga mos kelishi shart emas. Misol uchun, agar bizda o'zgaruvchi bo'lsa firstName, jadvaldagi maydonga nom beramiz first_name. Ushbu izohlar to'g'ridan-to'g'ri maydonlarda ham, ularni oluvchilarda ham o'rnatilishi mumkin. Ammo agar siz ushbu usullardan birini tanlasangiz, butun dasturingiz davomida ushbu uslubni saqlashga harakat qiling. Men ro'yxatlarni qisqartirish uchun birinchi usuldan foydalandim. Ma'lumotlar bazalari bilan ishlash uchun izohlarning to'liq ro'yxatini bu erda topishingiz mumkin . Endi sinfga boramiz ClientServiceImplva uni quyidagicha qayta yozamiz:
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;
    }
}
Ro'yxatda ko'rib turganingizdek, biz endi kerak bo'lmagan satrlarni o'chirib tashladik:
// Хранorще клиентов
private static final Map<Integer, Client>  CLIENT_REPOSITORY_MAP = new HashMap<>();

// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
Buning o'rniga biz interfeysimizni e'lon qildik ClientRepositoryva uning ustiga @Autowired izohini joylashtirdik , shunda Spring avtomatik ravishda bizning sinfimizga ushbu bog'liqlikni qo'shadi. Shuningdek, biz barcha ishlarni ushbu interfeysga topshirdik, aniqrog'i uni Bahor qo'shadigan amalga oshirish. Keling, yakuniy va eng qiziqarli bosqichga o'tamiz - arizamizni sinab ko'rish. Keling, Postman dasturini ochamiz (uni qanday ishlatishni bu erda ko'ring ) va ushbu manzilga GET so'rovini yuboramiz: http://localhost:8080/clients. Biz bu javobni olamiz:
[
    {
        "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)"
    }
]
Biz POST so'rovini yuboramiz:
{
  "name" : "Amigo",
  "email" : "amigo@jr.com",
  "phone" : "+7 (191) 746-43-23"
}
Va... biz dasturdagi birinchi xatoni ushlaymiz:
{
    "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-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish.  2-2 qism Biz jurnallarni ko'rib chiqamiz va quyidagi xatoni topamiz:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
Biz yana bir xil POST so'rovini yuboramiz, natija bir xil, ammo bu farq bilan: Ключ "(id)=(2)" уже существует. Biz bir xil so'rovni uchinchi marta yuboramiz va biz Status: 201 Created ni olamiz. Biz yana GET so'rovini yuboramiz va javob olamiz:
[
    {
        "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"
    }
]
Bu shuni ko'rsatadiki, bizning dasturimiz ushbu jadval oldindan to'ldirilganligiga e'tibor bermayapti va yana bittadan boshlab identifikatorni tayinlamoqda. Xo'sh, xato - bu ish vaqti, umidsizlikka tushmang, bu tez-tez sodir bo'ladi. Shuning uchun men yordam uchun ko'proq tajribali hamkasblarga murojaat qilaman: "Hurmatli hamkasblar, dastur normal ishlashi uchun buni qanday tuzatish kerakligini sharhlarda maslahat bering." Yordam ko'p vaqt o'tmadi va Stas Pasinkov sharhlarda menga qaysi tomonga qarashim kerakligini aytdi. Buning uchun unga alohida rahmat! Gap shundaki, men sinfda maydon uchun Clientannotatsiya strategiyasini noto'g'ri ko'rsatganman . Ushbu strategiya MySQL uchun mos keladi. Agar biz Oracle yoki PostrgeSQL bilan ishlasak, unda boshqa strategiyani o'rnatishimiz kerak. Asosiy kalitlar uchun strategiyalar haqida ko'proq ma'lumotni bu erda o'qishingiz mumkin . Men GenerationType.SEQUENCE strategiyasini tanladim. Uni amalga oshirish uchun biz initDB.sql faylini va, albatta, Client sinfining id maydonining izohlarini biroz qayta yozishimiz kerak bo'ladi. initDB.sql faylini qayta yozing: @GeneratedValue(strategy = GenerationType.IDENTITY)idSpring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish.  2-3-qism
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;
Nima o'zgardi: jadvalimizning id ustunining turi o'zgardi, lekin bu haqda keyinroq. Biz quyidagi qatorni qo'shdik, unda biz clients_id_seq yangi ketma-ketligini yaratamiz, u uchtadan boshlanishi kerakligini ko'rsatadi (chunki populateDB.sql faylidagi oxirgi identifikator 2) va o'sish bir marta sodir bo'lishi kerakligini ko'rsatadi. Keling, id ustun turiga qaytaylik. Bu erda biz INTEGERni ko'rsatdik, chunki agar biz SERIAL dan chiqsak, ketma-ketlik xuddi shu nomli clients_id_seq bilan avtomatik ravishda yaratiladi, lekin bittadan boshlanadi (bu dastur xatosiga olib keldi). Ammo, agar siz jadvalni o'chirmoqchi bo'lsangiz, pgAdmin interfeysi orqali yoki .sql fayli orqali quyidagi buyruqlar yordamida ushbu ketma-ketlikni qo'shimcha ravishda o'chirishingiz kerak bo'ladi:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Lekin jadvalni dastlab toʻldirish uchun populateDB.sql kabi fayldan foydalanmasangiz, unda asosiy kalit uchun SERIAL yoki BIGSERIAL turlaridan foydalanishingiz mumkin va ketma-ketlikni qoʻlda yaratishingiz shart emas, shuning uchun uni oʻchirishingiz shart emas. uni alohida. Ketma-ketliklar haqida ko'proq veb-saytida o'qishingiz mumkin. PostgreSQL hujjatlari . idKeling, sinf maydonining izohlariga o'tamiz Clientva ularni quyidagicha formatlaymiz:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
Biz nima qildik: ketma-ketlik generatorini yaratish uchun yangi izoh oʻrnatdik @SequenceGenerator, unga nom berdik clientsIdSeq, bu ketma-ketlik uchun generator ekanligini koʻrsatdik clients_id_seqva atribut qoʻshdik. allocationSize = 1 Bu ixtiyoriy atribut, lekin buni qilmasak, dasturni ishga tushirganimizda quyidagi xatoni olamiz:

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]
Bu haqda Andrey foydalanuvchisi izohlarda nima yozadi: allocationSize birinchi navbatda hibernatening "yangi identifikator" uchun ma'lumotlar bazasiga sayohatini kamaytirish uchun mo'ljallangan. Agar qiymat == 1 bo'lsa, har bir yangi ob'ekt uchun kutish rejimiga o'ting, uni ma'lumotlar bazasiga saqlashdan oldin, id uchun ma'lumotlar bazasiga "ishlaydi". Agar qiymat > 1 (masalan, 5) bo'lsa, hibernate "yangi" identifikator uchun ma'lumotlar bazasiga kamroq murojaat qiladi (masalan, 5 marta) va bog'langanda hibernate ma'lumotlar bazasidan ushbu raqamni zaxiralashni so'raydi (bizning sahifamizda holat, 5) qiymatlar. Siz ta'riflagan xato hibernate 50 ta standart identifikatorni olishni xohlashini ko'rsatadi, ammo ma'lumotlar bazasida siz ushbu ob'ekt uchun faqat 1-chi identifikatorga muvofiq identifikator berishga tayyor ekanligingizni bildirgansiz . Yana bir xatolik Nikolya Kudryashov tomonidan aniqlandi : Agar siz http://localhost:8080/clients/1 asl maqolasidan soʻrov yuborsangiz, xatolik qaytariladi:
{
    "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"
}
Ushbu xato Hibernate-ning dangasa ishga tushirilishi bilan bog'liq va undan xalos bo'lish uchun biz Client sinfiga qo'shimcha izoh qo'shishimiz kerak:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Shu tarzda, shu ravishda, shunday qilib:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Endi dasturimizni ishga tushiramiz (mijozlar jadvalini ma'lumotlar bazasidan olib tashlangandan so'ng, agar u oxirgi marta saqlanib qolsa) va application.properties faylidan 3 qatorni sharhlaymiz:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Oxirgi marta biz faqat oxirgi satrga izoh berdik, lekin... Jadvalni allaqachon yaratganimiz va to'ldirganimiz sababli, hozir bu menga mantiqiyroq tuyuldi. Keling, sinovga o'tamiz, GET, POST, PUT va DELETE so'rovlarini Postman orqali bajaramiz va biz xatolar yo'qolganini va hamma narsa yaxshi ishlayotganini ko'ramiz. Mana, ish tugadi. Endi biz qisqacha xulosa qilib, o'rganganimizni ko'rib chiqamiz:
  • PostgreSQL-ni kompyuteringizga o'rnating
  • pgAdmin-da ma'lumotlar bazalarini yarating
  • Jadvallarni qo'lda va dasturiy ravishda yaratish va o'chirish
  • .sql fayllari orqali jadvallarni to'ldiring
  • Biz Spring Frameworkning "sehrli" JpaRepository interfeysi haqida ozgina bilib oldik
  • Biz bunday dasturni yaratishda yuzaga kelishi mumkin bo'lgan ba'zi xatolar haqida bilib oldik
  • Biz hamkasblardan maslahat so'rashdan uyalmaslik kerakligini angladik
  • Biz JavaRush hamjamiyati har doim yordamga keladigan kuch ekanligini tasdiqladik;)
Hozircha shu yerda tugatishimiz mumkin. Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish.  2-4 qismUshbu materialni o'qish uchun vaqt ajratgan barchaga rahmat. Sizning sharhlaringiz, kuzatishlaringiz, qo'shimchalaringiz va konstruktiv tanqidlaringizni ko'rishdan xursand bo'laman. Ehtimol, siz ushbu muammoning yanada oqlangan echimlarini taklif qilasiz, men ushbu maqolaga UPD "kalit so'zi" orqali sizni muallif sifatida eslatib qo'yishga va'da beraman. Xo'sh, umuman olganda, ushbu maqola va materialni taqdim etishning ushbu uslubi sizga yoqdimi yoki yo'qligini yozing va umuman, men JR haqida maqola yozishni davom ettirishim kerakmi? Mana qo'shimchalar: UPD1: foydalanuvchi Justinian Java-da nomlash qoidalarini buzmaslik uchun paket nomini com.javarush.lectures.rest_exampleva com.javarush.lectures.rest.exampleloyiha nomini o'zgartirishni qat'iy tavsiya qildi. UPD2 foydalanuvchisi Aleksandr PyanovClientRepository sinfdagi maydonni ishga tushirish uchun ClientServiceImplizohdan ko'ra konstruktordan foydalanishni tavsiya qildi @Autowired. Bu kamdan-kam hollarda olishingiz mumkinligi bilan izohlanadi NullPointerException, va umuman olganda, bu eng yaxshi amaliyot va men bunga qo'shilaman. Mantiqan, agar ob'ektning dastlabki funksionalligi uchun maydon kerak bo'lsa, uni konstruktorda ishga tushirgan ma'qul, chunki konstruktorsiz sinf ob'ektga yig'ilmaydi, shuning uchun bu maydon bosqichda ishga tushiriladi. ob'ektni yaratish. Men tuzatishlar bilan kod bo'lagini qo'shaman (nima bilan almashtirilishi kerak):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Birinchi qismga havola: Spring Boot-da RESTful xizmatiga PostgreSQL ma'lumotlar bazasini qo'shish. 1-qism PS. Agar sizlardan birortangiz ushbu ta'lim dasturini ishlab chiqishda davom etmoqchi bo'lsangiz, men ushbu maqoladagi qo'llanmalaringizga havolani qo'shishdan mamnun bo'laman. Ehtimol, bir kun kelib ushbu dastur sizning portfelingizga ish qo'shishingiz mumkin bo'lgan haqiqiy biznes ilovasiga o'xshash narsaga aylanadi. PPS Ushbu kamtarona maqolaga kelsak, men ushbu qalam sinovini aziz qizlarimiz, ayollarimiz va xonimlarimizga bag'ishlashga qaror qildim. Kim biladi, ehtimol, bu ayol bo'lmaganida, endi tabiatda Java ham, JavaRush ham, dasturlash ham bo'lmasdi . Bayramingiz bilan tabriklaymiz, aziz aqlli xalqimiz! 8-mart bayramingiz bilan! Baxtli va chiroyli bo'ling!
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION