Iqiniso kuqala, noma kungani isistimu idinga ukuklanywa ngokusekelwe kusakhiwo sedathabhesi

Sawubona Habr!

Siyaqhubeka nokuhlola isihloko Java ΠΈ Springokuhlanganisa nezinga lesizindalwazi. Namuhla sinikeza ukufunda mayelana nokuthi kungani, lapho uklama izinhlelo zokusebenza ezinkulu, kuyisakhiwo sedathabhesi, hhayi ikhodi ye-Java, okufanele ibe semqoka kakhulu, ukuthi lokhu kwenziwa kanjani, futhi yiziphi izinto ezihlukile kulo mthetho.

Kulesi sihloko esishiywe yisikhathi, ngizochaza ukuthi kungani ngicabanga ukuthi cishe kuzo zonke izimo, imodeli yedatha kuhlelo lokusebenza kufanele yakhelwe "kusuka kusizindalwazi" kunokuba "kusuka emandleni e-Java" (noma yiluphi ulimi lweklayenti oyilo. ukusebenza nayo). Ngokukhetha indlela yesibili, ufaka indlela ende yobuhlungu nokuhlupheka uma iphrojekthi yakho isiqala ukukhula.

Isihloko sabhalwa sisekelwe ku umbuzo OWODWA, inikezwe Ekuchichimeni Kwesitaki.

Izingxoxo ezithakazelisayo ku-reddit ezigabeni /r/java ΠΈ /r/uhlelo.

Ukwenziwa kwekhodi

Ngimangele kanjani ukuthi kukhona ungqimba oluncane lwabasebenzisi abathi, sebejwayelene ne-jOOQ, bacasuke ngokuthi i-jOOQ incike kakhulu ekukhiqizeni amakhodi omthombo ukuze isebenze. Akekho okuvimbela ukuthi usebenzise i-jOOQ ngendlela obona kufanele ngayo, futhi akekho okuphoqayo ukuthi usebenzise ukukhiqiza amakhodi. Kodwa ngokuzenzakalelayo (njengoba kuchaziwe kumanuwali), i-jOOQ isebenza kanje: uqala nge-schema (yefa) sedathabhesi, uyibuyisele emuva uyinjiniyela ngejeneretha yekhodi ye-jOOQ ukuze uthole iqoqo lamakilasi amele amatafula akho, bese ubhala uhlobo- imibuzo ephephile ngokumelene nalawa mathebula:

	for (Record2<String, String> record : DSL.using(configuration)
//   ^^^^^^^^^^^^^^^^^^^^^^^ Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ Ρ‚ΠΈΠΏΠ°Ρ… Π²Ρ‹Π²Π΅Π΄Π΅Π½Π° Π½Π° 
//   основании сгСнСрированного ΠΊΠΎΠ΄Π°, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ссылаСтся ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½ΠΎΠ΅
// Π½ΠΈΠΆΠ΅ условиС SELECT 
 
       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
//           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ сгСнСрированныС ΠΈΠΌΠ΅Π½Π°
       .from(ACTOR)
       .orderBy(1, 2)) {
    // ...
}

Ikhodi ikhiqizwa ngesandla ngaphandle kwesakhiwo, noma mathupha kukho konke ukwakha. Isibonelo, ukuvuselelwa okunjalo kungase kulandele ngokushesha ngemva kwalokho Ukufuduka kwesizindalwazi se-Flyway, nakho okungenziwa ngesandla noma ngokuzenzakalelayo.

Ukukhiqizwa kwekhodi yomthombo

Kunamafilosofi ahlukahlukene, izinzuzo kanye nobubi obuhlobene nalezi zindlela zokukhiqiza amakhodi - okwenziwa ngesandla kanye nokuzenzakalelayo - engingeke ngixoxe ngakho ngokuningiliziwe kulesi sihloko. Kodwa, ngokuvamile, iphuzu lonke lekhodi ekhiqiziwe ukuthi ikuvumela ukuthi ukhiqize kabusha ku-Java "iqiniso" esilithatha kalula, ngaphakathi kwesistimu yethu noma ngaphandle kwayo. Ngomqondo othile, abahlanganisi abakhiqiza i-bytecode, ikhodi yomshini, noma olunye uhlobo lwekhodi kusuka kukhodi yomthombo benza into efanayo - sithola ukumelwa "kweqiniso" lethu ngolunye ulimi, ngaphandle kwezizathu ezithile.

Maningi anjalo amakhodi generator. Ngokwesibonelo, I-XJC ingakwazi ukukhiqiza ikhodi ye-Java ngokusekelwe kumafayela e-XSD noma e-WSDL. Isimiso sihlala sifana:

  • Kukhona iqiniso elithile (elingaphakathi noma langaphandle) - isibonelo, ukucaciswa, imodeli yedatha, njll.
  • Sidinga ukumelwa kwasendaweni kwaleli qiniso ngolimi lwethu lokuhlela.

Ngaphezu kwalokho, cishe njalo kuhle ukukhiqiza ukumelela okunjalo - ukuze kugwenywe ukuphinda kusetshenziswe.

Abahlinzeki Bezinhlobo Nokucutshungulwa Kwezichasiselo

Qaphela: Enye indlela, yesimanjemanje futhi eqondile yokwenza amakhodi we-jOOQ ibandakanya ukusetshenziswa kwabahlinzeki bezinhlobo, njengoba zisetshenziswa ku-F#. Kulokhu, ikhodi ikhiqizwa umdidiyeli, empeleni esigabeni sokuhlanganiswa. Empeleni, ikhodi enjalo ayikho ngendlela yamakhodi omthombo. Ku-Java, amathuluzi afanayo, nakuba engenhle, - lawa amaphrosesa wezichasiselo, isibonelo, Chili.

Ngomqondo othile, izinto ezifanayo zenzeka lapha njengasekuqaleni, ngaphandle:

  • Awuyiboni ikhodi ekhiqiziwe (mhlawumbe lesi simo asibonakali sinengeka kangako kumuntu?)
  • Kufanele uqinisekise ukuthi izinhlobo zinganikezwa, okungukuthi, "iqiniso" kufanele lihlale litholakala. Lokhu kulula endabeni ye-Lombok, echaza "iqiniso". Kunzima kakhulu ngamamodeli esizindalwazi ancike ekuxhumekeni okubukhoma okuhlala kutholakala.

Yini inkinga ngokukhiqizwa kwekhodi?

Ngaphezu kombuzo okhohlisayo wokuthi kungcono kanjani ukuqala ukukhiqizwa kwekhodi - ngesandla noma ngokuzenzakalelayo, kufanele ngikhulume ukuthi kukhona abantu abakholelwa ukuthi ukukhiqizwa kwekhodi akudingeki nhlobo. Isizathu salo mbono, engivame ukuhlangana nawo, ukuthi kunzima-ke ukumisa ipayipi lokwakha. Yebo, kunzima ngempela. Kunezindleko ezengeziwe zengqalasizinda. Uma usanda kuqalisa ngomkhiqizo othile (kungaba i-jOOQ, noma i-JAXB, noma i-Hibernate, njll.), kuthatha isikhathi ukusetha ibhentshi lomsebenzi ongathanda ukulisebenzisa ekufundeni i-API ngokwayo ukuze uthole inzuzo kuyo. .

Uma izindleko ezihambisana nokuqonda idivayisi ye-generator ziphezulu kakhulu, khona-ke, ngempela, i-API yenze umsebenzi ompofu ekusetshenzisweni kwe-generator ikhodi (futhi esikhathini esizayo kuvela ukuthi ukwenza ngokwezifiso kuyo nakho kunzima). Ukusebenziseka kufanele kube okuhamba phambili kakhulu kunoma iyiphi i-API enjalo. Kodwa lokho kuyimpikiswano eyodwa nje ephikisana nokukhiqizwa kwekhodi. Uma kungenjalo, bhala ngokuphelele ngesandla ukumelwa kwendawo kweqiniso langaphakathi noma langaphandle.

Abaningi bazothi abanaso isikhathi sokwenza konke lokhu. Bamnqamulajuqu woMkhiqizo wabo Omkhulu. Ngolunye usuku sizohlanganisa ama-conveyors, sizoba nesikhathi. Ngizobaphendula:

Iqiniso kuqala, noma kungani isistimu idinga ukuklanywa ngokusekelwe kusakhiwo sedathabhesi
Okwangempela, U-Alan O'Rourke, Isitaki Sezethameli

Kodwa ku-Hibernate / JPA kulula kakhulu ukubhala ikhodi "ku-Java".

Ngempela. Ku-Hibernate nakubasebenzisi bayo, lokhu kuyisibusiso nesiqalekiso. Ku-Hibernate, ungavele ubhale izinhlangano ezimbalwa, ezinjengalezi:

	@Entity
class Book {
  @Id
  int id;
  String title;
}

Futhi cishe yonke into isimi ngomumo. Manje ingxenye enkulu ye-Hibernate iwukukhiqiza "imininingwane" eyinkimbinkimbi yokuthi leli bhizinisi lizochazwa kanjani ku-DDL "yolimi" lwakho lwe-SQL:

	CREATE TABLE book (
  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  title VARCHAR(50),
 
  CONSTRAINT pk_book PRIMARY KEY (id)
);
 
CREATE INDEX i_book_title ON book (title);

... bese uqala ukusebenzisa uhlelo. Isici esihle kakhulu sokuvuka nokusebenza ngokushesha futhi uzame izinto ezihlukile.

Nokho, ngivumele. Bengiqamba amanga.

  • Ngabe u-Hibernate uzoyisebenzisa ngempela incazelo yalo khiye oyinhloko oqanjwe ngaye?
  • Ingabe u-Hibernate uzodala inkomba kokuthi TITLE? Ngiyazi ngokuqinisekile ukuthi siyayidinga.
  • Ingabe i-Hibernate izokwenza lo khiye ube ukhiye kamazisi ku-Identity Specification?

Cishe cha. Uma uthuthukisa iphrojekthi yakho kusukela ekuqaleni, kuhlale kulungele ukuvele ulahle isizindalwazi esidala bese ukhiqiza entsha ngokushesha nje lapho usungeze izichasiselo ezidingekayo. Ngakho, inkampane Yezincwadi ekugcineni izothatha ifomu:

	@Entity
@Table(name = "book", indexes = {
  @Index(name = "i_book_title", columnList = "title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
  String title;
}

Kuhle. Khiqiza kabusha. Futhi, kulokhu, kuzoba lula kakhulu ekuqaleni.

Kodwa uzoyikhokhela ngokuhamba kwesikhathi.

Ngokushesha noma kamuva kuzodingeka ukuthi uye ekukhiqizeni. Yilapho imodeli iyeka ukusebenza. Ngoba:

Ekukhiqizeni, ngeke kusenzeka, uma kunesidingo, ukulahla i-database endala futhi uqale yonke into kusukela ekuqaleni. Isizindalwazi sakho sizophenduka ifa.

Kusukela manje kuze kube phakade kuzodingeka ubhale Imibhalo yokufuduka ye-DDL, isb. kusetshenziswa i-Flyway. Futhi kuzokwenzekani ngezinhlangano zakho kuleli cala? Ungakwazi ukuzilungisa ngokwakho (futhi uphindwe kabili umthwalo wakho wokusebenza) noma wenze i-Hibernate iphinde ikwenzele yona (angakanani amathuba okuthi leyo ekhiqizwe ngale ndlela ihlangabezane nalokho okulindele?) Ulahlekelwa noma iyiphi indlela.

Ngakho-ke, ngokushesha nje lapho uthuthela ekukhiqizeni, uzodinga ama-patches ashisayo. Futhi zidinga ukulethwa ekukhiqizeni ngokushesha okukhulu. Njengoba ungalungiselelanga futhi ungahlelanga ukuthuthela kwelinye izwe ukuze kukhiqizwe, uchibiyela ngendlela exakile. Futhi-ke awunaso isikhathi sokwenza yonke into kahle. Futhi uyamthethisa uHibernate, ngoba kuhlala kuyiphutha lanoma ngubani, kepha hhayi wena ...

Kunalokho, kusukela ekuqaleni, yonke into yayingenziwa ngendlela ehluke ngokuphelele. Isibonelo, faka amasondo ayindilinga ebhayisikilini.

Isizindalwazi kuqala

"Iqiniso" langempela ku-schema sakho sesizindalwazi kanye "nobukhosi" phezu kwalo lingaphakathi kwesizindalwazi. I-schema ichazwa kuphela ku-database ngokwayo futhi ayikho kwenye indawo, futhi iklayenti ngalinye linekhophi yalesi schema, ngakho-ke kunengqondo ngokuphelele ukuphoqelela ukubambelela ku-schema nobuqotho bawo, ukukwenza kahle ku-database - lapho ulwazi lugcinwa.
Lokhu ubuhlakani obudala noma be-hackneyed. Okhiye abayinhloko nabahlukile bahle. Okhiye bangaphandle balungile. Ukuhlolwa kokuvinjelwa kuhle. Ukuqinisekiswa - Kuhle.

Futhi, akugcini lapho. Isibonelo, usebenzisa i-Oracle, ungafuna ukucacisa:

  • Itafula lakho likuyiphi indawo yetafula
  • Liyini inani lakhe le-PCTFREE
  • Uthini usayizi wenqolobane ngokulandelana kwakho (ngemuva kwe-id)

Konke lokhu kungase kungabi nandaba ezinhlelweni ezincane, kodwa akudingekile ukulinda kuze kube yilapho ukuguqulwa endaweni "yedatha enkulu" - ungaqala ukuzuza ekuthuthukisweni kwesitoreji esinikezwe umthengisi, njengalezo ezishiwo ngenhla, ngaphambili kakhulu. Awekho ama-ORM engiwabonile (okuhlanganisa i-jOOQ) anikeza ukufinyelela kusethi egcwele yezinketho ze-DDL ongase ufune ukuzisebenzisa kusizindalwazi sakho. Ama-ORM anikezela ngamathuluzi athile ukukusiza ukuthi ubhale i-DDL.

Kodwa ekugcineni kosuku, i-schema eklanywe kahle ibhalwe ngesandla ku-DDL. Noma iyiphi i-DDL ekhiqiziwe iwukulinganiselwa kwayo kuphela.

Kuthiwani ngemodeli yeklayenti?

Njengoba kushiwo ngenhla, kuklayenti uzodinga ikhophi ye-schema sesizindalwazi sakho, ukubuka kweklayenti. Akudingekile ukusho ukuthi, lokhu kubuka kweklayenti kufanele kuhambisane nemodeli yangempela. Iyiphi indlela engcono kakhulu yokufeza lokhu? Ngejeneretha yekhodi.

Zonke izingosi zolwazi zinikeza imininingwane yazo nge-SQL. Nansi indlela yokuthola wonke amathebula ngezilimi ezahlukene ze-SQL kusizindalwazi sakho:

	-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server
SELECT table_schema, table_name
FROM information_schema.tables
 
-- DB2
SELECT tabschema, tabname
FROM syscat.tables
 
-- Oracle
SELECT owner, table_name
FROM all_tables
 
-- SQLite
SELECT name
FROM sqlite_master
 
-- Teradata
SELECT databasename, tablename
FROM dbc.tables

Le mibuzo (noma efanayo, kuye ngokuthi kufanele yini ucabangele ukubuka, ukubuka okuphathekayo, imisebenzi enenani lethebula) nayo yenziwa ngokushaya ucingo. DatabaseMetaData.getTables() kusuka ku-JDBC, noma usebenzisa i-meta-module ye-jOOQ.

Kusukela emiphumeleni yaleyo mibuzo, kulula ngokuqhathaniswa ukukhiqiza noma yikuphi ukumelwa ohlangothini lweklayenti lwemodeli yakho yesizindalwazi, kungakhathaliseki ukuthi usebenzisa luphi ubuchwepheshe kuklayenti.

  • Uma usebenzisa i-JDBC noma i-Spring ungakha isethi yezintambo ezingaguquki
  • Uma usebenzisa i-JPA, ungazenzela amabhizinisi ngokwawo
  • Uma usebenzisa i-jOOQ ungakha imodeli ye-meta ye-jOOQ

Kuye ngokuthi iklayenti lakho i-API inikeza amandla angakanani (isb. i-jOOQ noma i-JPA), imodeli ye-meta ekhiqiziwe ingaba inothe ngempela futhi iphelele. Thatha, ngokwesibonelo, amathuba okujoyina okungaqondile, yethulwe ku-jOOQ 3.11, encike olwazini olukhiqiziwe lwe-meta mayelana nobudlelwano bokhiye bangaphandle phakathi kwamathebula akho.

Manje noma yikuphi ukukhushulwa kwesizindalwazi kuzobuyekeza ngokuzenzakalelayo ikhodi yeklayenti. Cabanga nje ngesibonelo:

ALTER TABLE book RENAME COLUMN title TO book_title;

Ungathanda ngempela ukwenza lo msebenzi kabili? Akukho lutho. Sivele sibophe i-DDL, siyiqhube ngepayipi lakho lokwakha, futhi sithole ibhizinisi elibuyekeziwe:

@Entity
@Table(name = "book", indexes = {
 
  // Π’Ρ‹ ΠΎΠ± этом Π·Π°Π΄ΡƒΠΌΡ‹Π²Π°Π»ΠΈΡΡŒ?
  @Index(name = "i_book_title", columnList = "book_title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
 
  @Column("book_title")
  String bookTitle;
}

Noma ikilasi elibuyekeziwe le-jOOQ. Izinguquko eziningi ze-DDL nazo zithinta i-semantics, hhayi nje i-syntax. Ngakho-ke, kungaba lula ukubona kukhodi ehlanganisiwe ukuthi iyiphi ikhodi ezothikamezeka (noma engathinteka) ngokukhuphula isizindalwazi sakho.

Iqiniso kuphela

Kungakhathalekile ukuthi usebenzisa luphi ubuchwepheshe, kuhlala kunemodeli eyodwa okuwukuphela komthombo weqiniso wesistimu ethile engaphansi - noma okungenani kufanele silwele lokhu futhi sigweme ukudideka kwebhizinisi lapho "iqiniso" likhona yonke indawo futhi lingekho ndawo ngesikhathi esisodwa. Konke kungaba lula kakhulu. Uma uvele ushintshashintsha amafayela e-XML ngolunye uhlelo, vele usebenzise i-XSD. Bheka i-meta-model ye-jOOQ ye-INFORMATION_SCHEMA ngefomu le-XML:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • I-XSD iqondwa kahle
  • I-XSD imaka okuqukethwe kwe-XML kahle kakhulu futhi ivumela ukuqinisekiswa kuzo zonke izilimi zamaklayenti
  • I-XSD iguqulelwe kahle futhi iyahambisana kakhulu
  • I-XSD ingahunyushelwa kukhodi ye-Java kusetshenziswa i-XJC

Iphuzu lokugcina libalulekile. Uma sixhumana nesistimu yangaphandle sisebenzisa imilayezo ye-XML, sifuna ukuqiniseka ukuthi imilayezo yethu ivumelekile. Lokhu kulula kakhulu ukukufeza nge-JAXB, XJC ne-XSD. Kungaba ubuhlanya nje ukucabanga ukuthi, ngendlela yokuqala ye-Java yokuklama lapho senza khona imilayezo yethu njengezinto ze-Java, inganikezwa ngendlela eqondakalayo ku-XML futhi ithunyelwe ukuthi isetshenziswe kwenye isistimu. I-XML ekhiqizwa ngale ndlela izoba eyekhwalithi embi kakhulu, ingenawo amadokhumentari, futhi kube nzima ukuyithuthukisa. Uma bekunesivumelwano ngezinga lekhwalithi yesevisi (i-SLA) kusixhumi esibonakalayo esinjalo, besingakufipha ngokushesha.

Ukukhuluma iqiniso, yilokhu okwenzeka ngaso sonke isikhathi nge-JSON API, kodwa leyo enye indaba, ngizoyiphikisa ngokuzayo ...

Imininingo egciniwe: ziyefana

Ukusebenza ngemininingwane, uyaqonda ukuthi zonke ziyefana. Isizindalwazi siphethe idatha yayo futhi kufanele silawule i-schema. Noma yiziphi izinguquko ezenziwe ku-schema kufanele zisetshenziswe ngokuqondile ku-DDL ukuze umthombo owodwa weqiniso ubuyekezwe.

Lapho ukubuyekezwa komthombo kwenzekile, wonke amaklayenti kufanele futhi abuyekeze amakhophi awo emodeli. Amanye amaklayenti angase abhalwe nge-Java kusetshenziswa i-jOOQ ne-Hibernate noma i-JDBC (noma kokubili). Amanye amaklayenti angabhalwa nge-Perl (ake siwafisele inhlanhla), amanye ku-C#. Akunandaba. Imodeli eyinhloko ikusizindalwazi. Amamodeli akhiqizwe nge-ORM avamise ukuba nekhwalithi embi, abhalwe phansi, futhi kunzima ukuwathuthukisa.

Ngakho ungawenzi amaphutha. Ungawenzi amaphutha zisuka nje. Sebenza kusizindalwazi. Yakha ipayipi lokuphakela elingakwazi ukuzenzela. Nika amandla abakhiqizi bekhodi ukuze bakopishe kalula imodeli yakho yesizindalwazi bese uyilahla kumakhasimende. Futhi yeka ukukhathazeka ngamakhodi generator. Bahle. Ngazo, uzokhiqiza kakhulu. Odinga ukukwenza nje ukuchitha isikhathi esincane uzihlela zisuka nje, futhi uzoba neminyaka yokusebenza okuthuthukisiwe ukuze wakhe indaba yephrojekthi yakho.

Ungangibongi okwamanje, kamuva.

Ukuchazwa

Ukuze kucace: Lesi sihloko asikhuthazi nganoma iyiphi indlela ukuthi lonke uhlelo (okungukuthi, isizinda, ingqondo yebhizinisi, njll., njll.) kudingeka luguqulwe ukuze lulingane nemodeli yakho yesizindalwazi. Engikhuluma ngakho kulesi sihloko ukuthi ikhodi yeklayenti esebenzisana nesizindalwazi kufanele isebenze ngesisekelo semodeli yesizindalwazi ukuze ingakhiqizi kabusha imodeli yesizindalwazi esimweni "sekilasi lokuqala". Ukuqonda okunjalo kuvame ukutholakala kusendlalelo sokufinyelela idatha kuklayenti lakho.

Ezakhiweni ezisezingeni ezimbili, ezisagcinwe kwezinye izindawo, imodeli yesistimu enjalo ingase ibe yodwa engenzeka. Kodwa-ke, kumasistimu amaningi, isendlalelo sokufinyelela idatha kimi sibonakala "siyisistimu engaphansi" ehlanganisa imodeli yedathabheyisi.

Ukungafani

Kukhona okuhlukile kuyo yonke imithetho, futhi ngike ngasho ngaphambili ukuthi indlela yokukhiqiza ikhodi yomthombo yokuqala neyomthombo ngezinye izikhathi ingaba engafanele. Nakhu okuhlukile okumbalwa okunjalo (mhlawumbe kukhona okunye):

  • Lapho i-schema ingaziwa futhi idinga ukuvulwa. Isibonelo, uhlinzeka ngethuluzi lokusiza abasebenzisi ukuthi bazulazule kunoma yimuphi umdwebo. Phew. Akukho ukukhiqiza ikhodi lapha. Kodwa noma kunjalo - i-database kuqala kunakho konke.
  • Lapho isekethe idinga ukwenziwa ngokundiza ukuxazulula inkinga ethile. Lesi sibonelo sibonakala siyinguqulo encane yephethini ivelu yesibaluli sebhizinisi, okungukuthi, awunaso ngempela i-schema esichazwe kahle. Kulokhu, ngokuvamile awukwazi ngisho nokuqiniseka nhlobo ukuthi i-RDBMS izofanela wena.

Okuhlukile ngokwemvelo kuhlukile. Ezimweni eziningi ezibandakanya ukusetshenziswa kwe-RDBMS, i-schema saziwa kusengaphambili, singaphakathi kwe-RDBMS futhi siwukuphela komthombo "weqiniso", futhi wonke amaklayenti kufanele athole amakhophi asuselwe kuso. Okufanelekile, lokhu kufanele kufake i-generator ikhodi.

Source: www.habr.com

Engeza amazwana