JavaRush /Blog Jawa /Random-JV /Nambahake database PostgreSQL menyang layanan RESTful ing...
Artur
tingkat
Tallinn

Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot. Bagean 2

Diterbitake ing grup
Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot. Part 1 Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot.  Bagean 2 - 1 Dadi, ing bagean pungkasan kita sinau carane nginstal database PostgresSQL ing komputer, nggawe database ing pgAdmin, lan uga nggawe lan mbusak tabel ing manual lan programmatically. Ing bagean iki kita bakal nulis maneh program kita supaya sinau nggarap database lan tabel iki. Kenapa kita? Amarga aku dhewe sinau karo sampeyan saka materi iki. Banjur kita ora mung bakal ngrampungake tugas ing tangan, nanging uga mbenerake kesalahan sing muncul nalika lelungan, kanthi bantuan saran saka programer sing luwih berpengalaman. Dadi, kita bakal sinau kerja ing tim ;) Pisanan, ayo nggawe com.javarush.lectures.rest_examplepaket anyar ing folder lan nyebat repository. Ing paket iki kita bakal nggawe antarmuka anyar 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>  {
}
Antarmuka iki bakal "ajaib" sesambungan karo database lan tabel kita. Kenapa gaib? Amarga kita ora perlu nulis implementasine, lan framework Spring bakal nyedhiyani kita. Sampeyan mung kudu nggawe antarmuka kasebut, lan sampeyan wis bisa nggunakake "sihir" iki. Langkah sabanjure yaiku nyunting kelas Clientkaya iki:
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
}
Kabeh sing ditindakake ing kelas iki mung nambah sawetara anotasi. Ayo padha liwat:
  • @Entity - nuduhake yen kacang iki (kelas) minangka entitas.
  • @Tabel - nuduhake jeneng tabel sing bakal ditampilake ing entitas iki.
  • @Id - id kolom (kunci utama - nilai sing bakal digunakake kanggo njamin keunikan data ing tabel saiki. Cathetan: Andrei )
  • @Column - nuduhake jeneng kolom sing dipetakan menyang properti entitas.
  • @GeneratedValue - nuduhake yen properti iki bakal digawe miturut strategi kasebut.
Jeneng kolom tabel ora kudu cocog karo jeneng variabel ing kelas. Contone, yen kita duwe variabel firstName, banjur kita bakal menehi jeneng kolom ing tabel first_name. Anotasi iki bisa disetel langsung ing lapangan lan ing getter. Nanging yen sampeyan milih salah siji saka cara iki, banjur nyoba kanggo njaga gaya iki saindhenging kabeh program. Aku nggunakake cara pisanan mung kanggo shorten listings. Dhaptar anotasi sing luwih lengkap kanggo nggarap database bisa ditemokake ing kene . Saiki ayo menyang kelas ClientServiceImpllan nulis maneh kaya ing ngisor iki:
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;
    }
}
Kaya sing sampeyan deleng saka dhaptar, kabeh sing ditindakake yaiku mbusak garis sing ora dibutuhake maneh:
// Хранorще клиентов
private static final Map<Integer, Client>  CLIENT_REPOSITORY_MAP = new HashMap<>();

// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
Kita ngumumake antarmuka tinimbang ClientRepositorylan uga nyelehake anotasi @Autowired ing ndhuwur supaya Spring kanthi otomatis nambah ketergantungan iki menyang kelas kita. Kita uga delegated kabeh karya kanggo antarmuka iki, utawa rodo implementasine, kang Spring bakal nambah. Ayo pindhah menyang tahap pungkasan lan paling menarik - nyoba aplikasi kita. Ayo mbukak program Postman (ndeleng carane nggunakake kene ) lan ngirim panjalukan GET menyang alamat iki: http: // localhost: 8080 / clients. Kita entuk jawaban iki:
[
    {
        "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)"
    }
]
Kita ngirim panjalukan POST:
{
  "name" : "Amigo",
  "email" : "amigo@jr.com",
  "phone" : "+7 (191) 746-43-23"
}
Lan ... kita nyekel bug pisanan ing program:
{
    "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"
}
Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot.  Bagean 2 - 2 Kita ndeleng log lan nemokake kesalahan ing ngisor iki:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
Kita ngirim panjalukan POST sing padha maneh, asile padha, nanging kanthi prabédan iki: Ключ "(id)=(2)" уже существует. Kita ngirim panjalukan sing padha kanggo kaping telune, lan entuk Status: 201 Digawe. Kita ngirim panjalukan GET maneh lan nampa respon:
[
    {
        "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"
    }
]
Iki nuduhake yen program kita ora nggatekake kasunyatan manawa tabel iki wis diisi lan menehi id maneh wiwit saka siji. Inggih, bug minangka wayahe kerja, aja nglokro, iki asring kedadeyan. Mula, aku bakal njaluk bantuan saka kanca-kanca sing luwih berpengalaman: "Kanca-kanca sing ditresnani, menehi saran ing komentar babagan carane ndandani iki supaya program kasebut bisa digunakake kanthi normal." Bantuan ora suwe teka, lan Stas Pasinkov ngandhani ing komentar babagan arah sing kudu dakdeleng. Thanks khusus kanggo dheweke kanggo iki! Nanging bab iku ing kelas Clientaku salah nemtokake strategi kanggo anotasi @GeneratedValue(strategy = GenerationType.IDENTITY)kanggo lapangan id. Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot.  Bagean 2 - 3 Strategi iki cocok kanggo MySQL. Yen kita nggarap Oracle utawa PostrgeSQL, mula kita kudu nyetel strategi sing beda. Sampeyan bisa maca liyane babagan strategi kanggo kunci utami kene . Aku milih strategi GenerationType.SEQUENCE. Kanggo ngleksanakake, kita kudu rada nulis ulang file initDB.sql, lan, mesthi, anotasi lapangan id saka kelas Klien. Tulis ulang initDB.sql:
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;
Apa sing wis diganti: jinis kolom id ing tabel kita wis diganti, nanging luwih akeh babagan mengko. We ditambahaké baris ing ngisor iki kita nggawe urutan anyar clients_id_seq, nunjukaké sing kudu miwiti karo telung (amarga id pungkasan ing file populateDB.sql 2), lan nunjukaké sing nambah kudu kelakon siji. Ayo bali menyang jinis kolom id. Ing kene kita nemtokake INTEGER, amarga yen ninggalake SERIAL, urutan kasebut bakal digawe kanthi otomatis, kanthi jeneng clients_id_seq sing padha, nanging bakal diwiwiti saka siji (sing nyebabake bug program). Nanging, saiki yen sampeyan pengin mbusak tabel, sampeyan uga kudu mbusak urutan iki kanthi manual liwat antarmuka pgAdmin utawa liwat file .sql nggunakake printah ing ngisor iki:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Nanging yen sampeyan ora nggunakake file kaya populateDB.sql kanggo pisanan populate tabel, sampeyan bisa nggunakake jinis SERIAL utawa BIGSERIAL kanggo tombol utami, lan sampeyan ora kudu nggawe urutan kanthi manual, lan mulane ora kudu mbusak. iku kapisah. Sampeyan bisa maca liyane babagan urutan ing website saka. Dokumentasi PostgreSQL . Ayo pindhah menyang anotasi lapangan idkelas Clientlan format kaya ing ngisor iki:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
Apa sing ditindakake: kita nginstal anotasi anyar @SequenceGeneratorkanggo nggawe generator urutan, diwenehi jeneng clientsIdSeq, nuduhake yen iki generator kanggo urutan clients_id_seq, lan nambah atribut. allocationSize = 1 Iki minangka atribut opsional, nanging yen kita ora nindakake iki, kita bakal entuk kesalahan ing ngisor iki nalika mbukak program:

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]
Punika panganggo Andrei nyerat babagan iki ing komentar: allocationSize utamané dimaksudaké kanggo ngurangi trip hibernate kanggo database kanggo "id anyar". Yen nilai == 1, hibernate kanggo saben entitas anyar, sadurunge disimpen ing database, "mlaku" menyang database kanggo id. Yen nilai > 1 (contone, 5), hibernate bakal ngubungi database kanggo id "anyar" kurang asring (contone, kaping 5), lan nalika ngubungi, hibernate bakal njaluk database kanggo cadangan nomer iki (ing kita kasus, 5) nilai. Kesalahan sing sampeyan jelasake nuduhake manawa hibernate pengin nampa 50 id standar, nanging ing basis data sampeyan nuduhake yen sampeyan wis siyap ngetokake id kanggo entitas iki mung miturut nomer 1 . Bug liyane kejiret dening pangguna Nikolya Kudryashov : Yen sampeyan mbukak panjalukan saka artikel asli http://localhost:8080/clients/1, kesalahan bakal dibalekake:
{
    "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"
}
Kesalahan iki ana gandhengane karo initialization malas Hibernate, lan kanggo nyingkirake, kita kudu nambah anotasi tambahan menyang kelas Klien:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Ing cara iki:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Saiki ayo mbukak program kita (sawise mbusak tabel klien saka database yen tetep ana wiwit pungkasan), lan komentar metu 3 baris saka file application.properties:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Pungkasan kita menehi komentar mung baris pungkasan, nanging ... Awit kita wis nggawe lan ngisi meja, iki katon luwih logis kanggo aku. Ayo pindhah menyang nyoba, nindakake panjalukan GET, POST, PUT lan DELETE liwat Postman, lan kita bakal weruh manawa bug wis ilang lan kabeh bisa digunakake. Wis, tugas rampung. Saiki kita bisa ngringkes kanthi ringkes lan nimbang apa sing wis kita sinau:
  • Instal PostgreSQL ing komputer
  • Nggawe database ing pgAdmin
  • Nggawe lan mbusak tabel kanthi manual lan programmatically
  • Isi tabel liwat file .sql
  • Kita sinau sethithik babagan antarmuka JpaRepository "sihir" saka kerangka Spring
  • Kita sinau babagan sawetara kewan omo sing bisa kedadeyan nalika nggawe program kasebut
  • Kita nyadari yen kita ora kudu isin kanggo njaluk saran saka kolega
  • Kita wis ngonfirmasi manawa komunitas JavaRush minangka pasukan sing bakal nulungi;)
Kita bisa rampung ing kene kanggo saiki. Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot.  Bagean 2 - 4Matur nuwun kanggo kabeh sing wis melu maca materi iki. Aku bakal seneng ndeleng komentar, pengamatan, tambahan lan kritik sing mbangun. Mbok menawa sampeyan bakal menehi solusi sing luwih elegan kanggo masalah iki, sing aku janji bakal ditambahake ing artikel iki liwat "kata kunci" UPD, kanthi nyebutake sampeyan minangka penulis, mesthi. Inggih, umume, nulis yen sampeyan seneng karo artikel iki lan gaya presentasi materi iki, lan umume, apa aku kudu terus nulis artikel ing JR. Ing ngisor iki tambahan: UPD1: pangguna Justinian banget dianjurake supaya aku ngganti jeneng paket com.javarush.lectures.rest_exampledadi com.javarush.lectures.rest.example, lan jeneng proyek, supaya ora nglanggar konvensi jeneng ing Jawa. Pangguna UPD2 Alexander Pyanov ngusulake manawa kanggo miwiti lapangan ClientRepositorying kelas ClientServiceImplluwih becik nggunakake konstruktor tinimbang anotasi @Autowired. Iki diterangno dening kasunyatan sing ing kasus langka sampeyan bisa njaluk NullPointerException, lan ing umum, iki paling laku, lan aku setuju karo. Secara logis, yen lapangan dibutuhake kanggo fungsi awal obyek, mula luwih apik kanggo miwiti ing konstruktor, amarga kelas tanpa konstruktor ora bakal dirakit dadi obyek, mula lapangan iki bakal diinisialisasi ing panggung. saka nggawe obyek. Aku bakal nambah fragmen kode kanthi koreksi (apa sing kudu diganti karo apa):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Link menyang bagean pisanan: Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot. Part 1 PS Yen ana sing pengin terus ngembangake aplikasi pendidikan iki, aku bakal seneng nambah link menyang pandhuan sampeyan ing artikel iki. Mbok menawa program iki bakal tuwuh dadi kaya aplikasi bisnis nyata sing bisa ditambahake ing portofolio sampeyan. PPS Babagan artikel andhap asor iki, aku mutusake kanggo ngaturake tes pena iki kanggo bocah-bocah wadon, wanita lan wanita sing ditresnani. Sapa ngerti, mbok menawa saiki ora ana Java, ora JavaRush, ora ana program ing alam, yen ora kanggo wong wadon iki . Sugeng liburan sampeyan, wong pinter sing dikasihi! Sugeng 8 Maret! Seneng lan ayu!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION