JavaRush /جاوا بلاگ /Random-UR /اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شا...
Artur
سطح
Tallinn

اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔ حصہ 2

گروپ میں شائع ہوا۔
اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔ حصہ 1 اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔  حصہ 2 - 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 تشریح بھی رکھی تاکہ اسپرنگ خود بخود اس انحصار کو ہماری کلاس میں شامل کردے۔ ہم نے تمام کام اس انٹرفیس کو سونپ دیے ہیں، یا اس کے نفاذ کو، جس میں بہار شامل کرے گی۔ آئیے آخری اور سب سے دلچسپ مرحلے پر چلتے ہیں - اپنی درخواست کی جانچ کر رہے ہیں۔ آئیے پوسٹ مین پروگرام کھولیں (دیکھیں کہ اسے یہاں کیسے استعمال کیا جائے ) اور اس ایڈریس پر ایک 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"
}
اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔  حصہ 2 - 2 ہم نوشتہ جات کو دیکھتے ہیں اور درج ذیل خامی تلاش کرتے ہیں:

org.postgresql.util.PSQLException: ОШИБКА: повторяющееся meaning ключа нарушает ограничение уникальности "clients_pkey"
  Detail: Ключ "(id)=(1)" уже существует.
ہم وہی POST درخواست دوبارہ بھیجتے ہیں، نتیجہ وہی ہے، لیکن اس فرق کے ساتھ: Ключ "(id)=(2)" уже существует. ہم وہی درخواست تیسری بار بھیجتے ہیں، اور ہمیں Status: 201 Created ملتا ہے۔ ہم دوبارہ 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"
    }
]
اس سے پتہ چلتا ہے کہ ہمارا پروگرام اس حقیقت کو نظر انداز کر رہا ہے کہ یہ ٹیبل پہلے ہی پہلے سے آباد ہو چکا ہے اور ایک سے شروع ہونے والی آئی ڈی دوبارہ تفویض کر رہا ہے۔ ٹھیک ہے، ایک بگ کام کرنے کا لمحہ ہے، مایوس نہ ہوں، ایسا اکثر ہوتا ہے۔ لہذا، میں مدد کے لیے مزید تجربہ کار ساتھیوں سے رجوع کروں گا: "پیارے ساتھیوں، براہ کرم تبصروں میں مشورہ دیں کہ اسے کیسے ٹھیک کیا جائے تاکہ پروگرام عام طور پر کام کرے۔" مدد پہنچنے میں زیادہ وقت نہیں لگا، اور Stas Pasinkov نے مجھے تبصروں میں بتایا کہ مجھے کس سمت دیکھنے کی ضرورت ہے۔ اس کے لیے اس کا خصوصی شکریہ! لیکن بات یہ تھی کہ کلاس میں میں نے فیلڈ کے لیے Clientتشریح کی حکمت عملی کو غلط طریقے سے بیان کیا تھا ۔ یہ حکمت عملی MySQL کے لیے موزوں ہے۔ اگر ہم Oracle یا PostrgeSQL کے ساتھ کام کرتے ہیں، تو ہمیں ایک مختلف حکمت عملی ترتیب دینے کی ضرورت ہے۔ آپ یہاں پرائمری کیز کے لیے حکمت عملیوں کے بارے میں مزید پڑھ سکتے ہیں ۔ میں نے GenerationType.SEQUENCE حکمت عملی کا انتخاب کیا۔ اسے نافذ کرنے کے لیے، ہمیں initDB.sql فائل کو تھوڑا سا دوبارہ لکھنا ہوگا، اور یقیناً، کلائنٹ کلاس کے آئی ڈی فیلڈ کی تشریحات۔ initDB.sql دوبارہ لکھیں: @GeneratedValue(strategy = GenerationType.IDENTITY)idاسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔  حصہ 2 - 3
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;
کیا بدلا ہے: ہمارے ٹیبل کے آئی ڈی کالم کی قسم بدل گئی ہے، لیکن اس پر مزید بعد میں۔ ہم نے ذیل میں ایک لائن شامل کی ہے جس میں ہم ایک نئی ترتیب clients_id_seq بناتے ہیں، اشارہ کرتے ہیں کہ اسے تین سے شروع ہونا چاہیے (کیونکہ populateDB.sql فائل میں آخری id 2 ہے)، اور اشارہ کرتے ہیں کہ اضافہ ایک سے ہونا چاہیے۔ آئی ڈی کالم کی قسم پر واپس چلتے ہیں۔ یہاں ہم نے INTEGEER کی وضاحت کی ہے، کیونکہ اگر ہم SERIAL کو چھوڑ دیتے ہیں، تو ترتیب خود بخود بن جائے گی، اسی نام کے ساتھ clients_id_seq، لیکن ایک سے شروع ہوگا (جس کی وجہ سے پروگرام بگ ہوا)۔ تاہم، اب اگر آپ ٹیبل کو حذف کرنا چاہتے ہیں، تو آپ کو اس ترتیب کو یا تو دستی طور پر pgAdmin انٹرفیس کے ذریعے یا درج ذیل کمانڈز کا استعمال کرتے ہوئے .sql فائل کے ذریعے حذف کرنا ہوگا۔
DROP TABLE IF EXISTS clients;
DROP SEQUENCE IF EXISTS clients_id_seq
لیکن اگر آپ ابتدائی طور پر ٹیبل کو آباد کرنے کے لیے populateDB.sql جیسی فائل استعمال نہیں کرتے ہیں، تو آپ بنیادی کلید کے لیے SERIAL یا BIGSERIAL اقسام استعمال کر سکتے ہیں، اور آپ کو ترتیب دستی طور پر بنانے کی ضرورت نہیں ہے، اور اس لیے حذف کرنے کی ضرورت نہیں ہے۔ یہ الگ سے. آپ کی ویب سائٹ پر ترتیب کے بارے میں مزید پڑھ سکتے ہیں. پوسٹگری ایس کیو ایل دستاویزات آئیے 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، ہر نئی ہستی کے لیے ہائبرنیٹ ہو، اسے ڈیٹا بیس میں محفوظ کرنے سے پہلے، آئی ڈی کے لیے ڈیٹا بیس میں "رنز" کرتا ہے۔ اگر قیمت > 1 ہے (مثال کے طور پر، 5)، ہائبرنیٹ ڈیٹا بیس سے "نئی" آئی ڈی کے لیے کم کثرت سے رابطہ کرے گا (مثال کے طور پر، 5 بار)، اور رابطہ کرتے وقت، ہائبرنیٹ ڈیٹا بیس سے اس نمبر کو محفوظ کرنے کے لیے کہے گا (ہمارے کیس، 5) اقدار۔ آپ نے جو غلطی بیان کی ہے اس سے پتہ چلتا ہے کہ ہائبرنیٹ 50 ڈیفالٹ آئی ڈیز وصول کرنا چاہے گا، لیکن ڈیٹا بیس میں آپ نے اشارہ کیا ہے کہ آپ اس ہستی کے لیے صرف 1st کے مطابق آئی ڈی جاری کرنے کے لیے تیار ہیں ۔ ایک اور بگ صارف نکولیا کدریاشوف نے پکڑا تھا : اگر آپ اصل مضمون 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"
}
اس خرابی کا تعلق ہائبرنیٹ کے سست آغاز سے ہے، اور اس سے چھٹکارا پانے کے لیے، ہمیں کلائنٹ کلاس میں ایک اضافی تشریح شامل کرنے کی ضرورت ہے:
@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 انٹرفیس کے بارے میں تھوڑا سا سیکھا۔
  • ہم نے کچھ کیڑے کے بارے میں سیکھا جو اس طرح کا پروگرام بناتے وقت پیدا ہو سکتے ہیں۔
  • ہمیں احساس ہوا کہ ہمیں ساتھیوں سے مشورہ لینے میں شرمندہ نہیں ہونا چاہیے۔
  • ہم نے تصدیق کی ہے کہ JavaRush کمیونٹی ایک ایسی قوت ہے جو ہمیشہ بچائے گی؛)
ہم ابھی کے لیے یہاں ختم کر سکتے ہیں۔ اسپرنگ بوٹ پر ایک آرام دہ سروس میں PostgreSQL ڈیٹا بیس شامل کرنا۔  حصہ 2 - 4ہر ایک کا شکریہ جنہوں نے اس مواد کو پڑھنے کے لیے وقت نکالا۔ مجھے آپ کے تبصرے، مشاہدات، اضافے اور تعمیری تنقید دیکھ کر خوشی ہوگی۔ شاید آپ اس مسئلے کے مزید خوبصورت حل پیش کریں گے، جسے میں اس مضمون میں UPD "کلیدی لفظ" کے ذریعے شامل کرنے کا وعدہ کرتا ہوں، یقیناً آپ کے بطور مصنف کا ذکر ہے۔ ٹھیک ہے، عمومی طور پر، لکھیں کہ کیا آپ کو یہ مضمون اور مواد پیش کرنے کا یہ انداز پسند آیا، اور عمومی طور پر، کیا مجھے JR پر مضامین لکھنا جاری رکھنا چاہیے۔ یہ اضافے ہیں: UPD1: صارف جسٹنین نے سختی سے سفارش کی کہ میں پیکیج کا نام تبدیل کر 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 اگر آپ میں سے کوئی بھی اس تعلیمی ایپلیکیشن کو تیار کرنا جاری رکھنا چاہتا ہے، تو مجھے اس مضمون میں آپ کے گائیڈز کا لنک شامل کرنے میں خوشی ہوگی۔ شاید کسی دن یہ پروگرام ایک حقیقی کاروباری ایپلی کیشن سے ملتا جلتا چیز بن جائے گا جس پر آپ اپنے پورٹ فولیو میں کام شامل کر سکتے ہیں۔ پی پی ایس اس معمولی مضمون کے حوالے سے، میں نے فیصلہ کیا کہ قلم کا یہ امتحان اپنی پیاری لڑکیوں، خواتین اور خواتین کے لیے وقف کروں۔ کون جانتا ہے، شاید اب اس عورت کے لیے نہ تو جاوا، نہ جاوا رش، فطرت میں کوئی پروگرامنگ نہ ہوتی ۔ آپ کی چھٹی پر مبارک ہو، ہمارے پیارے ہوشیار لوگ! 8 مارچ مبارک ہو! خوش اور خوبصورت رہو!
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION