JavaRush /Java Blog /Random-ID /Menambahkan database PostgreSQL ke layanan RESTful di Spr...
Artur
Level 40
Tallinn

Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot. Bagian 2

Dipublikasikan di grup Random-ID
Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot. Bagian 1 Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot.  Bagian 2 - 1 Jadi, pada bagian terakhir kita mempelajari cara menginstal database PostgresSQL di komputer, membuat database di pgAdmin, serta membuat dan menghapus tabel di dalamnya secara manual dan terprogram. Pada bagian ini kita akan menulis ulang program kita sehingga dapat belajar bekerja dengan database dan tabel ini. Mengapa kita? Karena saya sendiri belajar bersama anda dari materi ini. Dan kemudian kami tidak hanya akan menyelesaikan tugas yang ada, tetapi juga memperbaiki kesalahan yang muncul saat bepergian, dengan bantuan saran dari programmer yang lebih berpengalaman. Jadi bisa dikatakan, kita akan belajar bekerja dalam tim ;) Pertama, mari kita buat com.javarush.lectures.rest_examplepaket baru di dalam folder dan beri nama repository. Dalam paket ini kita akan membuat antarmuka baru 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 ini akan “secara ajaib” berinteraksi dengan database dan tabel kami. Mengapa secara ajaib? Karena kita tidak perlu menulis implementasinya, dan framework Spring akan menyediakannya kepada kita. Anda hanya perlu membuat antarmuka seperti itu, dan Anda sudah dapat menggunakan “keajaiban” ini. Langkah selanjutnya adalah mengedit kelas Clientseperti ini:
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
}
Yang kami lakukan di kelas ini hanyalah menambahkan beberapa anotasi. Mari kita bahas:
  • @Entity - menunjukkan bahwa kacang (kelas) ini adalah suatu entitas.
  • @Table - menunjukkan nama tabel yang akan ditampilkan di entitas ini.
  • @Id - kolom id (kunci utama - nilai yang akan digunakan untuk memastikan keunikan data pada tabel saat ini. Catatan: Andrei )
  • @Column - menunjukkan nama kolom yang dipetakan ke properti entitas.
  • @GeneratedValue - menunjukkan bahwa properti ini akan dihasilkan sesuai dengan strategi yang ditentukan.
Nama field tabel tidak harus sama dengan nama variabel di kelasnya. Misalnya jika kita mempunyai variabel firstName, maka kita akan memberi nama field tersebut pada tabel first_name. Anotasi ini dapat diatur baik secara langsung pada bidang maupun pada pengambilnya. Namun jika Anda memilih salah satu metode ini, cobalah untuk mempertahankan gaya ini di seluruh program Anda. Saya menggunakan metode pertama hanya untuk mempersingkat daftar. Daftar anotasi yang lebih lengkap untuk bekerja dengan database dapat ditemukan di sini . Sekarang mari kita masuk ke kelas ClientServiceImpldan menulis ulang sebagai berikut:
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;
    }
}
Seperti yang Anda lihat dari daftar, yang kami lakukan hanyalah menghapus baris yang tidak lagi kami perlukan:
// Хранorще клиентов
private static final Map<Integer, Client>  CLIENT_REPOSITORY_MAP = new HashMap<>();

// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
Kami mendeklarasikan antarmuka kami ClientRepositorydan juga menempatkan anotasi @Autowired di atasnya sehingga Spring secara otomatis menambahkan ketergantungan ini ke kelas kami. Kami juga mendelegasikan semua pekerjaan ke antarmuka ini, atau lebih tepatnya implementasinya, yang akan ditambahkan Spring. Mari kita beralih ke tahap terakhir dan paling menarik - menguji aplikasi kita. Mari kita buka program Postman (lihat cara menggunakannya di sini ) dan kirim permintaan GET ke alamat ini: http://localhost:8080/clients. Kami mendapatkan jawaban ini:
[
    {
        "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)"
    }
]
Kami mengirimkan permintaan POST:
{
  "name" : "Amigo",
  "email" : "amigo@jr.com",
  "phone" : "+7 (191) 746-43-23"
}
Dan... kami menemukan bug pertama kami di program ini:
{
    "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"
}
Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot.  Bagian 2 - 2 Kami melihat log dan menemukan kesalahan berikut:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
Kita kirim lagi request POST yang sama, hasilnya sama, tapi dengan perbedaan ini: Ключ "(id)=(2)" уже существует. Kita kirim request yang sama untuk ketiga kalinya, dan kita mendapat Status: 201 Dibuat. Kami mengirim permintaan GET lagi dan menerima tanggapan:
[
    {
        "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"
    }
]
Hal ini menunjukkan bahwa program kami mengabaikan fakta bahwa tabel ini telah diisi sebelumnya dan menetapkan id lagi mulai dari satu. Nah, bug adalah momen yang berhasil, jangan putus asa, ini sering terjadi. Oleh karena itu, saya akan meminta bantuan rekan-rekan yang lebih berpengalaman: “Rekan-rekan yang terhormat, mohon saran di komentar bagaimana cara memperbaikinya agar program berfungsi normal.” Bantuan tidak butuh waktu lama untuk tiba, dan Stas Pasinkov memberi tahu saya di kolom komentar ke arah mana saya harus mencari. Terima kasih khusus padanya untuk ini! Tapi masalahnya di kelas Clientsaya salah menentukan strategi anotasi @GeneratedValue(strategy = GenerationType.IDENTITY)untuk lapangan id. Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot.  Bagian 2 - 3 Strategi ini cocok untuk MySQL. Jika kita bekerja dengan Oracle atau PostrgeSQL, maka kita perlu menetapkan strategi yang berbeda. Anda dapat membaca lebih lanjut tentang strategi kunci utama di sini . Saya memilih strategi GenerationType.SEQUENCE. Untuk mengimplementasikannya, kita perlu sedikit menulis ulang file initDB.sql, dan, tentu saja, anotasi pada bidang id 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 yang berubah: jenis kolom id tabel kita telah berubah, tetapi akan dibahas lebih lanjut nanti. Kami menambahkan baris di bawah ini di mana kami membuat urutan baru client_id_seq, menunjukkan bahwa itu harus dimulai dengan tiga (karena id terakhir dalam file populateDB.sql adalah 2), dan menunjukkan bahwa kenaikan harus terjadi satu. Mari kita kembali ke tipe kolom id. Disini kita tentukan INTEGER, karena jika kita keluar dari SERIAL, maka sequence akan dibuat secara otomatis, dengan nama yang sama client_id_seq, tetapi akan dimulai dari satu (yang menyebabkan bug program). Namun, sekarang jika Anda ingin menghapus tabel, Anda juga perlu menghapus urutan ini secara manual melalui antarmuka pgAdmin atau melalui file .sql menggunakan perintah berikut:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Tetapi jika Anda tidak menggunakan file seperti populateDB.sql untuk mengisi tabel terlebih dahulu, maka Anda dapat menggunakan tipe SERIAL atau BIGSERIAL untuk kunci utama, dan Anda tidak perlu membuat urutan secara manual, dan oleh karena itu tidak perlu menghapus itu secara terpisah. Anda dapat membaca lebih lanjut tentang urutannya di situs web. Dokumentasi PostgreSQL . Mari beralih ke anotasi bidang idkelas Clientdan memformatnya sebagai berikut:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
Apa yang kami lakukan: kami memasang anotasi baru @SequenceGeneratoruntuk membuat generator urutan, memberinya nama clientsIdSeq, menunjukkan bahwa ini adalah generator untuk sequence clients_id_seq, dan menambahkan atribut. allocationSize = 1 Ini adalah atribut opsional, tetapi jika kami tidak melakukan ini, kita akan mendapatkan error berikut ketika kita menjalankan 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]
Inilah yang ditulis oleh pengguna Andrei tentang hal ini di komentar: alokasiSize terutama dimaksudkan untuk mengurangi perjalanan hibernasi ke database untuk "id baru". Jika nilainya == 1, hibernasi untuk setiap entitas baru, sebelum menyimpannya di database, “berjalan” ke database untuk id. Jika nilainya > 1 (misalnya, 5), hibernate akan lebih jarang menghubungi database untuk mendapatkan id "baru" (misalnya, 5 kali), dan saat menghubungi, hibernate akan meminta database untuk memesan nomor ini (di kami kasus, 5) nilai. Kesalahan yang Anda jelaskan menunjukkan bahwa hibernasi ingin menerima 50 id default, tetapi dalam database Anda menunjukkan bahwa Anda siap mengeluarkan id untuk entitas ini hanya sesuai dengan yang pertama . Bug lain ditangkap oleh pengguna Nikolya Kudryashov : Jika Anda menjalankan permintaan dari artikel asli http://localhost:8080/clients/1, kesalahan akan dikembalikan:
{
    "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 ini terkait dengan inisialisasi Hibernate yang lambat, dan untuk menghilangkannya, kita perlu menambahkan anotasi tambahan ke kelas Klien:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Lewat sini:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Sekarang mari kita jalankan program kita (setelah menghapus tabel klien dari database jika tabel tersebut masih ada sejak terakhir kali), dan beri komentar pada 3 baris dari file application.properties:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Terakhir kali kami hanya mengomentari baris terakhir, tapi... Karena kita telah membuat dan mengisi tabel, hal ini tampak lebih logis bagi saya saat ini. Mari kita lanjutkan ke pengujian, lakukan permintaan GET, POST, PUT dan DELETE melalui Postman, dan kita akan melihat bahwa bug telah hilang dan semuanya berfungsi dengan baik. Selesai, pekerjaan selesai. Sekarang kita dapat meringkas secara singkat dan mempertimbangkan apa yang telah kita pelajari:
  • Instal PostgreSQL di komputer Anda
  • Buat database di pgAdmin
  • Membuat dan menghapus tabel secara manual dan terprogram
  • Isi tabel melalui file .sql
  • Kami belajar sedikit tentang antarmuka JpaRepository “ajaib” dari kerangka Spring
  • Kami mempelajari beberapa bug yang mungkin timbul saat membuat program semacam itu
  • Kami menyadari bahwa kami tidak perlu malu untuk meminta nasihat dari rekan kerja
  • Kami telah mengonfirmasi bahwa komunitas JavaRush adalah kekuatan yang akan selalu datang untuk menyelamatkan ;)
Kita bisa menyelesaikannya di sini untuk saat ini. Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot.  Bagian 2 - 4Terima kasih kepada semua orang yang meluangkan waktu untuk membaca materi ini. Saya akan senang melihat komentar, pengamatan, tambahan dan kritik yang membangun dari Anda. Mungkin Anda akan menawarkan solusi yang lebih elegan untuk masalah ini, yang saya berjanji akan menambahkannya ke artikel ini melalui “kata kunci” UPD, dengan menyebutkan Anda sebagai penulisnya, tentunya. Secara umum, tulislah apakah Anda menyukai artikel ini dan gaya penyajian materinya, dan secara umum, apakah saya harus terus menulis artikel di JR. Berikut tambahannya: UPD1: pengguna Justinian sangat menyarankan agar saya mengganti nama paket com.javarush.lectures.rest_examplemenjadi com.javarush.lectures.rest.example, dan nama proyeknya, agar tidak melanggar konvensi penamaan di Java. Pengguna UPD2 Alexander Pyanov menyarankan bahwa untuk menginisialisasi bidang ClientRepositorydi kelas ClientServiceImpllebih baik menggunakan konstruktor daripada anotasi @Autowired. Hal ini dijelaskan oleh fakta bahwa dalam kasus yang jarang terjadi, Anda bisa mendapatkan NullPointerException, dan secara umum, ini adalah praktik terbaik, dan saya setuju dengan itu. Logikanya, jika suatu field diperlukan untuk fungsionalitas awal suatu objek, maka lebih baik diinisialisasi di konstruktor, karena kelas tanpa konstruktor tidak akan dirangkai menjadi suatu objek, oleh karena itu bidang ini akan diinisialisasi pada tahap dari pembuatan objek. Saya akan menambahkan potongan kode dengan koreksi (yang perlu diganti dengan apa):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Tautan ke bagian pertama: Menambahkan database PostgreSQL ke layanan RESTful di Spring Boot. Bagian 1 PS Jika ada di antara Anda yang ingin terus mengembangkan aplikasi pendidikan ini, maka saya akan dengan senang hati menambahkan link panduan Anda di artikel ini. Mungkin suatu saat nanti program ini akan berkembang menjadi sesuatu yang menyerupai aplikasi bisnis nyata yang dapat Anda tambahkan pekerjaan ke dalam portofolio Anda. PPS Mengenai artikel sederhana ini, saya memutuskan untuk mendedikasikan tes pena ini untuk para gadis, wanita, dan wanita terkasih. Siapa tahu, mungkin sekarang tidak akan ada Java, tidak ada JavaRush, tidak ada pemrograman di alam, jika bukan karena wanita ini . Selamat atas liburan Anda, orang-orang pintar kami yang terkasih! Selamat 8 Maret! Jadilah bahagia dan cantik!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION