JavaRush /Java блогу /Random-KY /Жазгы жүктөөдө RESTful кызматына PostgreSQL маалымат база...
Artur
Деңгээл
Tallinn

Жазгы жүктөөдө RESTful кызматына PostgreSQL маалымат базасын кошуу. 2 бөлүк

Группада жарыяланган
Жазгы жүктөөдө RESTful кызматына PostgreSQL маалымат базасын кошуу. 1-бөлүк Жазгы жүктөөдө RESTful кызматына PostgreSQL маалымат базасын кошуу.  2-1-бөлүк Ошентип, акыркы бөлүктө биз PostgresSQL маалымат базасын компьютерге орнотууну, pgAdminде маалымат базасын түзүүнү, ошондой эле андагы tableларды кол менен жана программалык түрдө түзүп, жок кылууну үйрөндүк. Бул бөлүктө биз программабызды бул маалымат базасы жана tableлар менен иштөөнү үйрөнүшү үчүн кайра жазабыз. Эмне үчүн биз? Анткени мен өзүм бул материалдан сиздер менен бирге үйрөнүп жатам. Анан биз алдыбыздагы тапшырманы гана чечпестен, тажрыйбалуу программисттердин кеңеши менен жолдо пайда болгон каталарды оңдойбуз. Мындайча айтканда, биз командада иштегенди үйрөнөбүз;) Алгач 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>  {
}
Бул интерфейс биздин базаларыбыз жана tableларыбыз менен "сыйкырдуу" өз ара аракеттенет. Эмне үчүн сыйкырдуу? Анткени биз анын аткарылышын жазуунун кереги жок болот жана Жазгы алHow аны бизге берет. Сиз жөн гана ушундай интерфейсти түзүшүңүз керек жана сиз бул "сыйкырды" колдоно аласыз. 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 - бул an objectте көрсөтүлө турган tableнын атын көрсөтөт.
  • @Id - мамычанын id (негизги ачкыч - учурдагы tableдагы маалыматтардын уникалдуулугун камсыз кылуу үчүн колдонула турган маани. Эскертүү: Андрей )
  • @Column - an object касиетине түшүрүлгөн тилкенин атын көрсөтөт.
  • @GeneratedValue - бул касиет көрсөтүлгөн стратегияга ылайык түзүлөөрүн көрсөтөт.
Таблица талааларынын аталыштары класстагы өзгөрмөлөрдүн атына дал келүүгө тийиш эмес. Мисалы, бизде өзгөрмө болсо firstName, анда биз tableдагы талааны атайбыз 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();
Биз анын ордуна интерфейсибизди жарыяладык жана анын үстүнө @AutowiredClientRepository annotationсын койдук , ошондуктан Spring бул көз карандылыкты классыбызга автоматтык түрдө кошот. Биз ошондой эле бардык ишти ушул интерфейске тапшырдык, тагыраак айтканда, аны Жаз кошо турган ишке ашыруу. Келгиле, акыркы жана эң кызыктуу этапка — биздин тиркемени сынап көрөлү. Почтачы программасын ачалы (аны кантип колдонууну бул жерден караңыз ) жана бул дарекке 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"
}
Жазгы жүктөөдө 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"
    }
]
Бул биздин программа бул table алдын ала толтурулганына көңүл бурбай, бирден баштап кайра id дайындап жатканын көрсөтүп турат. Ооба, мүчүлүштүк - бул иштөө учуру, үмүтүңүздү үзбөңүз, бул көп учурда болот. Ошондуктан, мен тажрыйбалуу кесиптештеримден жардам сурап кайрылам: "Урматтуу кесиптештер, программанын нормалдуу иштеши үчүн муну кантип оңдоону комментарийлерде кеңешиңиз." Жардам көпкө созулган жок жана Стас Пасинков комментарийлерде мага кайсы тарапты карашым керектигин айтты. Бул үчүн ага өзгөчө рахмат! Бирок класста мен талаа үчүн Clientannotationнын стратегиясын туура эмес көрсөткөнүмдө болду . Бул стратегия MySQL үчүн ылайыктуу. Эгерде биз Oracle же PostrgeSQL менен иштесек, анда биз башка стратегияны коюшубуз керек. Негизги ачкычтар үчүн стратегиялар тууралуу кененирээк бул жерден окуй аласыз . Мен GenerationType.SEQUENCE стратегиясын тандадым. Аны ишке ашыруу үчүн initDB.sql файлын жана, албетте, Client классынын id талаасынын annotationларын бир аз кайра жазышыбыз керек болот. initDB.sql кайра жазыңыз: @GeneratedValue(strategy = GenerationType.IDENTITY)idЖазгы жүктөөдө 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;
Эмне өзгөрдү: биздин tableбыздын id мамычасынын түрү өзгөрдү, бирок бул тууралуу кийинчерээк. Төмөндө биз clients_id_seq жаңы ырааттуулугун түзүүчү сапты коштук, ал үчтөн башталышы керек экенин көрсөтүп турат (анткени populateDB.sql файлындагы акыркы идентификатор 2) жана өсүү бир болушу керек экенин көрсөтөт. Келгиле, id мамычасынын түрүнө кайрылалы. Бул жерде биз INTEGER көрсөттүк, анткени SERIALдан чыксак, ырааттуулук автоматтык түрдө түзүлөт, ошол эле ат менен clients_id_seq, бирок бирөөдөн башталат (бул программанын мүчүлүштүгүнө алып келди). Бирок, эгер сиз tableны жок кылгыңыз келсе, анда бул ырааттуулукту pgAdmin интерфейси аркылуу кол менен же .sql файлы аркылуу төмөнкү буйруктарды колдонуу менен кошумча жок кылышыңыз керек болот:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Бирок tableны алгач толтуруу үчүн populateDB.sql сыяктуу файлды колдонбосоңуз, анда негизги ачкыч үчүн SERIAL же BIGSERIAL түрлөрүн колдонсоңуз болот жана ырааттуулукту кол менен түзүүнүн кажети жок, ошондуктан жок кылуунун кереги жок. аны өзүнчө. Катышуулар тууралуу кененирээк сайттан окуй аласыз. PostgreSQL documentациясы . 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 болсо, ар бир жаңы an object үчүн күтүү режимине өтүңүз, аны маалымат базасына сактоодон мурун, id үчүн маалымат базасына "чүрөт". Эгерде маани > 1 болсо (мисалы, 5), гибернация "жаңы" идентификатор үчүн маалымат базасына азыраак кайрылат (мисалы, 5 жолу) жана байланышып жатканда, күтүү режими маалымат базасынан бул номерди сактап коюуну суранат (биздин жагдай, 5) баалуулуктар. Сиз сүрөттөгөн ката күтүү режими 50 демейки идентификаторлорду алууну каалай турганын көрсөтүп турат, бирок маалымат базасында сиз бул an objectке 1-чиге ылайык гана ID чыгарууга даяр экениңизди көрсөткөнсүз . Дагы бир катаны колдонуучу Николя Кудряшов кармады : Эгер сиз 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"
}
Бул ката Hibernate'тин жалкоо инициализациясына байланыштуу жана андан кутулуу үчүн биз Client классына кошумча annotation кошуубуз керек:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Бул жол менен:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Эми программабызды иштетели (кардарлар tableсын маалымат базасынан алып салгандан кийин, эгерде ал акыркы жолу ошол жерде кала берсе) жана application.properties файлынан 3 сапты комментарийлейли:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Акыркы жолу биз акыркы сапка гана комментарий бергенбиз, бирок... Биз буга чейин tableны түзүп, толтургандыктан, бул учурда мага логикалуураак көрүндү. Келгиле, тестирлөөгө өтөлү, GET, POST, PUT жана DELETE өтүнүчтөрүн Почтачы аркылуу аткаралы, ошондо мүчүлүштүктөр жок болуп, баары жакшы иштеп жатканын көрөбүз. Болду, иш бүттү. Эми биз кыскача жыйынтыктап, үйрөнгөнүбүздү карап чыгабыз:
  • PostgreSQLди компьютериңизге орнотуңуз
  • pgAdmin маалымат базасын түзүү
  • Таблицаларды кол менен жана программалык түрдө түзүү жана жок кылуу
  • .sql файлдары аркылуу tableларды толтуруңуз
  • Биз Жаз алкагынын "сыйкырдуу" JpaRepository интерфейси жөнүндө бир аз билдик
  • Биз мындай программаны түзүүдө пайда болушу мүмкүн болгон кээ бир мүчүлүштүктөр жөнүндө билдик
  • Кесиптештерден кеңеш суроодон тартынбашыбыз керектигин түшүндүк
  • Биз JavaRush коомчулугу ар дайым жардамга келе турган күч экенин тастыктадык;)
Азырынча ушул жерден бүтүрө алабыз. Жазгы жүктөөдө RESTful кызматына PostgreSQL маалымат базасын кошуу.  2-4-бөлүкБул материалды окууга убакыт бөлгөндөрдүн баарына рахмат. Мен сиздин сын пикириңизди, байкооңузду, толуктоолорду жана конструктивдүү сынды көргөнүмө кубанычтамын. Мүмкүн, сиз бул маселенин дагы саркеч чечимдерин сунуштайсыз, мен бул макалага UPD “ачкыч сөзү” аркылуу кошууга убада берем, албетте, сизди автор катары белгилеп. Жалпысынан алганда, бул макала жана материалды берүү стor сизге жактыбы же жокпу, жана жалпысынан, мен JR боюнча макала жазууну улантуу керекпи же жокпу, жазыңыз. Бул жерде кошумчалар: UPD1: колдонуучу Юстиниан Java тorнде ат коюу конвенцияларын бузбоо үчүн пакеттин com.javarush.lectures.rest_exampleатын com.javarush.lectures.rest.exampleжана долбоордун атын өзгөртүүнү катуу сунуштады. UPD2 колдонуучусу Александр ПьяновClientRepository класстагы талааны инициализациялоо үчүн ClientServiceImplannotationга караганда конструкторду колдонууну сунуштады @Autowired. Бул сейрек учурларда ала тургандыгы менен түшүндүрүлөт NullPointerException, жана жалпысынан алганда, бул эң жакшы тажрыйба жана мен ага кошулам. Логикалык жактан, эгерде an objectтин баштапкы функционалдуулугу үчүн талаа талап кылынса, анда аны конструктордо инициализациялоо жакшы, анткени конструктору жок класс an objectке чогултулbyte, демек, бул талаа этапта инициализацияланат. an objectти түзүү. Мен оңдоолору менен code фрагментин кошом (эмнени эмне менен алмаштыруу керек):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Биринчи бөлүккө шилтеме: Жазгы жүктөөдөгү RESTful кызматына PostgreSQL маалымат базасын кошуу. 1-бөлүк PS. Эгер кимдир бирөө бул бorм берүү колдонмосун иштеп чыгууну улантууну кааласа, анда мен бул макаладагы колдонмолоруңузга шилтеме кошууга кубанычта болом. Балким, качандыр бир күнү бул программа сиз портфолиоңузга жумуш кошо турган чыныгы бизнес тиркемеге окшош нерсеге айланат. PPS Бул жөнөкөй макалага байланыштуу, мен бул калем сынагын биздин сүйүктүү кыздарыбызга, айымдарыбызга жана айымдарыбызга арнаууну чечтим. Ким билет, балким азыр бул аял болбогондо Java, JavaRush, жаратылышта программалоо болмок эмес . Майрамыңыздар менен урматтуу акылдуу элибиз! 8-март майрамыңыздар менен! Бактылуу жана сулуу бол!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION