JavaRush /جاوا بلاگ /Random-SD /اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ 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 - اشارو ڪري ٿو ته هي ملڪيت مخصوص حڪمت عملي جي مطابق ٺاهي ويندي.
ٽيبل جي فيلڊ جا نالا ڪلاس ۾ متغير جي نالن سان ملائڻ نه گهرجن. مثال طور، جيڪڏهن اسان وٽ هڪ variable آهي 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"
}
اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ PostgreSQL ڊيٽابيس شامل ڪرڻ.  حصو 2 - 2 اسان لاگز کي ڏسو ۽ هيٺ ڏنل غلطي ڳوليون ٿا:

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اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ 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;
ڇا تبديل ڪيو ويو آهي: اسان جي ٽيبل جي 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 انٽرفيس جي اسپرنگ فريم ورڪ جي
  • اسان ڪجھ بگ بابت ڄاڻو ٿا جيڪي پيدا ٿي سگھن ٿيون جڏھن اھڙي پروگرام ٺاھيو
  • اسان محسوس ڪيو ته اسان کي ساٿين کان مشورو وٺڻ ۾ شرمسار نه ٿيڻ گهرجي
  • اسان تصديق ڪئي آهي ته جاوا رش ڪميونٽي هڪ قوت آهي جيڪا هميشه بچاء لاء ايندي؛)
اسان ھاڻي ھتي ختم ڪري سگھون ٿا. اسپرنگ بوٽ تي هڪ آرام واري خدمت ۾ PostgreSQL ڊيٽابيس شامل ڪرڻ.  حصو 2 - 4هر ڪنهن جي مهرباني جن هن مواد کي پڙهڻ لاء وقت ورتو. مون کي توهان جي تبصرن، مشاهدن، اضافو ۽ تعميري تنقيد کي ڏسي خوشي ٿيندي. ٿي سگهي ٿو ته توهان هن مسئلي جو وڌيڪ خوبصورت حل پيش ڪندا، جنهن کي مان هن مضمون ۾ شامل ڪرڻ جو واعدو ڪريان ٿو UPD ”ڪي ورڊ“ ذريعي، يقيناً توهان جي ليکڪ طور ذڪر ڪرڻ سان. خير، عام طور تي، لکو جيڪڏهن توهان کي هي مضمون پسند آهي ۽ مواد پيش ڪرڻ جو هي انداز، ۽ عام طور تي، ڇا مون کي JR تي آرٽيڪل لکڻ جاري رکڻ گهرجي. هتي اضافا آهن: UPD1: صارف Justinian سختي سان صلاح ڏني ته آئون پيڪيج جو نالو تبديل ڪريان 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 مارچ مبارڪ! خوش ۽ خوبصورت ٿي!
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION