JavaRush /Java blogi /Random-UZ /JDBC yoki hammasi qaerdan boshlanadi
Viacheslav
Daraja

JDBC yoki hammasi qaerdan boshlanadi

Guruhda nashr etilgan
Zamonaviy dunyoda ma'lumotlarni saqlashsiz hech qanday yo'l yo'q. Va ma'lumotlar bazalari bilan ishlash tarixi juda uzoq vaqt oldin, JDBC paydo bo'lishi bilan boshlangan. Men JDBC ustiga qurilgan zamonaviy ramkalarsiz qila olmaydigan narsani eslashni taklif qilaman. Bundan tashqari, ular bilan ishlashda ham, ba'zida sizga "o'z ildizlaringizga qaytish" imkoniyati kerak bo'lishi mumkin. Umid qilamanki, ushbu sharh kirish sifatida yordam beradi yoki xotirangizni yangilashga yordam beradi.
JDBC yoki hammasi qaerdan boshlanadi - 1

Kirish

Dasturlash tilining asosiy maqsadlaridan biri axborotni saqlash va qayta ishlashdir. Ma'lumotlarni saqlash qanday ishlashini yaxshiroq tushunish uchun ilovalar nazariyasi va arxitekturasiga ozgina vaqt sarflashga arziydi. Masalan, siz adabiyotlarni, ya'ni Jozef Ingenoning " Dasturiy ta'minot me'morining qo'llanmasi: samarali archni amalga oshirish orqali muvaffaqiyatli dasturiy ta'minot arxitektori bo'l... " kitobini o'qishingiz mumkin. Aytganimizdek, ma'lum bir ma'lumotlar darajasi yoki "Ma'lumotlar qatlami" mavjud. U ma'lumotlarni saqlash joyini (masalan, SQL ma'lumotlar bazasi) va ma'lumotlar ombori bilan ishlash vositalarini (masalan, JDBC, muhokama qilinadi) o'z ichiga oladi. Bundan tashqari, Microsoft veb-saytida maqola mavjud: “ Infratuzilmaning barqaror qatlamini loyihalash ”, unda ma'lumotlar sathidan qo'shimcha qatlamni ajratishning arxitektura yechimi tasvirlangan - Persistence Layer. Bunday holda, ma'lumotlar darajasi ma'lumotlarning o'zini saqlash darajasidir, doimiylik darajasi esa ma'lumotlar sathidan saqlashdan olingan ma'lumotlar bilan ishlash uchun ma'lum darajadagi mavhumlikdir. Doimiylik qatlami "DAO" shablonini yoki turli ORMlarni o'z ichiga olishi mumkin. Ammo ORM boshqa muhokama uchun mavzu. Siz allaqachon tushunganingizdek, birinchi navbatda Data Tier paydo bo'ldi. JDK 1.1 davridan boshlab Java dunyosida JDBC (Java DataBase Connectivity - Java-dagi ma'lumotlar bazalariga ulanish) paydo bo'ldi. Bu Java SE tarkibiga kiritilgan java.sql va javax.sql paketlari ko'rinishida amalga oshirilgan Java ilovalarining turli ma'lumotlar bazasi tizimlari bilan o'zaro ta'siri uchun standartdir:
JDBC yoki hammasi qaerdan boshlanadi - 2
Ushbu standart " JSR 221 JDBC 4.1 API " spetsifikatsiyasi bilan tavsiflangan . Ushbu spetsifikatsiya JDBC API Java-da yozilgan dasturlardan relyatsion ma'lumotlar bazalariga dasturiy kirishni ta'minlaydi. Shuningdek, u JDBC API Java platformasining bir qismi ekanligini va shuning uchun Java SE va Java EE ga kiritilganligini aytadi. JDBC API ikkita paketda taqdim etilgan: java.sql va javax.sql. Keyin ular bilan tanishamiz.
JDBC yoki hammasi qaerdan boshlanadi - 3

Ishning boshlanishi

Umuman olganda JDBC API nima ekanligini tushunish uchun bizga Java ilovasi kerak. Loyihani yig'ish tizimlaridan birini ishlatish eng qulaydir. Misol uchun, Gradle dan foydalanamiz . Siz Gradle haqida qisqacha sharhda o'qishingiz mumkin: " Gradle haqida qisqacha kirish ". Birinchidan, yangi Gradle loyihasini ishga tushiramiz. Gradle funksiyasi plaginlar orqali amalga oshirilganligi sababli, ishga tushirish uchun " Gradle Build Init Plugin " dan foydalanishimiz kerak :
gradle init --type java-application
Shundan so'ng, qurilish skriptini ochamiz - build.gradle fayli , u bizning loyihamizni va u bilan qanday ishlashni tavsiflaydi. Bizni " bog'liqlar " bloki qiziqtiradi , bu erda bog'liqliklar tasvirlangan - ya'ni biz ularsiz ishlay olmaydigan va biz bog'liq bo'lgan kutubxonalar/ramkalar/api. Odatiy bo'lib, biz shunga o'xshash narsani ko'ramiz:
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'
}
Nega biz buni bu erda ko'rmoqdamiz? Bular loyihamizning bog'liqliklari bo'lib, ular loyihani yaratganimizda Gradle tomonidan avtomatik ravishda yaratilgan. Shuningdek, guava Java SE-ga kiritilmagan alohida kutubxona bo'lgani uchun. JUnit ham Java SE-ga kiritilmagan. Ammo bizda JDBC mavjud, ya'ni u Java SE ning bir qismidir. Ma'lum bo'lishicha, bizda JDBC bor. Ajoyib. Bizga yana nima kerak? Bunday ajoyib diagramma mavjud:
JDBC yoki hammasi qaerdan boshlanadi - 4
Ko'rib turganimizdek va bu mantiqiy, ma'lumotlar bazasi Java SE-ga xos bo'lmagan tashqi komponentdir. Bu oddiygina tushuntirilgan - juda ko'p ma'lumotlar bazalari mavjud va siz istalgan biri bilan ishlashingiz mumkin. Masalan, PostgreSQL, Oracle, MySQL, H2 mavjud. Ushbu ma'lumotlar bazalarining har biri ma'lumotlar bazasi sotuvchilari deb ataladigan alohida kompaniya tomonidan taqdim etiladi. Har bir ma'lumotlar bazasi o'ziga xos dasturlash tilida yozilgan (Java emas). Java dasturidan ma'lumotlar bazasi bilan ishlash imkoniyatiga ega bo'lish uchun ma'lumotlar bazasi provayderi o'zining tasvir adapteri bo'lgan maxsus drayverni yozadi. Bunday JDBC-ga mos keladiganlar (ya'ni, JDBC drayveri bo'lganlar) "JDBC-mos keladigan ma'lumotlar bazasi" deb ham ataladi. Bu erda biz kompyuter qurilmalari bilan taqqoslashimiz mumkin. Masalan, bloknotda "Chop etish" tugmasi mavjud. Uni har safar bosganingizda dastur operatsion tizimga bloknot ilovasi chop qilmoqchi ekanligini aytadi. Va sizda printer bor. Operatsion tizimingizni Canon yoki HP printerlari bilan bir xilda muloqot qilishni o'rgatish uchun sizga turli drayverlar kerak bo'ladi. Lekin siz uchun, foydalanuvchi sifatida, hech narsa o'zgarmaydi. Siz hali ham xuddi shu tugmani bosasiz. JDBC bilan bir xil. Siz bir xil kodni ishlatmoqdasiz, shunchaki kaput ostida turli ma'lumotlar bazalari ishlayotgan bo'lishi mumkin. Menimcha, bu juda aniq yondashuv. Har bir bunday JDBC drayveri artefakt, kutubxona, jar faylidir. Bu bizning loyihamizga bog'liq. Misol uchun, biz " H2 ma'lumotlar bazasi " ma'lumotlar bazasini tanlashimiz mumkin va keyin quyidagi kabi bog'liqlikni qo'shishimiz kerak:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
Qaramlikni qanday topish va uni qanday tasvirlash ma'lumotlar bazasi provayderining rasmiy veb-saytlarida yoki " Maven Central " da ko'rsatilgan. JDBC drayveri siz tushunganingizdek ma'lumotlar bazasi emas. Lekin u faqat unga yo'l ko'rsatuvchidir. Ammo " Xotira ma'lumotlar bazalarida " degan narsa bor . Bular ilovangizning butun umri davomida xotirada mavjud bo'lgan ma'lumotlar bazalari. Odatda, bu ko'pincha sinov yoki o'quv maqsadlari uchun ishlatiladi. Bu sizga mashinada alohida ma'lumotlar bazasi serverini o'rnatishdan qochish imkonini beradi. Bu biz uchun JDBC bilan tanishish uchun juda mos keladi. Shunday qilib, bizning qum qutimiz tayyor va biz boshlaymiz.
JDBC yoki hammasi qaerdan boshlanadi - 5

Ulanish

Shunday qilib, bizda JDBC drayveri bor, bizda JDBC API mavjud. Esingizda bo'lsa, JDBC Java ma'lumotlar bazasi ulanishini anglatadi. Shuning uchun, barchasi Ulanishdan boshlanadi - ulanishni o'rnatish qobiliyati. Va ulanish - bu Ulanish. Keling, yana JDBC spetsifikatsiyasi matniga murojaat qilaylik va mundarijani ko'rib chiqaylik. " 4-BOB Umumiy ko'rinish " bo'limida (umumiy ko'rinish) biz " 4.1 Ulanishni o'rnatish " (ulanishni o'rnatish) bo'limiga murojaat qilamiz , ma'lumotlar bazasiga ulanishning ikkita usuli borligi aytiladi:
  • DriverManager orqali
  • DataSource orqali
Keling, DriverManager bilan shug'ullanamiz. Aytganimizdek, DriverManager ma'lumotlar bazasiga ko'rsatilgan URL manzilida ulanish imkonini beradi, shuningdek, CLASSPATH da topilgan JDBC drayverlarini yuklaydi (va oldin JDBC 4.0 dan oldin drayverlar sinfini o'zingiz yuklashingiz kerak edi). Ma'lumotlar bazasiga ulanish haqida "9-BOB Ulanishlar" alohida bobi mavjud. Biz DriverManager orqali ulanishni qanday olishimiz bilan qiziqamiz, shuning uchun bizni "9.3 DriverManager klassi" bo'limi qiziqtiradi. Bu ma'lumotlar bazasiga qanday kirishimiz mumkinligini ko'rsatadi:
Connection con = DriverManager.getConnection(url, user, passwd);
Parametrlarni biz tanlagan ma'lumotlar bazasi veb-saytidan olish mumkin. Bizning holatda, bu H2 - " H2 Cheat Sheet ". Keling, Gradle tomonidan tayyorlangan AppTest sinfiga o'tamiz. Unda JUnit testlari mavjud. JUnit testi izoh bilan belgilangan usuldir @Test. Birlik testlari ushbu sharhning mavzusi emas, shuning uchun biz shunchaki bu ma'lum bir tarzda tasvirlangan usullar ekanligini tushunish bilan cheklanamiz, ularning maqsadi nimanidir sinab ko'rishdir. JDBC spetsifikatsiyasi va H2 veb-saytiga ko'ra, biz ma'lumotlar bazasiga ulanishni olganimizni tekshiramiz. Ulanishni olish usulini yozamiz:
private Connection getNewConnection() throws SQLException {
	String url = "jdbc:h2:mem:test";
	String user = "sa";
	String passwd = "sa";
	return DriverManager.getConnection(url, user, passwd);
}
Keling, ushbu usul uchun ulanishning haqiqatda o'rnatilganligini tekshiradigan test yozamiz:
@Test
public void shouldGetJdbcConnection() throws SQLException {
	try(Connection connection = getNewConnection()) {
		assertTrue(connection.isValid(1));
		assertFalse(connection.isClosed());
	}
}
Ushbu test, bajarilganda, natijada olingan ulanishning haqiqiyligini (to'g'ri yaratilgan) va u yopilmaganligini tekshiradi. Resurslarni sinab ko'rish orqali biz ularga kerak bo'lmaganda resurslarni chiqaramiz. Bu bizni ulanishlar va xotira oqishidan himoya qiladi. Ma'lumotlar bazasi bilan bog'liq har qanday harakatlar ulanishni talab qilganligi sababli, test boshida @Test with Connection deb belgilangan qolgan sinov usullarini taqdim qilaylik, biz ularni sinovdan so'ng chiqaramiz. Buning uchun bizga ikkita izoh kerak: @Before va @After Testlar uchun JDBC ulanishini saqlaydigan AppTest sinfiga yangi maydon qo'shamiz:
private static Connection connection;
Va keling, yangi usullarni qo'shamiz:
@Before
public void init() throws SQLException {
	connection = getNewConnection();
}
@After
public void close() throws SQLException {
	connection.close();
}
Endi har qanday test usuli JDBC ulanishiga ega bo'lishi kafolatlanadi va har safar uni o'zi yaratishi shart emas.
JDBC yoki hammasi qaerdan boshlanadi - 6

Bayonotlar

Keyinchalik biz bayonotlar yoki iboralar bilan qiziqamiz. Ular hujjatlarda " 13-BOB Bayonotlar " bo'limida tasvirlangan . Birinchidan, unda aytilishicha, bayonotlarning bir nechta turlari yoki turlari mavjud:
  • Bayonot: Parametrlarni o'z ichiga olmaydigan SQL ifodasi
  • PreparedStatement: Kirish parametrlarini o'z ichiga olgan tayyorlangan SQL bayonoti
  • CallableStatement: SQL saqlangan protseduralardan qaytish qiymatini olish qobiliyatiga ega SQL ifodasi.
Shunday qilib, ulanishga ega bo'lgan holda, biz ushbu ulanish doirasida ba'zi so'rovlarni bajarishimiz mumkin. Shuning uchun biz dastlab Connection dan SQL ifodasining namunasini olishimiz mantiqan to'g'ri. Jadval yaratishdan boshlashingiz kerak. Jadval yaratish so'rovini String o'zgaruvchisi sifatida tasvirlaymiz. Buni qanday qilish kerak? Keling, " sqltutorial.org ", " sqlbolt.com ", " postgresqltutorial.com ", " codecademy.com " kabi o'quv qo'llanmalaridan foydalanaylik. Masalan, khanacademy.org saytidagi SQL kursidan misol keltiraylik . Ma'lumotlar bazasida ifodani bajarish usulini qo'shamiz:
private int executeUpdate(String query) throws SQLException {
	Statement statement = connection.createStatement();
	// Для Insert, Update, Delete
	int result = statement.executeUpdate(query);
	return result;
}
Oldingi usul yordamida test jadvalini yaratish usulini qo'shamiz:
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);
}
Endi buni sinab ko'raylik:
@Test
public void shouldCreateCustomerTable() throws SQLException {
	createCustomerTable();
	connection.createStatement().execute("SELECT * FROM customers");
}
Endi so'rovni bajaramiz va hatto parametr bilan:
@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);
}
JDBC PreparedStatement uchun nomlangan parametrlarni qo'llab-quvvatlamaydi, shuning uchun parametrlarning o'zi savollar bilan belgilanadi va qiymatni ko'rsatish orqali biz savol indeksini ko'rsatamiz (noldan emas, 1 dan boshlab). Oxirgi testda biz natija bor yoki yo'qligini ko'rsatuvchi haqiqatni oldik. Ammo so'rov natijasi JDBC API-da qanday ifodalanadi? Va u ResultSet sifatida taqdim etiladi.
JDBC yoki hammasi qaerdan boshlanadi - 7

Natijalar to'plami

Natijalar to'plami tushunchasi JDBC API spetsifikatsiyasida "15-BOB Natijalar to'plami" bo'limida tasvirlangan. Avvalo, unda aytilishicha, ResultSet bajarilgan so'rovlar natijalarini olish va boshqarish usullarini taqdim etadi. Ya'ni, agar bajarish usuli bizga haqiqiy bo'lsa, biz ResultSet-ni olamiz. Qo'ng'iroqni createCustomerTable() usuliga @Before sifatida belgilangan init usuliga o'tkazamiz. Keling, shouldSelectData testimizni yakunlaymiz:
@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);
}
Shuni ta'kidlash kerakki, keyingi usul "kursor" deb ataladigan harakatni amalga oshiradi. ResultSet-dagi kursor bir qatorga ishora qiladi. Shunday qilib, satrni o'qish uchun siz aynan shu kursorni uning ustiga qo'yishingiz kerak. Kursor ko'chirilganda, kursor to'g'ri bo'lsa (to'g'ri, to'g'ri), ya'ni ma'lumotlarga ishora qilsa, kursorni ko'chirish usuli "true" ni qaytaradi. Agar u noto'g'ri qaytarsa, unda hech qanday ma'lumot yo'q, ya'ni kursor ma'lumotlarga ishora qilmaydi. Agar yaroqsiz kursor bilan ma'lumotlarni olishga harakat qilsak, biz xatoga duch kelamiz: Ma'lumotlar mavjud emas. Bundan tashqari, ResultSet orqali siz qatorlarni yangilashingiz yoki hatto kiritishingiz mumkinligi ham qiziq:
@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();
}

Qatorlar to'plami

ResultSet-dan tashqari, JDBC RowSet kontseptsiyasini taqdim etadi. Batafsil bu yerda oʻqishingiz mumkin: “ JDBC asoslari: RowSet obyektlaridan foydalanish ”. Foydalanishning turli xil variantlari mavjud. Masalan, eng oddiy holat quyidagicha ko'rinishi mumkin:
@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);
}
Ko'rib turganingizdek, RowSet bayonotning simbioziga o'xshaydi (biz u orqali buyruq berdik) va bajarilgan buyruq. U orqali biz kursorni boshqaramiz (keyingi usulni chaqirish orqali) va undan ma'lumotlarni olamiz. Ushbu yondashuv nafaqat qiziqarli, balki amalga oshirish mumkin bo'lgan variantlardir. Masalan, CachedRowSet. U "uzilgan" (ya'ni ma'lumotlar bazasiga doimiy ulanishdan foydalanmaydi) va ma'lumotlar bazasi bilan aniq sinxronlashni talab qiladi:
CachedRowSet jdbcRsCached = new CachedRowSetImpl();
jdbcRsCached.acceptChanges(connection);
Batafsil ma'lumotni Oracle veb-saytidagi qo'llanmada o'qishingiz mumkin: " CachedRowSetObjects dan foydalanish ".
JDBC yoki hammasi qaerdan boshlanadi - 8

Metadata

So'rovlarga qo'shimcha ravishda, ma'lumotlar bazasiga ulanish (ya'ni, Connection sinfining namunasi) metama'lumotlarga kirishni ta'minlaydi - bizning ma'lumotlar bazasi qanday tuzilgan va tashkil etilganligi haqidagi ma'lumotlar. Lekin birinchi navbatda, bir nechta asosiy fikrlarni eslatib o'tamiz: Bizning ma'lumotlar bazasiga ulanish uchun URL: "jdbc: h2: mem: test". test - bu bizning ma'lumotlar bazamizning nomi. JDBC API uchun bu katalog. Va ism katta harf bilan, ya'ni TEST bo'ladi. H2 uchun standart sxema PUBLIC. Keling, barcha foydalanuvchi jadvallarini ko'rsatadigan test yozamiz. Nega odatiy? Chunki ma'lumotlar bazalarida nafaqat foydalanuvchi jadvallari (jadval ifodalarini yaratish orqali o'zimiz yaratgan jadvallar), balki tizim jadvallari ham mavjud. Ular ma'lumotlar bazasining tuzilishi haqidagi tizim ma'lumotlarini saqlash uchun zarurdir. Har bir ma'lumotlar bazasi bunday tizim jadvallarini turlicha saqlashi mumkin. Masalan, H2 da ular " INFORMATION_SCHEMA " sxemasida saqlanadi . Qizig'i shundaki, AXBOROT SHEMASI keng tarqalgan yondashuv, ammo Oracle boshqa yo'ldan bordi. Batafsil bu yerda oʻqishingiz mumkin: “ INFORMATION_SCHEMA va Oracle ”. Keling, foydalanuvchi jadvallarida metama'lumotlarni oladigan testni yozamiz:
@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 yoki hammasi qaerdan boshlanadi - 9

Ulanish hovuzi

JDBC spetsifikatsiyasidagi ulanish hovuzida "11-bob ulanishni birlashtirish" deb nomlangan bo'lim mavjud. Shuningdek, u ulanish hovuziga bo'lgan ehtiyojning asosiy asosini beradi. Har bir Coonection ma'lumotlar bazasiga jismoniy ulanishdir. Uni yaratish va yopish ancha "qimmat" ish. JDBC faqat ulanishni birlashtiruvchi APIni taqdim etadi. Shuning uchun, amalga oshirish tanlovi bizda qoladi. Masalan, bunday ilovalar HikariCP ni o'z ichiga oladi . Shunga ko'ra, biz loyihaga bog'liqligimizga hovuz qo'shishimiz kerak bo'ladi:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
    implementation 'com.zaxxer:HikariCP:3.3.1'
    testImplementation 'junit:junit:4.12'
}
Endi bu hovuzdan qandaydir tarzda foydalanishimiz kerak. Buni amalga oshirish uchun siz ma'lumotlar manbai sifatida ham tanilgan ma'lumotlar manbasini ishga tushirishingiz kerak:
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;
}
Keling, hovuzdan ulanishni olish uchun test yozaylik:
@Test
public void shouldGetConnectionFromDataSource() throws SQLException {
	DataSource datasource = getDatasource();
	try (Connection con = datasource.getConnection()) {
		assertTrue(con.isValid(1));
	}
}
JDBC yoki hammasi qaerdan boshlanadi - 10

Bitimlar

JDBC haqidagi eng qiziqarli narsalardan biri bu tranzaktsiyalardir. JDBC spetsifikatsiyasida ularga "10-BOB operatsiyalari" bobi berilgan. Avvalo, tranzaktsiya nima ekanligini tushunishga arziydi. Tranzaksiya - ma'lumotlar bo'yicha mantiqiy birlashtirilgan ketma-ket operatsiyalar guruhi, qayta ishlangan yoki umuman bekor qilingan. JDBC dan foydalanganda tranzaksiya qachon boshlanadi? Spetsifikatsiyada aytilganidek, bu to'g'ridan-to'g'ri JDBC drayveri tomonidan boshqariladi. Lekin, odatda, yangi tranzaksiya joriy SQL bayonoti tranzaksiyani talab qilganda va tranzaksiya hali yaratilmaganda boshlanadi. Tranzaktsiya qachon tugaydi? Bu auto-commit atributi tomonidan boshqariladi. Agar autocommit yoqilgan bo'lsa, tranzaksiya SQL bayonoti "tugallangandan" keyin yakunlanadi. "Bajarildi" degani SQL ifodasi turiga bog'liq:
  • DML (Qo'shish, yangilash, o'chirish) nomi bilan ham tanilgan ma'lumotlarni manipulyatsiya qilish tili
    .
  • Hisobotlarni tanlang
    Natijalar toʻplami yopilganda tranzaktsiya yakunlanadi ( ResultSet#close )
  • CallableStatement va bir nechta natijalarni qaytaradigan iboralar
    Barcha bog'langan ResultSets yopilganda va barcha chiqishlar qabul qilinganda (shu jumladan yangilanishlar soni)
JDBC API aynan shunday ishlaydi. Odatdagidek, keling, buning uchun test yozamiz:
@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);
}
Hammasi oddiy. Ammo bizda faqat bitta tranzaksiya mavjud ekan, bu to'g'ri. Ularning bir nechtasi bo'lsa, nima qilish kerak? Ular bir-biridan ajratilgan bo'lishi kerak. Shuning uchun, keling, tranzaksiya izolyatsiyasi darajalari va JDBC ular bilan qanday munosabatda bo'lishi haqida gapiraylik.
JDBC yoki hammasi qaerdan boshlanadi - 11

Izolyatsiya darajalari

JDBC spetsifikatsiyasining "10.2 Tranzaksiya izolyatsiyasi darajalari" kichik bo'limini ochamiz. Bu erda, oldinga siljishdan oldin, men ACID kabi narsa haqida eslashni istardim. ACID tranzaktsion tizimga qo'yiladigan talablarni tavsiflaydi.
  • Atomlik:
    Tizimga hech qanday tranzaksiya qisman amalga oshirilmaydi. Yoki uning barcha kichik operatsiyalari bajariladi yoki hech biri bajarilmaydi.
  • Muvofiqlik:
    Har bir muvaffaqiyatli tranzaksiya, ta'rifiga ko'ra, faqat haqiqiy natijalarni qayd etadi.
  • Izolyatsiya:
    tranzaktsiya bajarilayotganda, bir vaqtning o'zida amalga oshirilgan operatsiyalar uning natijasiga ta'sir qilmasligi kerak.
  • Bardoshlilik:
    Agar tranzaksiya muvaffaqiyatli yakunlangan bo'lsa, unga kiritilgan o'zgartirishlar hech qanday nosozlik tufayli bekor qilinmaydi.
Tranzaktsiyalarni izolyatsiya qilish darajalari haqida gapirganda, biz "Izolyatsiya" talabi haqida gapiramiz. Izolyatsiya qimmat talab, shuning uchun haqiqiy ma'lumotlar bazalarida tranzaktsiyani to'liq izolyatsiya qilmaydigan rejimlar mavjud (Repeatable Read izolyatsiya darajalari va undan pastroq). Vikipediyada tranzaktsiyalar bilan ishlashda yuzaga kelishi mumkin bo'lgan muammolar haqida ajoyib tushuntirish mavjud. Bu erda ko'proq o'qish kerak: " Tranzaksiyalardan foydalangan holda parallel kirish muammolari ". Sinovimizni yozishdan oldin, Gradle Build skriptimizni biroz o'zgartiramiz: xususiyatlarga ega blokni, ya'ni loyihamiz sozlamalarini qo'shing:
ext {
    h2Version = '1.3.176' // 1.4.177
    hikariVersion = '3.3.1'
    junitVersion = '4.12'
}
Keyinchalik, biz buni versiyalarda ishlatamiz:
dependencies {
    implementation "com.h2database:h2:${h2Version}"
    implementation "com.zaxxer:HikariCP:${hikariVersion}"
    testImplementation "junit:junit:${junitVersion}"
}
Siz h2 versiyasi pastroq bo'lganini payqagan bo'lishingiz mumkin. Sababini keyinroq bilib olamiz. Xo'sh, izolyatsiya darajalarini qanday qo'llaysiz? Keling, darhol kichik amaliy misolni ko'rib chiqaylik:
@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);
}
Qizig'i shundaki, bu test TRANSACTION_READ_UNCOMMITTED (masalan, sqlite yoki HSQL) ni qo'llab-quvvatlamaydigan sotuvchida muvaffaqiyatsiz bo'lishi mumkin. Va tranzaksiya darajasi oddiygina ishlamasligi mumkin. H2 ma'lumotlar bazasi drayverining versiyasini ko'rsatganimizni eslaysizmi? Agar biz uni h2Version = '1.4.177' va undan yuqori darajaga ko'tarsak, kodni o'zgartirmagan bo'lsak-da, READ UNCOMMITTED ishlashni to'xtatadi. Bu yana bir bor tasdiqlaydiki, sotuvchi va haydovchi versiyasini tanlash shunchaki harflar emas, balki sizning so'rovlaringiz qanday bajarilishini aniqlaydi. Ushbu xatti-harakatni 1.4.177 versiyasida qanday tuzatish va u yuqori versiyalarda qanday ishlamasligi haqida bu yerda o'qishingiz mumkin: " MVStore rejimida READ UNCOMMITTED izolyatsiya darajasini qo'llab-quvvatlash ".
JDBC yoki hammasi qaerdan boshlanadi - 12

Pastki chiziq

Ko'rib turganimizdek, JDBC ma'lumotlar bazalari bilan ishlash uchun Java qo'lida kuchli vositadir. Umid qilamanki, bu qisqa sharh sizga boshlang'ich nuqtani berishga yoki xotirangizni yangilashga yordam beradi. Xo'sh, gazak uchun ba'zi qo'shimcha materiallar: #Viacheslav
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION