Nambahake database PostgreSQL menyang layanan RESTful ing Spring Boot. Part 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_example
paket 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 Client
kaya 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.
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 ClientServiceImpl
lan 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 ClientRepository
lan 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"
}
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 Client
aku salah nemtokake strategi kanggo anotasi @GeneratedValue(strategy = GenerationType.IDENTITY)
kanggo lapangan id
. 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 id
kelas Client
lan 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 @SequenceGenerator
kanggo 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;)
com.javarush.lectures.rest_example
dadi com.javarush.lectures.rest.example
, lan jeneng proyek, supaya ora nglanggar konvensi jeneng ing Jawa. Pangguna UPD2 Alexander Pyanov ngusulake manawa kanggo miwiti lapangan ClientRepository
ing kelas ClientServiceImpl
luwih 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!
GO TO FULL VERSION