JavaRush /Java Blog /Random-TL /JDBC o kung saan nagsisimula ang lahat

JDBC o kung saan nagsisimula ang lahat

Nai-publish sa grupo
Sa modernong mundo, walang paraan kung walang imbakan ng data. At ang kasaysayan ng pagtatrabaho sa mga database ay nagsimula nang napakatagal na ang nakalipas, sa pagdating ng JDBC. Iminumungkahi kong tandaan ang isang bagay na walang makabagong balangkas na itinayo sa ibabaw ng JDBC ay magagawa nang wala. Bilang karagdagan, kahit na nagtatrabaho kasama sila, kung minsan ay maaaring kailanganin mo ang pagkakataong "bumalik sa iyong pinagmulan." Umaasa ako na ang pagsusuri na ito ay makakatulong bilang isang panimula o makakatulong sa pag-refresh ng iyong memorya.
JDBC o kung saan nagsisimula ang lahat - 1

Panimula

Ang isa sa mga pangunahing layunin ng isang programming language ay ang pag-iimbak at pagproseso ng impormasyon. Upang mas maunawaan kung paano gumagana ang pag-iimbak ng data, sulit na gumugol ng kaunting oras sa teorya at arkitektura ng mga application. Halimbawa, maaari mong basahin ang literatura, katulad ng aklat na " Software Architect's Handbook: Maging isang matagumpay na software architect sa pamamagitan ng pagpapatupad ng epektibong arch... " ni Joseph Ingeno. Gaya ng sinabi, mayroong isang tiyak na Tier ng Data o "Layer ng Data". Kabilang dito ang isang lugar upang mag-imbak ng data (halimbawa, isang SQL database) at mga tool para sa pagtatrabaho sa isang data store (halimbawa, JDBC, na tatalakayin). Mayroon ding artikulo sa website ng Microsoft: " Pagdidisenyo ng isang layer ng pagtitiyaga ng imprastraktura ," na naglalarawan sa solusyon sa arkitektura ng paghihiwalay ng karagdagang layer mula sa Data Tier - ang Persistence Layer. Sa kasong ito, ang Data Tier ay ang antas ng storage ng data mismo, habang ang Persistence Layer ay ilang antas ng abstraction para sa pagtatrabaho sa data mula sa storage mula sa Data Tier level. Maaaring kasama sa Persistence Layer ang template na "DAO" o iba't ibang ORM. Ngunit ang ORM ay isang paksa para sa isa pang talakayan. Tulad ng maaaring naintindihan mo na, unang lumitaw ang Data Tier. Mula noong panahon ng JDK 1.1, JDBC (Java DataBase Connectivity - koneksyon sa mga database sa Java) ay lumitaw sa mundo ng Java. Ito ay isang pamantayan para sa pakikipag-ugnayan ng mga Java application sa iba't ibang DBMS, na ipinatupad sa anyo ng mga java.sql at javax.sql na pakete na kasama sa Java SE:
JDBC o kung saan nagsisimula ang lahat - 2
Ang pamantayang ito ay inilalarawan ng detalyeng " JSR 221 JDBC 4.1 API ". Sinasabi sa amin ng detalyeng ito na ang JDBC API ay nagbibigay ng programmatic na access sa mga relational database mula sa mga program na nakasulat sa Java. Sinasabi rin nito na ang JDBC API ay bahagi ng Java platform at samakatuwid ay kasama sa Java SE at Java EE. Ang JDBC API ay ibinibigay sa dalawang pakete: java.sql at javax.sql. Kilalanin natin sila.
JDBC o kung saan nagsisimula ang lahat - 3

Simula ng trabaho

Upang maunawaan kung ano ang JDBC API sa pangkalahatan, kailangan namin ng Java application. Ito ay pinaka-maginhawa upang gamitin ang isa sa mga sistema ng pagpupulong ng proyekto. Halimbawa, gamitin natin ang Gradle . Maaari kang magbasa nang higit pa tungkol sa Gradle sa isang maikling pagsusuri: " Isang Maikling Panimula sa Gradle ". Una, magsimula tayo ng bagong proyekto ng Gradle. Dahil ang pagpapagana ng Gradle ay ipinatupad sa pamamagitan ng mga plugin, kailangan naming gamitin ang " Gradle Build Init Plugin " para sa pagsisimula:
gradle init --type java-application
Pagkatapos nito, buksan natin ang build script - ang build.gradle file , na naglalarawan sa aming proyekto at kung paano ito gagawin. Interesado kami sa block na " dependencies ", kung saan inilalarawan ang mga dependency - iyon ay, ang mga library/frameworks/api, kung wala ito hindi tayo gagana at kung saan tayo umaasa. Bilang default, makakakita tayo ng tulad ng:
dependencies {
    // This dependency is found on compile classpath of this component and consumers.
    implementation 'com.google.guava:guava:26.0-jre'
    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}
Bakit natin ito nakikita dito? Ito ang mga dependency ng aming proyekto na awtomatikong nabuo ng Gradle para sa amin kapag gumagawa ng proyekto. At dahil din ang bayabas ay isang hiwalay na aklatan na hindi kasama sa Java SE. Hindi rin kasama ang JUnit sa Java SE. Ngunit mayroon kaming JDBC out of the box, iyon ay, ito ay bahagi ng Java SE. May JDBC pala kami. Malaki. Ano pa ang kailangan natin? Mayroong napakagandang diagram:
JDBC o kung saan nagsisimula ang lahat - 4
Tulad ng nakikita natin, at ito ay lohikal, ang database ay isang panlabas na bahagi na hindi katutubong sa Java SE. Ito ay ipinaliwanag nang simple - mayroong isang malaking bilang ng mga database at maaari kang magtrabaho sa alinman. Halimbawa, mayroong PostgreSQL, Oracle, MySQL, H2. Ang bawat isa sa mga database na ito ay ibinibigay ng isang hiwalay na kumpanya na tinatawag na database vendor. Ang bawat database ay nakasulat sa sarili nitong programming language (hindi kinakailangang Java). Upang makapagtrabaho sa database mula sa isang Java application, ang database provider ay nagsusulat ng isang espesyal na driver, na kung saan ay ang sarili nitong adapter ng imahe. Ang mga naturang JDBC compatible (iyon ay, ang mga may JDBC driver) ay tinatawag ding "JDBC-Compliant Database". Dito maaari tayong gumuhit ng isang pagkakatulad sa mga aparatong computer. Halimbawa, sa isang notepad mayroong isang "Print" na pindutan. Sa tuwing pinindot mo ito, sasabihin ng program sa operating system na gustong i-print ng notepad application. At mayroon kang isang printer. Upang turuan ang iyong operating system na makipag-usap nang pantay sa isang Canon o HP printer, kakailanganin mo ng iba't ibang mga driver. Ngunit para sa iyo, bilang isang gumagamit, walang magbabago. Pipindutin mo pa rin ang parehong pindutan. Ganun din sa JDBC. Gumagamit ka ng parehong code, ito ay lamang na ang iba't ibang mga database ay maaaring tumatakbo sa ilalim ng hood. Sa tingin ko ito ay isang napakalinaw na diskarte. Ang bawat naturang driver ng JDBC ay ilang uri ng artifact, library, jar file. Ito ang dependency para sa aming proyekto. Halimbawa, maaari naming piliin ang database na " H2 Database " at pagkatapos ay kailangan naming magdagdag ng dependency tulad nito:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
Kung paano makahanap ng dependency at kung paano ilarawan ito ay ipinahiwatig sa mga opisyal na website ng database provider o sa " Maven Central ". Ang driver ng JDBC ay hindi isang database, tulad ng naiintindihan mo. Ngunit siya ay gabay lamang dito. Ngunit mayroong isang bagay tulad ng " Sa mga database ng memorya ". Ito ang mga database na umiiral sa memorya para sa buhay ng iyong aplikasyon. Kadalasan, ito ay kadalasang ginagamit para sa mga layunin ng pagsubok o pagsasanay. Pinapayagan ka nitong maiwasan ang pag-install ng isang hiwalay na server ng database sa makina. Na bagay na bagay para makilala natin ang JDBC. Kaya handa na ang sandbox namin at magsisimula na kami.
JDBC o kung saan nagsisimula ang lahat - 5

Koneksyon

Kaya, mayroon kaming driver ng JDBC, mayroon kaming JDBC API. Tulad ng naaalala namin, ang JDBC ay kumakatawan sa Java DataBase Connectivity. Samakatuwid, ang lahat ay nagsisimula sa Connectivity - ang kakayahang magtatag ng isang koneksyon. At ang koneksyon ay Koneksyon. Muli nating buksan ang teksto ng detalye ng JDBC at tingnan ang talaan ng mga nilalaman. Sa kabanata " CHAPTER 4 Overview " (pangkalahatang-ideya) bumaling tayo sa seksyong " 4.1 Establishing a Connection " (pagtatatag ng isang koneksyon) sinasabing mayroong dalawang paraan upang kumonekta sa database:
  • Sa pamamagitan ng DriverManager
  • Sa pamamagitan ng DataSource
Makipag-usap tayo sa DriverManager. Tulad ng sinabi, pinapayagan ka ng DriverManager na kumonekta sa database sa tinukoy na URL, at naglo-load din ng mga JDBC Driver na natagpuan nito sa CLASSPATH (at bago, bago ang JDBC 4.0, kailangan mong i-load ang klase ng driver mismo). Mayroong isang hiwalay na kabanata na "CHAPTER 9 Connections" tungkol sa pagkonekta sa database. Interesado kami sa kung paano makakuha ng koneksyon sa pamamagitan ng DriverManager, kaya interesado kami sa seksyong "9.3 The DriverManager Class". Ipinapahiwatig nito kung paano namin maa-access ang database:
Connection con = DriverManager.getConnection(url, user, passwd);
Ang mga parameter ay maaaring makuha mula sa website ng database na aming pinili. Sa aming kaso, ito ay H2 - " H2 Cheat Sheet ". Lumipat tayo sa klase ng AppTest na inihanda ni Gradle. Naglalaman ito ng mga pagsubok sa JUnit. Ang JUnit test ay isang paraan na minarkahan ng annotation @Test. Ang mga pagsubok sa yunit ay hindi ang paksa ng pagsusuri na ito, kaya lilimitahan lang namin ang aming sarili sa pag-unawa na ang mga ito ay mga pamamaraan na inilarawan sa isang tiyak na paraan, ang layunin nito ay upang subukan ang isang bagay. Ayon sa detalye ng JDBC at sa website ng H2, susuriin namin na nakatanggap kami ng koneksyon sa database. Sumulat tayo ng isang paraan para sa pagkuha ng isang koneksyon:
private Connection getNewConnection() throws SQLException {
	String url = "jdbc:h2:mem:test";
	String user = "sa";
	String passwd = "sa";
	return DriverManager.getConnection(url, user, passwd);
}
Ngayon magsulat tayo ng isang pagsubok para sa pamamaraang ito na susuriin kung ang koneksyon ay aktwal na naitatag:
@Test
public void shouldGetJdbcConnection() throws SQLException {
	try(Connection connection = getNewConnection()) {
		assertTrue(connection.isValid(1));
		assertFalse(connection.isClosed());
	}
}
Ang pagsubok na ito, kapag naisakatuparan, ay magpapatunay na ang resultang koneksyon ay wasto (nagawa nang tama) at hindi ito sarado. Sa pamamagitan ng paggamit ng try-with-resources, maglalabas kami ng mga mapagkukunan kapag hindi na namin kailangan ang mga ito. Poprotektahan tayo nito mula sa lumulubog na mga koneksyon at pagtagas ng memorya. Dahil nangangailangan ng koneksyon ang anumang mga aksyon na may database, ibigay natin ang natitirang mga pamamaraan ng pagsubok na may markang @Test na may Koneksyon sa simula ng pagsubok, na ilalabas namin pagkatapos ng pagsubok. Upang gawin ito, kailangan namin ng dalawang anotasyon: @Before at @After Magdagdag tayo ng bagong field sa klase ng AppTest na mag-iimbak ng koneksyon ng JDBC para sa mga pagsubok:
private static Connection connection;
At magdagdag tayo ng mga bagong pamamaraan:
@Before
public void init() throws SQLException {
	connection = getNewConnection();
}
@After
public void close() throws SQLException {
	connection.close();
}
Ngayon, ang anumang paraan ng pagsubok ay garantisadong magkaroon ng koneksyon sa JDBC at hindi na kailangang likhain ito sa bawat oras.
JDBC o kung saan nagsisimula ang lahat - 6

Mga pahayag

Susunod na interesado kami sa Mga Pahayag o mga expression. Ang mga ito ay inilarawan sa dokumentasyon sa kabanata " KABANATA 13 Mga Pahayag ". Una, sinasabi nito na mayroong ilang uri o uri ng mga pahayag:
  • Pahayag: SQL expression na walang mga parameter
  • PreparedStatement : Inihanda ang SQL statement na naglalaman ng mga parameter ng input
  • CallableStatement: SQL expression na may kakayahang makakuha ng return value mula sa SQL Stored Procedures.
Kaya, sa pagkakaroon ng isang koneksyon, maaari kaming magsagawa ng ilang kahilingan sa loob ng balangkas ng koneksyon na ito. Samakatuwid, lohikal na sa simula ay kumuha kami ng isang halimbawa ng SQL expression mula sa Connection. Kailangan mong magsimula sa pamamagitan ng paglikha ng isang talahanayan. Ilarawan natin ang kahilingan sa paglikha ng talahanayan bilang isang String variable. Paano ito gagawin? Gumamit tayo ng ilang tutorial tulad ng " sqltutorial.org ", " sqlbolt.com ", " postgresqltutorial.com ", " codecademy.com ". Gamitin natin, halimbawa, ang isang halimbawa mula sa kursong SQL sa khanacademy.org . Magdagdag tayo ng isang paraan para sa pagpapatupad ng isang expression sa database:
private int executeUpdate(String query) throws SQLException {
	Statement statement = connection.createStatement();
	// Для Insert, Update, Delete
	int result = statement.executeUpdate(query);
	return result;
}
Magdagdag tayo ng isang paraan para sa paglikha ng isang talahanayan ng pagsubok gamit ang nakaraang pamamaraan:
private void createCustomerTable() throws SQLException {
	String customerTableQuery = "CREATE TABLE customers " +
                "(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)";
	String customerEntryQuery = "INSERT INTO customers " +
                "VALUES (73, 'Brian', 33)";
	executeUpdate(customerTableQuery);
	executeUpdate(customerEntryQuery);
}
Ngayon subukan natin ito:
@Test
public void shouldCreateCustomerTable() throws SQLException {
	createCustomerTable();
	connection.createStatement().execute("SELECT * FROM customers");
}
Ngayon, isagawa natin ang kahilingan, at kahit na may isang parameter:
@Test
public void shouldSelectData() throws SQLException {
 	createCustomerTable();
 	String query = "SELECT * FROM customers WHERE name = ?";
	PreparedStatement statement = connection.prepareStatement(query);
	statement.setString(1, "Brian");
	boolean hasResult = statement.execute();
	assertTrue(hasResult);
}
Hindi sinusuportahan ng JDBC ang mga pinangalanang parameter para sa PreparedStatement, kaya ang mga parameter mismo ay tinukoy ng mga tanong, at sa pamamagitan ng pagtukoy sa halaga ay ipinapahiwatig namin ang index ng tanong (simula sa 1, hindi zero). Sa huling pagsubok na natanggap namin ang totoo bilang indikasyon kung may resulta. Ngunit paano kinakatawan ang resulta ng query sa JDBC API? At ito ay ipinakita bilang isang ResultSet.
JDBC o kung saan nagsisimula ang lahat - 7

ResultaSet

Ang konsepto ng isang ResultSet ay inilarawan sa detalye ng JDBC API sa kabanata na "CHAPTER 15 Result Sets". Una sa lahat, sinasabi nito na ang ResultSet ay nagbibigay ng mga pamamaraan para sa pagkuha at pagmamanipula ng mga resulta ng mga naisagawang query. Iyon ay, kung ang execute na paraan ay ibinalik na totoo sa amin, pagkatapos ay makakakuha tayo ng ResultSet. Ilipat natin ang tawag sa paraan ng createCustomerTable() sa paraan ng init, na minarkahan bilang @Before. Ngayon, tapusin natin ang ating shouldSelectData test:
@Test
public void shouldSelectData() throws SQLException {
	String query = "SELECT * FROM customers WHERE name = ?";
	PreparedStatement statement = connection.prepareStatement(query);
	statement.setString(1, "Brian");
	boolean hasResult = statement.execute();
	assertTrue(hasResult);
	// Обработаем результат
	ResultSet resultSet = statement.getResultSet();
	resultSet.next();
	int age = resultSet.getInt("age");
	assertEquals(33, age);
}
Ito ay nagkakahalaga na tandaan dito na ang susunod ay isang paraan na gumagalaw sa tinatawag na "cursor". Ang cursor sa ResultSet ay tumuturo sa ilang row. Kaya, upang mabasa ang isang linya, kailangan mong ilagay ang mismong cursor dito. Kapag ang cursor ay inilipat, ang cursor move method ay nagbabalik ng true kung ang cursor ay wasto (tama, tama), ibig sabihin, ito ay tumuturo sa data. Kung ito ay nagbabalik ng false, pagkatapos ay walang data, iyon ay, ang cursor ay hindi tumuturo sa data. Kung susubukan naming kumuha ng data gamit ang isang di-wastong cursor, makukuha namin ang error: Walang available na data.
@Test
public void shouldInsertInResultSet() throws SQLException {
	Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
	ResultSet resultSet = statement.executeQuery("SELECT * FROM customers");
	resultSet.moveToInsertRow();
	resultSet.updateLong("id", 3L);
	resultSet.updateString("name", "John");
	resultSet.updateInt("age", 18);
	resultSet.insertRow();
	resultSet.moveToCurrentRow();
}

RowSet

Bilang karagdagan sa ResultSet, ipinakilala ng JDBC ang konsepto ng RowSet. Maaari kang magbasa ng higit pa dito: " JDBC Basics: Using RowSet Objects ". Mayroong iba't ibang mga pagkakaiba-iba ng paggamit. Halimbawa, ang pinakasimpleng kaso ay maaaring magmukhang ganito:
@Test
public void shouldUseRowSet() throws SQLException {
 	JdbcRowSet jdbcRs = new JdbcRowSetImpl(connection);
 	jdbcRs.setCommand("SELECT * FROM customers");
	jdbcRs.execute();
	jdbcRs.next();
	String name = jdbcRs.getString("name");
	assertEquals("Brian", name);
}
Tulad ng nakikita mo, ang RowSet ay katulad ng isang symbiosis ng pahayag (tinukoy namin ang utos sa pamamagitan nito) at naisakatuparan ang utos. Sa pamamagitan nito kinokontrol namin ang cursor (sa pamamagitan ng pagtawag sa susunod na paraan) at kumuha ng data mula dito. Hindi lamang ang diskarte na ito ay kawili-wili, kundi pati na rin ang mga posibleng pagpapatupad. Halimbawa, CachedRowSet. Ito ay "nakadiskonekta" (iyon ay, hindi ito gumagamit ng patuloy na koneksyon sa database) at nangangailangan ng tahasang pag-synchronize sa database:
CachedRowSet jdbcRsCached = new CachedRowSetImpl();
jdbcRsCached.acceptChanges(connection);
Maaari kang magbasa nang higit pa sa tutorial sa website ng Oracle: " Gamit ang CachedRowSetObjects ".
JDBC o kung saan nagsisimula ang lahat - 8

Metadata

Bilang karagdagan sa mga query, ang isang koneksyon sa database (ibig sabihin, isang instance ng klase ng Connection) ay nagbibigay ng access sa metadata - data tungkol sa kung paano na-configure at nakaayos ang aming database. Ngunit una, banggitin natin ang ilang mahahalagang punto: Ang URL para sa pagkonekta sa aming database: “jdbc:h2:mem:test”. pagsubok ang pangalan ng aming database. Para sa JDBC API, ito ay isang direktoryo. At ang pangalan ay nasa uppercase, iyon ay, TEST. Ang default na schema para sa H2 ay PUBLIC. Ngayon, magsulat tayo ng isang pagsubok na nagpapakita ng lahat ng mga talahanayan ng user. Bakit custom? Dahil ang mga database ay naglalaman ng hindi lamang mga talahanayan ng gumagamit (ang mga mismong nilikha namin gamit ang mga expression ng paglikha ng talahanayan), kundi pati na rin ang mga talahanayan ng system. Ang mga ito ay kinakailangan upang mag-imbak ng impormasyon ng system tungkol sa istraktura ng database. Ang bawat database ay maaaring mag-imbak ng naturang mga talahanayan ng system nang iba. Halimbawa, sa H2 sila ay nakaimbak sa " INFORMATION_SCHEMA " schema. Kapansin-pansin, ang INFORMATION SCHEMA ay isang karaniwang diskarte, ngunit ang Oracle ay nagpunta sa ibang ruta. Maaari kang magbasa ng higit pa dito: " INFORMATION_SCHEMA at Oracle ". Sumulat tayo ng pagsubok na tumatanggap ng metadata sa mga talahanayan ng user:
@Test
public void shoudGetMetadata() throws SQLException {
	// У нас URL = "jdbc:h2:mem:test", где test - название БД
	// Название БД = catalog
	DatabaseMetaData metaData = connection.getMetaData();
	ResultSet result = metaData.getTables("TEST", "PUBLIC", "%", null);
	List<String> tables = new ArrayList<>();
	while(result.next()) {
		tables.add(result.getString(2) + "." + result.getString(3));
	}
	assertTrue(tables.contains("PUBLIC.CUSTOMERS"));
}
JDBC o kung saan nagsisimula ang lahat - 9

Pool ng koneksyon

Ang pool ng koneksyon sa detalye ng JDBC ay may seksyong tinatawag na "Chapter 11 Connection Pooling". Nagbibigay din ito ng pangunahing katwiran para sa pangangailangan para sa isang pool ng koneksyon. Ang bawat Coonection ay isang pisikal na koneksyon sa database. Ang paglikha at pagsasara nito ay isang "mahal" na trabaho. Nagbibigay lang ang JDBC ng connection pooling API. Samakatuwid, ang pagpili ng pagpapatupad ay nananatiling atin. Halimbawa, kasama sa mga naturang pagpapatupad ang HikariCP . Alinsunod dito, kakailanganin naming magdagdag ng pool sa aming dependency sa proyekto:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
    implementation 'com.zaxxer:HikariCP:3.3.1'
    testImplementation 'junit:junit:4.12'
}
Ngayon ay kailangan nating gamitin ang pool na ito. Upang gawin ito, kailangan mong simulan ang data source, na kilala rin bilang Datasource:
private DataSource getDatasource() {
	HikariConfig config = new HikariConfig();
	config.setUsername("sa");
	config.setPassword("sa");
	config.setJdbcUrl("jdbc:h2:mem:test");
	DataSource ds = new HikariDataSource(config);
	return ds;
}
At magsulat tayo ng pagsubok para makatanggap ng koneksyon mula sa pool:
@Test
public void shouldGetConnectionFromDataSource() throws SQLException {
	DataSource datasource = getDatasource();
	try (Connection con = datasource.getConnection()) {
		assertTrue(con.isValid(1));
	}
}
JDBC o kung saan nagsisimula ang lahat - 10

Mga transaksyon

Isa sa mga pinakakawili-wiling bagay tungkol sa JDBC ay ang mga transaksyon. Sa detalye ng JDBC, itinalaga sa kanila ang kabanata na "CHAPTER 10 Transactions". Una sa lahat, ito ay nagkakahalaga ng pag-unawa kung ano ang isang transaksyon. Ang isang transaksyon ay isang pangkat ng mga lohikal na pinagsamang sunud-sunod na mga operasyon sa data, naproseso o nakansela sa kabuuan. Kailan magsisimula ang isang transaksyon kapag gumagamit ng JDBC? Gaya ng nakasaad sa detalye, ito ay direktang pinangangasiwaan ng JDBC Driver. Ngunit kadalasan, nagsisimula ang isang bagong transaksyon kapag ang kasalukuyang SQL statement ay nangangailangan ng isang transaksyon at ang transaksyon ay hindi pa nagagawa. Kailan matatapos ang transaksyon? Ito ay kinokontrol ng auto-commit attribute. Kung naka-enable ang autocommit, makukumpleto ang transaksyon pagkatapos na "makumpleto" ang SQL statement. Ang ibig sabihin ng "tapos" ay depende sa uri ng SQL expression:
  • Data Manipulation Language, na kilala rin bilang DML (Insert, Update, Delete)
    Ang transaksyon ay nakumpleto sa sandaling makumpleto ang aksyon
  • Piliin ang Mga Pahayag
    Ang transaksyon ay nakumpleto kapag ang ResultSet ay sarado ( ResultSet#close )
  • CallableStatement at mga expression na nagbabalik ng maraming resulta
    Kapag ang lahat ng nauugnay na ResultSets ay naisara at lahat ng output ay natanggap (kabilang ang bilang ng mga update)
Ito ay eksakto kung paano kumikilos ang JDBC API. Gaya ng dati, sumulat tayo ng pagsubok para dito:
@Test
public void shouldCommitTransaction() throws SQLException {
	connection.setAutoCommit(false);
	String query = "INSERT INTO customers VALUES (1, 'Max', 20)";
	connection.createStatement().executeUpdate(query);
	connection.commit();
	Statement statement = connection.createStatement();
 	statement.execute("SELECT * FROM customers");
	ResultSet resultSet = statement.getResultSet();
	int count = 0;
	while(resultSet.next()) {
		count++;
	}
	assertEquals(2, count);
}
Simple lang. Ngunit ito ay totoo hangga't mayroon lamang tayong isang transaksyon. Ano ang gagawin kapag marami sa kanila? Kailangan nilang ihiwalay sa isa't isa. Samakatuwid, pag-usapan natin ang tungkol sa mga antas ng paghihiwalay ng transaksyon at kung paano sila makitungo sa JDBC.
JDBC o kung saan nagsisimula ang lahat - 11

Mga antas ng pagkakabukod

Buksan natin ang subsection na "10.2 Transaction Isolation Levels" ng detalye ng JDBC. Dito, bago magpatuloy, nais kong tandaan ang tungkol sa isang bagay tulad ng ACID. Inilalarawan ng ACID ang mga kinakailangan para sa isang transactional system.
  • Atomicity:
    Walang transaksyon ang bahagyang gagawin sa system. Alinman sa lahat ng mga sub-operasyon nito ay isasagawa, o walang isasagawa.
  • Consistency:
    Ang bawat matagumpay na transaksyon, ayon sa kahulugan, ay nagtatala lamang ng mga wastong resulta.
  • Paghihiwalay:
    Habang tumatakbo ang isang transaksyon, hindi dapat makaapekto ang mga kasabay na transaksyon sa kinalabasan nito.
  • Katatagan:
    Kung matagumpay na nakumpleto ang isang transaksyon, ang mga pagbabagong ginawa dito ay hindi mababawi dahil sa anumang pagkabigo.
Kapag pinag-uusapan ang mga antas ng paghihiwalay ng transaksyon, pinag-uusapan natin ang pangangailangang "Paghihiwalay". Ang paghihiwalay ay isang mamahaling pangangailangan, kaya sa mga totoong database ay may mga mode na hindi ganap na naghihiwalay ng isang transaksyon (Repeatable Read isolation level at mas mababa). Ang Wikipedia ay may mahusay na paliwanag sa mga problema na maaaring lumitaw kapag nagtatrabaho sa mga transaksyon. Ito ay nagkakahalaga ng pagbabasa ng higit pa dito: " Mga problema sa parallel na pag-access gamit ang mga transaksyon ." Bago namin isulat ang aming pagsubok, baguhin natin nang bahagya ang aming Gradle Build Script: magdagdag ng isang bloke na may mga katangian, iyon ay, kasama ang mga setting ng aming proyekto:
ext {
    h2Version = '1.3.176' // 1.4.177
    hikariVersion = '3.3.1'
    junitVersion = '4.12'
}
Susunod, ginagamit namin ito sa mga bersyon:
dependencies {
    implementation "com.h2database:h2:${h2Version}"
    implementation "com.zaxxer:HikariCP:${hikariVersion}"
    testImplementation "junit:junit:${junitVersion}"
}
Maaaring napansin mo na ang h2 na bersyon ay naging mas mababa. Malalaman natin kung bakit mamaya. Kaya paano mo ilalapat ang mga antas ng paghihiwalay? Tingnan natin kaagad ang isang maliit na praktikal na halimbawa:
@Test
public void shouldGetReadUncommited() throws SQLException {
	Connection first = getNewConnection();
	assertTrue(first.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED));
	first.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
	first.setAutoCommit(false);
	// Транзакиця на подключение. Поэтому первая транзакция с ReadUncommited вносит изменения
	String insertQuery = "INSERT INTO customers VALUES (5, 'Max', 15)";
	first.createStatement().executeUpdate(insertQuery);
	// Вторая транзакция пытается их увидеть
	int rowCount = 0;
	JdbcRowSet jdbcRs = new JdbcRowSetImpl(getNewConnection());
	jdbcRs.setCommand("SELECT * FROM customers");
	jdbcRs.execute();
	while (jdbcRs.next()) {
		rowCount++;
	}
	assertEquals(2, rowCount);
}
Kapansin-pansin, maaaring mabigo ang pagsubok na ito sa isang vendor na hindi sumusuporta sa TRANSACTION_READ_UNCOMMITTED (halimbawa, sqlite o HSQL). At maaaring hindi gumana ang antas ng transaksyon. Tandaan na ipinahiwatig namin ang bersyon ng driver ng H2 Database? Kung itataas namin ito sa h2Version = '1.4.177' at mas mataas, ang READ UNCOMMITTED ay hihinto sa paggana, bagama't hindi namin binago ang code. Muli nitong pinatutunayan na ang pagpili ng bersyon ng vendor at driver ay hindi lamang mga titik, talagang tutukuyin nito kung paano isasagawa ang iyong mga kahilingan. Mababasa mo ang tungkol sa kung paano ayusin ang gawi na ito sa bersyon 1.4.177 at kung paano ito hindi gumagana sa mas matataas na bersyon dito: " Suportahan ang READ UNCOMMITTED isolation level sa MVStore mode ".
JDBC o kung saan nagsisimula ang lahat - 12

Bottom line

Tulad ng nakikita natin, ang JDBC ay isang makapangyarihang tool sa mga kamay ng Java para sa pagtatrabaho sa mga database. Umaasa ako na ang maikling pagsusuri na ito ay makakatulong sa pagbibigay sa iyo ng panimulang punto o makakatulong sa pag-refresh ng iyong memorya. Well, para sa meryenda, ilang karagdagang materyales: #Viacheslav
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION