JavaRush /Blog Java /Random-MS /Menambah pangkalan data PostgreSQL pada perkhidmatan REST...
Artur
Tahap
Tallinn

Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot. Bahagian 2

Diterbitkan dalam kumpulan
Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot. Bahagian 1 Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot.  Bahagian 2 - 1 Jadi, pada bahagian terakhir kami belajar cara memasang pangkalan data PostgresSQL pada komputer, mencipta pangkalan data dalam pgAdmin, dan juga mencipta serta memadam jadual di dalamnya secara manual dan pemprograman. Dalam bahagian ini kami akan menulis semula program kami supaya ia belajar bekerja dengan pangkalan data dan jadual ini. Kenapa kita? Kerana saya sendiri belajar dengan anda dari bahan ini. Dan kemudian kami bukan sahaja akan menyelesaikan tugas di tangan, tetapi juga membetulkan kesilapan yang timbul semasa dalam perjalanan, dengan bantuan nasihat daripada pengaturcara yang lebih berpengalaman. Jadi untuk bercakap, kita akan belajar untuk bekerja dalam satu pasukan ;) Mula-mula, mari buat com.javarush.lectures.rest_examplepakej baharu dalam folder dan panggilnya repository. Dalam pakej ini kami akan mencipta antara muka baharu 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>  {
}
Antara muka ini akan "secara ajaib" berinteraksi dengan pangkalan data dan jadual kami. Kenapa ajaib? Kerana kami tidak perlu menulis pelaksanaannya, dan rangka kerja Spring akan memberikannya kepada kami. Anda hanya perlu mencipta antara muka sedemikian, dan anda sudah boleh menggunakan "sihir" ini. Langkah seterusnya ialah 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
}
Apa yang kami lakukan dalam kelas ini hanyalah menambah beberapa anotasi. Mari kita pergi melalui mereka:
  • @Entity - menunjukkan bahawa kacang ini (kelas) ialah entiti.
  • @Jadual - menunjukkan nama jadual yang akan dipaparkan dalam entiti ini.
  • @Id - id lajur (kunci utama - nilai yang akan digunakan untuk memastikan keunikan data dalam jadual semasa. Nota: Andrei )
  • @Column - menunjukkan nama lajur yang dipetakan pada sifat entiti.
  • @GeneratedValue - menunjukkan bahawa sifat ini akan dijana mengikut strategi yang ditentukan.
Nama medan jadual tidak perlu sepadan dengan nama pembolehubah dalam kelas. Sebagai contoh, jika kita mempunyai pembolehubah firstName, maka kita akan menamakan medan dalam jadual first_name. Anotasi ini boleh ditetapkan secara langsung pada medan dan pada getternya. Tetapi jika anda memilih salah satu daripada kaedah ini, cuba kekalkan gaya ini sepanjang program anda. Saya menggunakan kaedah pertama hanya untuk memendekkan penyenaraian. Senarai anotasi yang lebih lengkap untuk bekerja dengan pangkalan data boleh didapati di sini . Sekarang mari kita pergi ke kelas ClientServiceImpldan tulis semula seperti 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 daripada penyenaraian, semua yang kami lakukan ialah memadamkan baris yang tidak kami perlukan lagi:
// Хранorще клиентов
private static final Map<Integer, Client>  CLIENT_REPOSITORY_MAP = new HashMap<>();

// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
Sebaliknya, kami mengisytiharkan antara muka kami ClientRepository, dan juga meletakkan anotasi @Autowired di atasnya supaya Spring secara automatik menambah kebergantungan ini pada kelas kami. Kami juga mewakilkan semua kerja kepada antara muka ini, atau sebaliknya pelaksanaannya, yang Spring akan tambah. Mari kita beralih ke peringkat terakhir dan paling menarik - menguji aplikasi kami. Mari buka program Posmen (lihat cara menggunakannya di sini ) dan hantar permintaan GET ke alamat ini: http://localhost:8080/clients. Kami mendapat jawapan 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 menghantar permintaan POST:
{
  "name" : "Amigo",
  "email" : "amigo@jr.com",
  "phone" : "+7 (191) 746-43-23"
}
Dan... kami menangkap pepijat pertama kami dalam 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"
}
Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot.  Bahagian 2 - 2 Kami melihat log dan mencari ralat berikut:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
Kami menghantar permintaan POST yang sama sekali lagi, hasilnya adalah sama, tetapi dengan perbezaan ini: Ключ "(id)=(2)" уже существует. Kami menghantar permintaan yang sama untuk kali ketiga, dan kami mendapat Status: 201 Created. Kami menghantar permintaan GET sekali lagi dan menerima jawapan:
[
    {
        "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"
    }
]
Ini menunjukkan bahawa program kami mengabaikan fakta bahawa jadual ini telah dipraisi dan memberikan id sekali lagi bermula dari satu. Nah, pepijat adalah saat yang berkesan, jangan putus asa, ini sering berlaku. Oleh itu, saya akan berpaling kepada rakan sekerja yang lebih berpengalaman untuk mendapatkan bantuan: "Rakan sekerja yang dihormati, sila maklumkan dalam ulasan cara membetulkannya supaya program berfungsi dengan normal." Bantuan tidak mengambil masa lama untuk tiba, dan Stas Pasinkov memberitahu saya dalam ulasan ke arah mana saya perlu melihat. Terima kasih khas kepadanya untuk ini! Tetapi masalahnya ialah dalam kelas Clientsaya tersilap menentukan strategi untuk anotasi @GeneratedValue(strategy = GenerationType.IDENTITY)untuk medan id. Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot.  Bahagian 2 - 3 Strategi ini sesuai untuk MySQL. Jika kita bekerja dengan Oracle atau PostrgeSQL, maka kita perlu menetapkan strategi yang berbeza. Anda boleh membaca lebih lanjut mengenai strategi untuk kunci utama di sini . Saya memilih strategi GenerationType.SEQUENCE. Untuk melaksanakannya, kita perlu menulis semula sedikit fail initDB.sql, dan, sudah tentu, anotasi medan id kelas Klien. Tulis semula 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;
Perkara yang telah berubah: jenis lajur id jadual kami telah berubah, tetapi lebih lanjut mengenainya kemudian. Kami menambah baris di bawah di mana kami mencipta jujukan baharu clients_id_seq, menunjukkan bahawa ia harus bermula dengan tiga (kerana id terakhir dalam fail populateDB.sql ialah 2), dan menunjukkan bahawa kenaikan harus berlaku dengan satu. Mari kita kembali kepada jenis lajur id. Di sini kami menentukan INTEGER, kerana jika kami meninggalkan SERIAL, urutan akan dibuat secara automatik, dengan nama yang sama clients_id_seq, tetapi akan bermula dari satu (yang membawa kepada pepijat program). Walau bagaimanapun, kini jika anda ingin memadamkan jadual, anda perlu memadam jujukan ini secara tambahan sama ada secara manual melalui antara muka pgAdmin atau melalui fail .sql menggunakan arahan berikut:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
Tetapi jika anda tidak menggunakan fail seperti populateDB.sql untuk mengisi jadual pada mulanya, maka anda boleh menggunakan jenis SERIAL atau BIGSERIAL untuk kunci utama, dan anda tidak perlu mencipta urutan secara manual, dan oleh itu tidak perlu memadam ia secara berasingan. Anda boleh membaca lebih lanjut mengenai urutan di laman web. Dokumentasi PostgreSQL . Mari kita beralih ke anotasi medan idkelas Clientdan formatkannya seperti berikut:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
Perkara yang kami lakukan: kami memasang anotasi baharu @SequenceGeneratoruntuk mencipta penjana jujukan, memberikannya nama clientsIdSeq, menunjukkan bahawa ini ialah penjana untuk jujukan clients_id_seqdan menambahkan atribut. allocationSize = 1 Ini ialah atribut pilihan, tetapi jika kami tidak melakukannya, kami akan mendapat ralat berikut apabila kami 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 perkara ini dalam ulasan: allocationSize terutamanya bertujuan untuk mengurangkan perjalanan hibernate ke pangkalan data untuk "id baharu". Jika nilai == 1, hibernate untuk setiap entiti baharu, sebelum menyimpannya dalam pangkalan data, "berjalan" ke pangkalan data untuk id. Jika nilainya ialah > 1 (contohnya, 5), hibernate akan menghubungi pangkalan data untuk mendapatkan id "baharu" kurang kerap (contohnya, 5 kali), dan apabila menghubungi, hibernate akan meminta pangkalan data untuk menempah nombor ini (dalam kami kes, 5) nilai. Ralat yang anda huraikan menunjukkan bahawa hibernate ingin menerima 50 id lalai, tetapi dalam pangkalan data anda menyatakan bahawa anda sudah bersedia untuk mengeluarkan id untuk entiti ini hanya mengikut yang pertama . Pepijat lain telah ditangkap oleh pengguna Nikolya Kudryashov : Jika anda menjalankan permintaan daripada artikel asal http://localhost:8080/clients/1, ralat 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"
}
Ralat ini berkaitan dengan permulaan malas Hibernate, dan untuk menyingkirkannya, kita perlu menambah anotasi tambahan pada kelas Klien:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Dengan cara ini:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
Sekarang mari jalankan program kami (selepas mengalih keluar jadual pelanggan daripada pangkalan data jika ia kekal di sana dari kali terakhir), dan mengulas 3 baris daripada fail application.properties:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
Kali terakhir kami mengulas hanya baris terakhir, tetapi... Memandangkan kita telah pun mencipta dan mengisi jadual, ini nampaknya lebih logik bagi saya pada masa ini. Mari kita beralih kepada menguji, melaksanakan permintaan GET, POST, PUT dan DELETE melalui Posmen, dan kita akan melihat bahawa pepijat telah hilang dan semuanya berfungsi dengan baik. Itu sahaja, kerja selesai. Sekarang kita boleh merumuskan secara ringkas dan mempertimbangkan apa yang telah kita pelajari:
  • Pasang PostgreSQL pada komputer anda
  • Cipta pangkalan data dalam pgAdmin
  • Buat dan padam jadual secara manual dan pemprograman
  • Isi jadual melalui fail .sql
  • Kami belajar sedikit tentang antara muka JpaRepository "ajaib" rangka kerja Spring
  • Kami mengetahui tentang beberapa pepijat yang mungkin timbul semasa mencipta program sedemikian
  • Kami menyedari bahawa kami tidak perlu malu untuk mendapatkan nasihat daripada rakan sekerja
  • Kami telah mengesahkan bahawa komuniti JavaRush adalah kuasa yang akan sentiasa datang untuk menyelamatkan;)
Kita boleh selesaikan di sini buat masa ini. Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot.  Bahagian 2 - 4Terima kasih kepada semua yang meluangkan masa untuk membaca bahan ini. Saya akan gembira melihat komen, pemerhatian, penambahan dan kritikan membina anda. Mungkin anda akan menawarkan penyelesaian yang lebih elegan untuk masalah ini, yang saya janjikan untuk ditambah pada artikel ini melalui "kata kunci" UPD, dengan menyebut anda sebagai pengarang, sudah tentu. Nah, secara umum, tulis jika anda menyukai artikel ini dan gaya penyampaian bahan ini, dan secara umum, sama ada saya perlu terus menulis artikel tentang JR. Berikut ialah penambahan: UPD1: pengguna Justinian amat mengesyorkan saya menamakan semula pakej com.javarush.lectures.rest_examplekepada com.javarush.lectures.rest.example, dan nama projek, supaya tidak melanggar konvensyen penamaan di Jawa. Pengguna UPD2 Alexander Pyanov mencadangkan bahawa untuk memulakan medan ClientRepositorydalam kelas ClientServiceImpladalah lebih baik menggunakan pembina daripada anotasi @Autowired. Ini dijelaskan oleh fakta bahawa dalam kes yang jarang berlaku, anda boleh mendapatkan NullPointerException, dan secara umum, ini adalah amalan terbaik, dan saya bersetuju dengannya. Secara logiknya, jika medan diperlukan untuk fungsi awal objek, maka lebih baik untuk memulakannya dalam pembina, kerana kelas tanpa pembina tidak akan dipasang menjadi objek, oleh itu, medan ini akan dimulakan pada peringkat penciptaan objek. Saya akan menambah serpihan kod dengan pembetulan (apa yang perlu diganti dengan apa):
@Autowired
private ClientRepository clientRepository;

private final ClientRepository clientRepository;

public ClientServiceImpl(ClientRepository clientRepository) {
   this.clientRepository = clientRepository;
}
Pautan ke bahagian pertama: Menambah pangkalan data PostgreSQL pada perkhidmatan RESTful pada Spring Boot. Bahagian 1 PS Jika mana-mana daripada anda ingin terus membangunkan aplikasi pendidikan ini, maka saya dengan senang hati akan menambah pautan kepada panduan anda dalam artikel ini. Mungkin suatu hari nanti program ini akan berkembang menjadi sesuatu yang menyerupai aplikasi perniagaan sebenar yang boleh anda tambahkan kerja pada portfolio anda. PPS Mengenai artikel sederhana ini, saya memutuskan untuk mendedikasikan ujian pena ini kepada gadis, wanita dan wanita yang dikasihi. Siapa tahu, mungkin sekarang tiada Java, tiada JavaRush, tiada pengaturcaraan dalam alam semula jadi, jika bukan kerana wanita ini . Tahniah pada percutian anda, orang pintar yang dikasihi! Selamat 8 Mac! Bahagia dan cantik!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION