JavaRush /Java Blogu /Random-AZ /JDBC və ya hər şeyin başladığı yer
Viacheslav
Səviyyə

JDBC və ya hər şeyin başladığı yer

Qrupda dərc edilmişdir
Müasir dünyada məlumatların saxlanması olmadan heç bir yol yoxdur. Və verilənlər bazası ilə işləmə tarixi çox uzun müddət əvvəl, JDBC-nin meydana çıxması ilə başladı. JDBC üzərində qurulmuş heç bir müasir çərçivənin onsuz edə bilməyəcəyi bir şeyi xatırlamağı təklif edirəm. Bundan əlavə, hətta onlarla işləyərkən, bəzən "köklərinizə qayıtmaq" fürsətinə ehtiyacınız ola bilər. Ümid edirəm ki, bu baxış giriş kimi və ya yaddaşınızı yeniləməyə kömək edəcək.
JDBC və ya hər şeyin başladığı yer - 1

Giriş

Proqramlaşdırma dilinin əsas məqsədlərindən biri informasiyanın saxlanması və işlənməsidir. Məlumatların saxlanmasının necə işlədiyini daha yaxşı başa düşmək üçün tətbiqlərin nəzəriyyəsi və arxitekturasına bir az vaxt sərf etməyə dəyər. Məsələn, siz ədəbiyyatı oxuya bilərsiniz, yəni Joseph Ingenonun " Proqram təminatı Memarının Əl Kitabı: Effektiv arch həyata keçirərək uğurlu proqram memarı olun... " kitabını oxuya bilərsiniz. Dediyi kimi, müəyyən bir məlumat səviyyəsi və ya "Məlumat təbəqəsi" var. Buraya məlumatların saxlanması üçün yer (məsələn, SQL verilənlər bazası) və məlumat anbarı ilə işləmək üçün alətlər (məsələn, müzakirə ediləcək JDBC) daxildir. Microsoft veb-saytında həmçinin bir məqalə var: “ İnfrastrukturun davamlılıq qatının layihələndirilməsi ” əlavə təbəqənin Data səviyyəsindən – Davamlılıq Layerindən ayrılması üçün arxitektura həllini təsvir edir. Bu halda, Data Tier verilənlərin özünün saxlanma səviyyəsidir, Davamlılıq Layeri isə Data Tier səviyyəsindən yaddaşdan verilənlərlə işləmək üçün müəyyən səviyyəli abstraksiyadır. Davamlılıq Layerinə "DAO" şablonu və ya müxtəlif ORM-lər daxil ola bilər. Ancaq ORM başqa bir müzakirə mövzusudur. Artıq başa düşdüyünüz kimi, Data Tier ilk olaraq ortaya çıxdı. JDK 1.1-dən bəri Java dünyasında JDBC (Java DataBase Connectivity - Java-da verilənlər bazalarına qoşulma) yaranıb. Bu, Java SE-yə daxil olan java.sql və javax.sql paketləri şəklində həyata keçirilən Java proqramlarının müxtəlif DBMS-lərlə qarşılıqlı əlaqəsi üçün standartdır:
JDBC və ya hər şeyin başladığı yer - 2
Bu standart " JSR 221 JDBC 4.1 API " spesifikasiyası ilə təsvir edilmişdir . Bu spesifikasiya bizə JDBC API-nin Java-da yazılmış proqramlardan əlaqəli verilənlər bazalarına proqramlı girişi təmin etdiyini bildirir. O, həmçinin JDBC API-nin Java platformasının bir hissəsi olduğunu və buna görə də Java SE və Java EE-yə daxil olduğunu bildirir. JDBC API iki paketdə təqdim olunur: java.sql və javax.sql. Onda gəlin onlarla tanış olaq.
JDBC və ya hər şeyin başladığı yer - 3

İşin başlanğıcı

JDBC API-nin ümumiyyətlə nə olduğunu başa düşmək üçün bizə Java proqramı lazımdır. Layihə montaj sistemlərindən birini istifadə etmək ən rahatdır. Məsələn, Gradle istifadə edək . Siz qısa icmalda Gradle haqqında ətraflı oxuya bilərsiniz: " Gradle-a Qısa Giriş ". Əvvəlcə yeni Gradle layihəsini işə salaq. Gradle funksionallığı plaginlər vasitəsilə həyata keçirildiyi üçün işə salmaq üçün “ Gradle Build Init Plugin ” istifadə etməliyik :
gradle init --type java-application
Bundan sonra, layihəmizi və onunla necə işləməyi təsvir edən build.gradle faylını - qurma skriptini açaq . Bizi " asılılıqlar " bloku maraqlandırır , burada asılılıqlar təsvir olunur - yəni onlarsız işləyə bilməyəcəyimiz və asılı olduğumuz kitabxanalar/çərçivələr/api. Varsayılan olaraq, belə bir şey görəcəyik:
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'
}
Niyə biz bunu burada görürük? Bunlar layihəni yaratarkən Gradle-nin bizim üçün avtomatik olaraq yaratdığı layihəmizdən asılılıqlardır. Həm də ona görə ki, guava Java SE-yə daxil olmayan ayrıca kitabxanadır. JUnit də Java SE-yə daxil deyil. Ancaq bizdə JDBC qutudan çıxarılıb, yəni Java SE-nin bir hissəsidir. Belə çıxır ki, bizdə JDBC var. Əla. Bizə başqa nə lazımdır? Belə bir gözəl diaqram var:
JDBC və ya hər şeyin başladığı yer - 4
Gördüyümüz kimi və bu məntiqlidir, verilənlər bazası Java SE üçün doğma olmayan xarici komponentdir. Bu, sadəcə olaraq izah olunur - çoxlu sayda verilənlər bazası var və siz istənilən biri ilə işləyə bilərsiniz. Məsələn, PostgreSQL, Oracle, MySQL, H2 var. Bu verilənlər bazalarının hər biri verilənlər bazası satıcıları adlı ayrıca şirkət tərəfindən təmin edilir. Hər bir verilənlər bazası öz proqramlaşdırma dilində yazılmışdır (mütləq Java deyil). Java proqramından verilənlər bazası ilə işləyə bilmək üçün verilənlər bazası provayderi özünün təsvir adapteri olan xüsusi drayver yazır. Belə JDBC uyğun olanlara (yəni, JDBC sürücüsünə malik olanlar) “JDBC-Uyğun Verilənlər Bazası” da deyilir. Burada kompüter qurğuları ilə bənzətmə apara bilərik. Məsələn, notepadda "Çap et" düyməsi var. Hər dəfə basdığınız zaman proqram əməliyyat sisteminə notepad proqramının çap etmək istədiyini bildirir. Bir də printeriniz var. Əməliyyat sisteminizə Canon və ya HP printerləri ilə vahid əlaqə qurmağı öyrətmək üçün sizə müxtəlif sürücülər lazımdır. Ancaq bir istifadəçi olaraq sizin üçün heç nə dəyişməyəcək. Siz yenə də eyni düyməni basacaqsınız. JDBC ilə eyni. Siz eyni kodu işlədirsiniz, sadəcə olaraq, başlıq altında müxtəlif verilənlər bazaları işləyə bilər. Məncə, bu, çox aydın yanaşmadır. Hər bir belə JDBC sürücüsü bir növ artefakt, kitabxana, jar faylıdır. Bu, layihəmizdən asılılıqdır. Məsələn, verilənlər bazasını seçə bilərik " H2 Database " və sonra belə bir asılılıq əlavə etməliyik:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
Asılılığı necə tapmaq və onu necə təsvir etmək verilənlər bazası provayderinin rəsmi saytlarında və ya " Maven Central " da göstərilmişdir. JDBC sürücüsü anladığınız kimi verilənlər bazası deyil. Lakin o, yalnız ona bələdçidir. Ancaq " Yaddaş verilənlər bazasında " kimi bir şey var . Bunlar tətbiqinizin ömrü boyu yaddaşda mövcud olan verilənlər bazalarıdır. Tipik olaraq, bu, tez-tez sınaq və ya təlim məqsədləri üçün istifadə olunur. Bu, maşında ayrıca verilənlər bazası serverinin quraşdırılmasından qaçmağa imkan verir. Hansı ki, JDBC ilə tanış olmaq bizim üçün çox münasibdir. Beləliklə, qum qutumuz hazırdır və işə başlayırıq.
JDBC və ya hər şeyin başladığı yer - 5

Əlaqə

Beləliklə, bizdə JDBC sürücüsü var, JDBC API-miz var. Xatırladığımız kimi, JDBC Java DataBase Connectivity deməkdir. Buna görə də, hər şey Connectivity ilə başlayır - əlaqə yaratmaq imkanı. Və əlaqə Əlaqədir. Gəlin yenidən JDBC spesifikasiyasının mətninə keçək və məzmun cədvəlinə baxaq. " 4.FƏSİL XÜSUSİYYƏTLƏR " (ümumi baxış) bölməsində " 4.1 Əlaqənin qurulması " (bağlantı qurmaq) bölməsinə müraciət edirik ki, verilənlər bazasına qoşulmağın iki yolu var:
  • DriverManager vasitəsilə
  • DataSource vasitəsilə
Gəlin DriverManager ilə məşğul olaq. Dediyi kimi, DriverManager sizə göstərilən URL-də verilənlər bazasına qoşulmağa imkan verir, həmçinin CLASSPATH-də tapdığı JDBC Sürücülərini yükləyir (və əvvəllər JDBC 4.0-dan əvvəl sürücü sinifini özünüz yükləməli idiniz). Verilənlər bazasına qoşulma haqqında ayrıca “FƏSİL 9 Əlaqələr” fəsli var. Bizi DriverManager vasitəsilə necə bağlamaq maraqlandırır, ona görə də biz "9.3 DriverManager Sinfi" bölməsi ilə maraqlanırıq. Bu, verilənlər bazasına necə daxil ola biləcəyimizi göstərir:
Connection con = DriverManager.getConnection(url, user, passwd);
Parametrlər seçdiyimiz verilənlər bazasının saytından götürülə bilər. Bizim vəziyyətimizdə bu, H2 - " H2 Fırıldaqçı Vərəqi "dir. Gəlin Gradle tərəfindən hazırlanan AppTest sinfinə keçək. O, JUnit testlərini ehtiva edir. JUnit testi annotasiya ilə qeyd olunan bir üsuldur @Test. Vahid testləri bu baxışın mövzusu deyil, buna görə də biz sadəcə olaraq bunların müəyyən bir şəkildə təsvir olunan üsullar olduğunu başa düşməklə məhdudlaşacağıq, məqsədi bir şeyi sınamaqdır. JDBC spesifikasiyasına və H2 veb saytına əsasən, verilənlər bazası ilə əlaqə əldə etdiyimizi yoxlayacağıq. Bağlantı əldə etmək üçün bir üsul yazaq:
private Connection getNewConnection() throws SQLException {
	String url = "jdbc:h2:mem:test";
	String user = "sa";
	String passwd = "sa";
	return DriverManager.getConnection(url, user, passwd);
}
İndi bu üsul üçün əlaqənin həqiqətən qurulduğunu yoxlayacaq bir test yazaq:
@Test
public void shouldGetJdbcConnection() throws SQLException {
	try(Connection connection = getNewConnection()) {
		assertTrue(connection.isValid(1));
		assertFalse(connection.isClosed());
	}
}
Bu test, icra edildikdə, nəticədə yaranan əlaqənin etibarlı olduğunu (düzgün yaradıldığını) və bağlanmadığını yoxlayacaq. Resursları sınamaqdan istifadə etməklə biz onlara ehtiyac qalmayandan sonra onları buraxacağıq. Bu, bizi sarkma əlaqələri və yaddaş sızmalarından qoruyacaq. Verilənlər bazası ilə hər hansı bir hərəkət əlaqə tələb etdiyi üçün testin əvvəlində @Test with a Connection işarəsi ilə işarələnmiş qalan test üsullarını təqdim edək, testdən sonra buraxacağıq. Bunu etmək üçün bizə iki annotasiya lazımdır: @Before və @After Testlər üçün JDBC bağlantısını saxlayacaq AppTest sinfinə yeni sahə əlavə edək:
private static Connection connection;
Və yeni üsullar əlavə edək:
@Before
public void init() throws SQLException {
	connection = getNewConnection();
}
@After
public void close() throws SQLException {
	connection.close();
}
İndi hər hansı bir test metodunun JDBC bağlantısına malik olacağına zəmanət verilir və onu hər dəfə özü yaratmaq məcburiyyətində deyil.
JDBC və ya hər şeyin başladığı yer - 6

Bəyanatlar

Sonra biz bəyanatlar və ya ifadələrlə maraqlanırıq. Onlar " FƏSİL 13 Bəyanatlar " bölməsindəki sənədlərdə təsvir edilmişdir . Birincisi, ifadələrin bir neçə növü və ya növü olduğunu söyləyir:
  • Bəyanat: Parametrləri olmayan SQL ifadəsi
  • PreparedStatement: Giriş parametrlərini ehtiva edən hazırlanmış SQL bəyanatı
  • CallableStatement: SQL Saxlanılan Prosedurlardan qaytarma dəyəri əldə etmək imkanı olan SQL ifadəsi.
Beləliklə, bir əlaqə olduqda, bu əlaqə çərçivəsində bəzi sorğuları yerinə yetirə bilərik. Buna görə də məntiqlidir ki, biz əvvəlcə Connection-dan SQL ifadəsinin nümunəsini alırıq. Cədvəl yaratmaqla başlamaq lazımdır. Cədvəl yaratmaq sorğusunu String dəyişəni kimi təsvir edək. Bunu necə etmək olar? Gəlin " sqltutorial.org ", " sqlbolt.com ", " postgresqltutorial.com ", " codecademy.com " kimi bəzi dərsliklərdən istifadə edək. Məsələn, khanacademy.org saytındakı SQL kursundan bir nümunədən istifadə edək . Verilənlər bazasında ifadənin icrası üçün metod əlavə edək:
private int executeUpdate(String query) throws SQLException {
	Statement statement = connection.createStatement();
	// Для Insert, Update, Delete
	int result = statement.executeUpdate(query);
	return result;
}
Əvvəlki metoddan istifadə edərək test cədvəli yaratmaq üçün bir üsul əlavə edək:
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);
}
İndi bunu sınaqdan keçirək:
@Test
public void shouldCreateCustomerTable() throws SQLException {
	createCustomerTable();
	connection.createStatement().execute("SELECT * FROM customers");
}
İndi sorğunu yerinə yetirək və hətta bir parametrlə:
@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 üçün adlandırılmış parametrləri dəstəkləmir, ona görə də parametrlərin özləri suallarla müəyyən edilir və dəyəri təyin etməklə biz sual indeksini göstəririk (sıfırdan yox, 1-dən başlayaraq). Son testdə bir nəticənin olub-olmamasının göstəricisi olaraq doğru aldıq. Bəs sorğunun nəticəsi JDBC API-də necə təmsil olunur? Və o, Nəticə Seti kimi təqdim olunur.
JDBC və ya hər şeyin başladığı yer - 7

Nəticə dəsti

Nəticə dəsti anlayışı JDBC API spesifikasiyasında "FƏSİL 15 Nəticə Dəstləri" fəslində təsvir edilmişdir. Hər şeydən əvvəl, ResultSet icra edilən sorğuların nəticələrini əldə etmək və manipulyasiya etmək üçün üsullar təqdim etdiyini söyləyir. Yəni, əgər icra üsulu bizə doğrudursa, onda biz ResultSet əldə edə bilərik. Zəngi createCustomerTable() metoduna @Before kimi qeyd olunan init metoduna köçürək. İndi shouldSelectData testimizi yekunlaşdıraq:
@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);
}
Burada qeyd etmək lazımdır ki, növbəti sözdə "kursoru" hərəkət etdirən bir üsuldur. ResultSet-dəki kursor bəzi sətirə işarə edir. Beləliklə, bir sətri oxumaq üçün bu kursoru onun üzərinə yerləşdirmək lazımdır. Kursor köçürüldükdə, kursor etibarlıdırsa (düzgün, düzgün), yəni verilənlərə işarə edirsə, kursorun hərəkət metodu doğru qaytarır. Yanlış qaytarırsa, onda heç bir məlumat yoxdur, yəni kursor məlumatı göstərmir. Yanlış kursorla məlumatları əldə etməyə çalışsaq, xətanı alacağıq: Məlumat yoxdur. Həmçinin maraqlıdır ki, ResultSet vasitəsilə siz sətirləri yeniləyə və hətta əlavə edə bilərsiniz:
@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();
}

Sıra dəsti

ResultSet-ə əlavə olaraq, JDBC RowSet konsepsiyasını təqdim edir. Daha ətraflı burada oxuya bilərsiniz: " JDBC Əsasları: RowSet Obyektlərindən istifadə ". Müxtəlif istifadə variantları var. Məsələn, ən sadə hal belə görünə bilər:
@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);
}
Gördüyünüz kimi, RowSet bəyanatın simbiozuna (biz onun vasitəsilə əmr verdik) və yerinə yetirilən əmrə bənzəyir. Onun vasitəsilə biz kursoru idarə edirik (növbəti metodu çağırmaqla) və ondan məlumat alırıq. Yalnız bu yanaşma maraqlı deyil, həm də mümkün tətbiqlərdir. Məsələn, CachedRowSet. O, "əlaqəsi kəsilib" (yəni verilənlər bazası ilə davamlı əlaqədən istifadə etmir) və verilənlər bazası ilə açıq sinxronizasiya tələb edir:
CachedRowSet jdbcRsCached = new CachedRowSetImpl();
jdbcRsCached.acceptChanges(connection);
Daha ətraflı Oracle saytındakı təlimatda oxuya bilərsiniz: " CachedRowSetObjects-dən istifadə ".
JDBC və ya hər şeyin başladığı yer - 8

Metadata

Sorğulara əlavə olaraq, verilənlər bazasına qoşulma (yəni, Connection sinfinin nümunəsi) metadataya - verilənlər bazamızın necə konfiqurasiya və təşkil olunduğuna dair məlumatlara çıxış təmin edir. Ancaq əvvəlcə bir neçə əsas məqamı qeyd edək: Verilənlər bazamıza qoşulmaq üçün URL: “jdbc:h2:mem:test”. test verilənlər bazamızın adıdır. JDBC API üçün bu bir kataloqdur. Və ad böyük hərflə, yəni TEST olacaq. H2 üçün standart sxem PUBLIC-dir. İndi bütün istifadəçi cədvəllərini göstərən bir test yazaq. Niyə adət? Çünki verilənlər bazalarında təkcə istifadəçi cədvəlləri deyil (cədvəl ifadələri yaratmaqla özümüz yaratdıq), həm də sistem cədvəlləri var. Onlar verilənlər bazasının strukturu haqqında sistem məlumatlarını saxlamaq üçün lazımdır. Hər bir verilənlər bazası belə sistem cədvəllərini fərqli şəkildə saxlaya bilər. Məsələn, H2-də onlar " INFORMATION_SCHEMA " sxemində saxlanılır . Maraqlıdır ki, İNFORMASİYA SCHEMASI ümumi yanaşmadır, lakin Oracle fərqli bir yol tutdu. Ətraflı burada oxuya bilərsiniz: " INFORMATION_SCHEMA və Oracle ". İstifadəçi cədvəllərində metadata qəbul edən bir test yazaq:
@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 və ya hər şeyin başladığı yer - 9

Bağlantı hovuzu

JDBC spesifikasiyasındakı əlaqə hovuzunun "Fəsil 11 Əlaqələrin Birləşdirilməsi" adlı bölməsi var. O, həmçinin əlaqə hovuzuna ehtiyac üçün əsas əsaslandırmanı təmin edir. Hər Coonection verilənlər bazası ilə fiziki əlaqədir. Onun yaradılması və bağlanması kifayət qədər “bahalı” işdir. JDBC yalnız əlaqəni birləşdirən API təmin edir. Ona görə də icra seçimi bizim qalır. Məsələn, bu cür tətbiqlərə HikariCP daxildir . Müvafiq olaraq, layihə asılılığımıza bir hovuz əlavə etməliyik:
dependencies {
    implementation 'com.h2database:h2:1.4.197'
    implementation 'com.zaxxer:HikariCP:3.3.1'
    testImplementation 'junit:junit:4.12'
}
İndi birtəhər bu hovuzdan istifadə etməliyik. Bunu etmək üçün Datasource kimi tanınan məlumat mənbəyini işə salmalısınız:
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;
}
Və hovuzdan əlaqə almaq üçün test yazaq:
@Test
public void shouldGetConnectionFromDataSource() throws SQLException {
	DataSource datasource = getDatasource();
	try (Connection con = datasource.getConnection()) {
		assertTrue(con.isValid(1));
	}
}
JDBC və ya hər şeyin başladığı yer - 10

Əməliyyatlar

JDBC ilə bağlı ən maraqlı şeylərdən biri əməliyyatlardır. JDBC spesifikasiyasında onlara "FƏSİL 10 Əməliyyatlar" bölməsi təyin edilmişdir. Əvvəlcə əməliyyatın nə olduğunu başa düşməyə dəyər. Tranzaksiya bütövlükdə işlənmiş və ya ləğv edilmiş verilənlər üzərində məntiqi birləşmiş ardıcıl əməliyyatlar qrupudur. JDBC istifadə edərkən əməliyyat nə vaxt başlayır? Spesifikasiyada göstərildiyi kimi, bu, birbaşa JDBC Sürücüsü tərəfindən idarə olunur. Lakin adətən, yeni əməliyyat cari SQL ifadəsi əməliyyat tələb etdikdə və əməliyyat hələ yaradılmadıqda başlayır. Əməliyyat nə vaxt başa çatır? Bu, avtomatik icra atributu tərəfindən idarə olunur. Avtomatik icra aktivləşdirilibsə, əməliyyat SQL ifadəsi "tamamlandıqdan" sonra tamamlanacaq. "Tamamlandı" nə deməkdir SQL ifadəsinin növündən asılıdır:
  • DML (Insert, Update, Delete) kimi də tanınan Data Manipulyasiya Dili
    Əməliyyat tamamlanan kimi əməliyyat tamamlanır.
  • Hesablamaları seçin
    Nəticə dəsti bağlandıqda əməliyyat tamamlanır ( ResultSet#close )
  • CallableStatement və çoxsaylı nəticələr qaytaran ifadələr
    Bütün əlaqəli Nəticə Setləri bağlandıqda və bütün çıxışlar alındıqda (yeniləmələrin sayı daxil olmaqla)
JDBC API tam olaraq belə davranır. Həmişə olduğu kimi, bunun üçün bir test yazaq:
@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);
}
Bu sadədir. Ancaq bu, yalnız bir əməliyyatımız olduğu müddətcə doğrudur. Onlardan bir neçəsi olduqda nə etməli? Onları bir-birindən təcrid etmək lazımdır. Buna görə də, tranzaksiya təcrid səviyyələri və JDBC-nin onlarla necə məşğul olması haqqında danışaq.
JDBC və ya hər şeyin başladığı yer - 11

İzolyasiya səviyyələri

JDBC spesifikasiyasının "10.2 Transaction izolyasiya səviyyələri" alt bölməsini açaq. Burada, daha irəli getməzdən əvvəl, ACID kimi bir şeyi xatırlamaq istərdim. ACID əməliyyat sistemi üçün tələbləri təsvir edir.
  • Atomluq:
    Sistemə heç bir əməliyyat qismən bağlanmayacaq. Ya onun bütün alt əməliyyatları yerinə yetiriləcək, ya da heç biri yerinə yetirilməyəcək.
  • Ardıcıllıq:
    Hər bir uğurlu əməliyyat tərifinə görə yalnız etibarlı nəticələri qeyd edir.
  • İzolyasiya:
    Əməliyyat işləyərkən paralel əməliyyatlar onun nəticələrinə təsir etməməlidir.
  • Davamlılıq:
    Əgər tranzaksiya uğurla tamamlanarsa, ona edilən dəyişikliklər hər hansı uğursuzluq səbəbindən geri qaytarılmayacaq.
Əməliyyat təcrid səviyyələri haqqında danışarkən, "İzolyasiya" tələbindən danışırıq. İzolyasiya bahalı tələbdir, ona görə də real verilənlər bazalarında tranzaksiyanı tam təcrid etməyən rejimlər var (Təkrarlanan oxunma izolyasiya səviyyələri və aşağı). Vikipediyada əməliyyatlarla işləyərkən yarana biləcək problemlərin əla izahı var. Burada daha çox oxumağa dəyər: " Transaksiyalardan istifadə edərək paralel giriş problemləri ." Testimizi yazmazdan əvvəl gəlin Gradle Build Skriptimizi bir az dəyişdirək: xassələri olan, yəni layihəmizin parametrləri olan bir blok əlavə edin:
ext {
    h2Version = '1.3.176' // 1.4.177
    hikariVersion = '3.3.1'
    junitVersion = '4.12'
}
Sonra biz bunu versiyalarda istifadə edirik:
dependencies {
    implementation "com.h2database:h2:${h2Version}"
    implementation "com.zaxxer:HikariCP:${hikariVersion}"
    testImplementation "junit:junit:${junitVersion}"
}
Siz h2 versiyasının aşağı düşdüyünü görmüsünüz. Səbəbini sonra görəcəyik. Beləliklə, izolyasiya səviyyələrini necə tətbiq edirsiniz? Dərhal kiçik bir praktik nümunəyə baxaq:
@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);
}
Maraqlıdır ki, bu test TRANSACTION_READ_UNCOMMITTED-i dəstəkləməyən satıcıda uğursuz ola bilər (məsələn, sqlite və ya HSQL). Və əməliyyat səviyyəsi sadəcə işləməyə bilər. H2 Database sürücüsünün versiyasını göstərdiyimizi xatırlayırsınız? Əgər onu h2Version = '1.4.177' və daha yüksək səviyyəyə qaldırsaq, kodu dəyişdirməsək də, READ UNCOMMITTED fəaliyyətini dayandıracaq. Bu, bir daha sübut edir ki, satıcı və sürücü versiyasının seçimi sadəcə hərflərdən ibarət deyil, o, həqiqətən sorğularınızın necə yerinə yetiriləcəyini müəyyən edəcəkdir. Bu davranışı 1.4.177 versiyasında necə düzəltmək və daha yüksək versiyalarda işləməməsi haqqında burada oxuya bilərsiniz: " MVStore rejimində READ UNCOMMITTED izolyasiya səviyyəsini dəstəkləyin ".
JDBC və ya hər şeyin başladığı yer - 12

Alt xətt

Gördüyümüz kimi, JDBC verilənlər bazası ilə işləmək üçün Java-nın əlində güclü vasitədir. Ümid edirəm ki, bu qısa baxış sizə başlanğıc nöqtəsi verməyə və ya yaddaşınızı yeniləməyə kömək edəcək. Yaxşı, qəlyanaltı üçün bəzi əlavə materiallar: #Viaçeslav
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION