اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ PostgreSQL ڊيٽابيس شامل ڪرڻ. حصو 1 تنهن ڪري، پوئين حصي ۾ اسان سکيو ته ڪيئن ڪمپيوٽر تي PostgresSQL ڊيٽابيس کي انسٽال ڪجي، pgAdmin ۾ ڊيٽابيس ٺاهيو، ۽ ان ۾ ٽيبل ٺاهي ۽ ڊليٽ به دستي ۽ پروگرام سان. هن حصي ۾ اسان پنهنجي پروگرام کي ٻيهر لکنداسين ته جيئن اهو هن ڊيٽابيس ۽ ٽيبل سان ڪم ڪرڻ سکي. اسان کي ڇو؟ ڇو ته مان پاڻ توهان سان گڏ هن مواد مان سکي رهيو آهيان. ۽ پوءِ اسان نه رڳو ڪم کي هٿ ۾ آڻينداسين، پر وڌيڪ تجربيڪار پروگرامرز جي مشوري جي مدد سان، هلندي هلندي پيدا ٿيندڙ غلطين کي به درست ڪنداسين. تنهنڪري ڳالهائڻ لاء، اسان هڪ ٽيم ۾ ڪم ڪرڻ سکندا سين؛) پهرين، اچو ته
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> {
}
هي انٽرفيس ”جادوءَ سان“ اسان جي ڊيٽابيس ۽ جدولن سان لهه وچڙ ڪندو. جادوئي ڇو؟ ڇو ته اسان کي ان تي عمل درآمد لکڻ جي ضرورت نه پوندي، ۽ اسپرنگ فريم ورڪ اسان کي فراهم ڪندو. توهان کي صرف اهڙي انٽرفيس ٺاهڻ جي ضرورت آهي، ۽ توهان اڳ ۾ ئي هن "جادو" استعمال ڪري سگهو ٿا. ايندڙ قدم 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
}
اسان هن ڪلاس ۾ صرف ڪجهه تشريحون شامل ڪيون. اچو ته انهن جي ذريعي وڃو:
- @Entity - اشارو ڪري ٿو ته هي بين (طبقو) هڪ ادارو آهي.
- @ ٽيبل - ڏيکاري ٿو ٽيبل جو نالو جيڪو هن اداري ۾ ڏيکاريو ويندو.
- @Id - ڪالم id (ابتدائي ڪيئي - قيمت جيڪا موجوده جدول ۾ ڊيٽا جي انفراديت کي يقيني بڻائڻ لاءِ استعمال ڪئي ويندي. نوٽ: Andrei )
- @Column - ڪالم جو نالو اشارو ڪري ٿو جيڪو اداري جي ملڪيت سان نقشو ڪيو ويو آهي.
- @GeneratedValue - اشارو ڪري ٿو ته هي ملڪيت مخصوص حڪمت عملي جي مطابق ٺاهي ويندي.
firstName
، ته پوءِ اسان ٽيبل ۾ فيلڊ جو نالو ڏينداسين first_name
. اهي تشريحون سڌو سنئون فيلڊ ۽ انهن جي حاصل ڪندڙن تي سيٽ ڪري سگھجن ٿيون. پر جيڪڏهن توهان انهن طريقن مان هڪ چونڊيو ٿا، ته پوء توهان جي پروگرام ۾ هن انداز کي برقرار رکڻ جي ڪوشش ڪريو. مون پهريون طريقو استعمال ڪيو صرف فهرستن کي مختصر ڪرڻ لاءِ. ڊيٽابيس سان ڪم ڪرڻ لاءِ تشريح جي وڌيڪ مڪمل فهرست ملي سگهي ٿي هتي . هاڻي اچو ته ڪلاس ڏانهن وڃو 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();
اسان ان جي بدران اسان جي انٽرفيس جو اعلان ڪيو ClientRepository
۽ ان جي مٿان @Autowired annotation پڻ رکيا ته جيئن بهار خودڪار طريقي سان اسان جي ڪلاس ۾ انحصار شامل ڪري. اسان سڀني ڪم کي هن انٽرفيس تي پڻ تفويض ڪيو، يا بلڪه ان تي عمل درآمد، جيڪو بهار شامل ڪندو. اچو ته آخري ۽ سڀ کان دلچسپ اسٽيج تي وڃو - اسان جي ايپليڪيشن کي جانچڻ. اچو ته پوسٽ مين پروگرام کوليون (ڏسو ته ان کي هتي ڪيئن استعمال ڪجي ) ۽ 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)"
}
]
اسان پوسٽ جي درخواست موڪليندا آهيون:
{
"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"
}
اسان لاگز کي ڏسو ۽ هيٺ ڏنل غلطي ڳوليون ٿا:
org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
Detail: Ключ "(id)=(1)" уже существует.
اسان ساڳئي پوسٽ جي درخواست ٻيهر موڪليندا آهيون، نتيجو ساڳيو آهي، پر هن فرق سان: Ключ "(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"
}
]
ان مان معلوم ٿئي ٿو ته اسان جو پروگرام ان حقيقت کي نظر انداز ڪري رهيو آهي ته هي ٽيبل اڳ ۾ ئي اڳي ئي آباد ٿي چڪو آهي ۽ هڪ کان شروع ٿيندڙ id ٻيهر تفويض ڪري رهيو آهي. خير، هڪ بگ هڪ ڪم ڪندڙ لمحو آهي، نااميد نه ٿيو، اهو اڪثر ٿئي ٿو. تنهن ڪري، مان مدد لاءِ وڌيڪ تجربيڪار ساٿين ڏانهن رخ ڪندس: ”پيارا ساٿيو، مهرباني ڪري تبصرن ۾ صلاح ڏيو ته ان کي ڪيئن حل ڪجي ته جيئن پروگرام عام طور تي ڪم ڪري. مدد پهچڻ ۾ گهڻو وقت نه لڳو، ۽ اسٽاس پاسنڪوف مون کي تبصرن ۾ ٻڌايو ته مون کي ڪهڙي طرف ڏسڻ جي ضرورت آهي. ان لاءِ سندس خاص مهرباني! پر ڳالهه اها هئي ته ڪلاس ۾ مون فيلڊ جي Client
تشريح جي حڪمت عملي غلط بيان ڪئي هئي . هي حڪمت عملي MySQL لاءِ موزون آهي. جيڪڏهن اسان Oracle يا PostrgeSQL سان ڪم ڪريون ٿا، پوءِ اسان کي هڪ مختلف حڪمت عملي قائم ڪرڻ جي ضرورت آهي. توھان وڌيڪ پڙھي سگھوٿا پرائمري ڪنجيز لاءِ حڪمت عمليون ھتي . مون چونڊيو GenerationType.SEQUENCE حڪمت عملي. ان کي لاڳو ڪرڻ لاءِ، اسان کي initDB.sql فائل کي ٿورو ٻيهر لکڻو پوندو، ۽ يقيناً، ڪلائنٽ ڪلاس جي id فيلڊ جي تشريح. ٻيهر لکو initDB.sql: @GeneratedValue(strategy = GenerationType.IDENTITY)
id
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;
ڇا تبديل ڪيو ويو آهي: اسان جي ٽيبل جي id ڪالمن جو قسم تبديل ٿي چڪو آهي، پر ان تي وڌيڪ بعد ۾. اسان ھيٺ ڏنل ھڪڙي لائن شامل ڪئي آھي جنھن ۾ اسان ھڪڙو نئون ترتيب ٺاھيو آھي clients_id_seq، ظاھر ڪيو ته ان کي ٽن سان شروع ڪرڻ گھرجي (ڇاڪاڻ ته populateDB.sql فائل ۾ آخري id 2 آھي)، ۽ اشارو ڪيو ته واڌارو ھڪڙي ٿيڻ گھرجي. اچو ته واپس وڃو ID ڪالمن جي قسم تي. هتي اسان INTEGER جي وضاحت ڪئي آهي، ڇاڪاڻ ته جيڪڏهن اسان SERIAL کي ڇڏي ڏيون ٿا، ترتيب خود بخود ٺاهي ويندي، ساڳئي نالو clients_id_seq سان، پر هڪ کان شروع ٿيندي (جنهن جي ڪري پروگرام بگ ٿي ويو). بهرحال، هاڻي جيڪڏهن توهان هڪ ٽيبل کي حذف ڪرڻ چاهيو ٿا، توهان کي اضافي طور تي هن ترتيب کي حذف ڪرڻ جي ضرورت پوندي يا ته دستي طور تي pgAdmin انٽرفيس ذريعي يا هيٺ ڏنل حڪمن کي استعمال ڪندي .sql فائل ذريعي:
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
پر جيڪڏهن توهان ٽيبل کي شروع ڪرڻ لاءِ populateDB.sql وانگر فائل استعمال نٿا ڪريو، ته پوءِ توهان بنيادي ڪي لاءِ SERIAL يا BIGSERIAL قسم استعمال ڪري سگهو ٿا، ۽ توهان کي ترتيب دستي طور ٺاهڻ جي ضرورت ناهي، ۽ تنهنڪري توهان کي حذف ڪرڻ جي ضرورت ناهي. اهو الڳ. توهان جي ويب سائيٽ تي ترتيبن بابت وڌيڪ پڙهي سگهو ٿا. PostgreSQL دستاويز . اچو ته اڳتي وڌون id
ڪلاس فيلڊ جي تشريحن تي Client
۽ انھن کي ھيٺ ڏنل شڪل ڏيو:
@Id
@Column(name = "id")
@SequenceGenerator(name = "clientsIdSeq", sequenceName = "clients_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "clientsIdSeq")
private Integer id;
اسان ڇا ڪيو: اسان هڪ نئين تشريح نصب ڪئي @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]
هتي اهو آهي جيڪو صارف آندري هن بابت تبصرن ۾ لکي ٿو: مختص سائز بنيادي طور تي "نئين آئي ڊي" لاءِ ڊيٽابيس ڏانهن هائبرنيٽ جي سفر کي گهٽائڻ جو مقصد آهي. جيڪڏهن قيمت == 1، هر نئين اداري لاءِ هائبرنيٽ ڪريو، ان کي ڊيٽابيس ۾ محفوظ ڪرڻ کان اڳ، id لاءِ ڊيٽابيس ڏانهن ”رنز“ ڪيو وڃي. جيڪڏهن قيمت > 1 آهي (مثال طور، 5)، hibernate ڊيٽابيس سان رابطو ڪندو ”نئين“ id لاءِ گهٽ اڪثر (مثال طور، 5 ڀيرا)، ۽ جڏهن رابطو ڪندو، hibernate ڊيٽابيس کان پڇندو ته هن نمبر کي محفوظ ڪرڻ لاءِ (اسان جي صورت، 5) قدر. غلطي جيڪا توهان بيان ڪئي آهي ان مان معلوم ٿئي ٿو ته هائبرنيٽ 50 ڊفالٽ آئي ڊيز حاصل ڪرڻ چاهيندو، پر ڊيٽابيس ۾ توهان اشارو ڪيو ته توهان صرف پهرين نمبر جي مطابق هن اداري لاءِ آئي ڊي جاري ڪرڻ لاءِ تيار آهيو . هڪ ٻيو بگ استعمال ڪندڙ Nikolya Kudryashov پاران پڪڙيو ويو : جيڪڏهن توهان اصل آرٽيڪل 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 جي سست شروعات سان لاڳاپيل آهي، ۽ ان کان نجات حاصل ڪرڻ لاء، اسان کي ڪلائنٽ ڪلاس ۾ اضافي تشريح شامل ڪرڻ جي ضرورت آهي:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
هن طريقي سان:
@Entity
@Table(name = "clients")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Client {.....}
هاڻي اچو ته اسان جو پروگرام هلون (ڊيٽابيس مان ڪلائنٽ ٽيبل کي هٽائڻ کان پوءِ جيڪڏهن اهو آخري وقت کان اتي رهي ٿو)، ۽ application.properties فائل مان 3 لائينون تبصرو ڪريو:
#spring.datasource.initialization-mode=ALWAYS
#spring.datasource.schema=classpath*:database/initDB.sql
#spring.datasource.data=classpath*:database/populateDB.sql
آخري دفعو اسان صرف آخري لائن تي تبصرو ڪيو، پر ... جيئن ته اسان اڳ ۾ ئي ٽيبل ٺاهي ۽ ڀريو آهي، اهو هن وقت مون کي وڌيڪ منطقي لڳي. اچو ته جاچ ڏانھن وڃو، پوسٽ مين ذريعي GET، POST، PUT ۽ DELETE درخواستن کي انجام ڏيو، ۽ اسان ڏسنداسين ته ڪيڙا غائب ٿي ويا آھن ۽ سڀ ڪجھ ٺيڪ ڪم ڪري رھيو آھي. اهو آهي، ڪم ڪيو. هاڻي اسان مختصر طور تي بيان ڪري سگهون ٿا ۽ غور ڪريون ٿا ته اسان ڇا سکيو آهي:
- انسٽال ڪريو PostgreSQL توهان جي ڪمپيوٽر تي
- pgAdmin ۾ ڊيٽابيس ٺاهيو
- دستي طور تي ۽ پروگرام جي ترتيب سان ٽيبل ٺاهيو ۽ ختم ڪريو
- .sql فائلن ذريعي جدولن کي آباد ڪريو
- اسان ٿورو سکيو ”جادو“ JpaRepository انٽرفيس جي اسپرنگ فريم ورڪ جي
- اسان ڪجھ بگ بابت ڄاڻو ٿا جيڪي پيدا ٿي سگھن ٿيون جڏھن اھڙي پروگرام ٺاھيو
- اسان محسوس ڪيو ته اسان کي ساٿين کان مشورو وٺڻ ۾ شرمسار نه ٿيڻ گهرجي
- اسان تصديق ڪئي آهي ته جاوا رش ڪميونٽي هڪ قوت آهي جيڪا هميشه بچاء لاء ايندي؛)
com.javarush.lectures.rest_example
۽ com.javarush.lectures.rest.example
پروجيڪٽ جو نالو، ته جيئن جاوا ۾ نالي جي ڪنوينشن جي ڀڃڪڙي نه ٿئي. UPD2 استعمال ڪندڙ اليگزينڊر پيانوفClientRepository
تجويز ڪيو ته ڪلاس ۾ فيلڊ کي شروع ڪرڻ لاءِ ClientServiceImpl
اهو بهتر آهي ته ڪنسٽرڪٽر استعمال ڪجي هڪ تشريح کان @Autowired
. هن حقيقت جي وضاحت ڪئي وئي آهي ته نادر ڪيسن ۾ توهان حاصل ڪري سگهو ٿا NullPointerException
، ۽ عام طور تي، اهو بهترين عمل آهي، ۽ مان ان سان متفق آهيان. منطقي طور تي، جيڪڏهن ڪنهن شئي جي شروعاتي ڪارڪردگيءَ لاءِ فيلڊ گهربل هجي ته پوءِ بهتر آهي ته ان کي ڪنسٽرڪٽر ۾ شروع ڪيو وڃي، ڇاڪاڻ ته ڪانسٽرڪٽر کان سواءِ ڪلاس کي ڪنهن شئي ۾ گڏ نه ڪيو ويندو، تنهن ڪري، هن فيلڊ کي اسٽيج تي شروع ڪيو ويندو. اعتراض جي تخليق جو. مان ترميمن سان ڪوڊ جو ٽڪرو شامل ڪندس (جنھن کي تبديل ڪرڻ جي ضرورت آھي)
@Autowired
private ClientRepository clientRepository;
private final ClientRepository clientRepository;
public ClientServiceImpl(ClientRepository clientRepository) {
this.clientRepository = clientRepository;
}
پهرين حصي سان ڳنڍيو: اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ PostgreSQL ڊيٽابيس شامل ڪرڻ. حصو 1 PS جيڪڏھن توھان مان ڪو ھن تعليمي ايپليڪيشن کي ترقي ڪرڻ جاري رکڻ چاھيو، ته پوءِ مون کي ھن مضمون ۾ توھان جي ھدايتن جي لنڪ شامل ڪندي خوشي ٿيندي. شايد ڪنهن ڏينهن هي پروگرام هڪ حقيقي ڪاروباري ايپليڪيشن جهڙي شيءِ ۾ وڌندو جيڪو توهان پنهنجي پورٽ فوليو تي ڪم شامل ڪري سگهو ٿا. PPS هن معمولي مضمون جي حوالي سان، مون فيصلو ڪيو ته قلم جي هن آزمائش کي اسان جي پياري ڇوڪرين، عورتن ۽ عورتن کي وقف ڪري. ڪير ڄاڻي ٿو، شايد هاڻي اتي نه هوندو جاوا، نه جاوا رش، نه پروگرامنگ فطرت ۾، جيڪڏهن هن عورت لاء نه . توهان جي موڪلن تي مبارڪون هجن، اسان جا پيارا سمارٽ ماڻهو! 8 مارچ مبارڪ! خوش ۽ خوبصورت ٿي!
GO TO FULL VERSION