JavaRush /Kurslar /All lectures for UZ purposes /R2DBC DatabaseClient-dan foydalanish

R2DBC DatabaseClient-dan foydalanish

All lectures for UZ purposes
Daraja, Dars
Mavjud

DatabaseClient – bu R2DBC asosiy paketidagi markaziy sinf. Bu resurslarni yaratish va bo'shatishni boshqaradi, shu bilan birga ulanishni yopishni unutish kabi keng tarqalgan xatolardan qochishga yordam beradi. Bu R2DBC asosiy ish jarayonining asosiy vazifalarini bajaradi (masalan, bayonotlarni yaratish va bajarish), dastur kodini SQL yetkazish va natijalarni olish uchun qoldiradi. DatabaseClient sinf quyidagi vazifalarni bajaradi:

  • SQL so'rovlarini bajaradi

  • Saqlangan protsedurani chaqiruvchi va yangilovchi ko'rsatmalarni yangilaydi

  • Result nusxalarini aylanib o'tadi

  • R2DBC istisnolarini ushlaydi va ularni org.springframework.dao paketida aniqlangan tipizatsiyalangan, ko'proq ma'lumot beruvchi istisnolar ierarxiyasiga o'zgartiradi. (Izohli istisnolar ierarxiyasini ko'ring)

Klient reaktiv turlardan foydalanib deklarativ komponentlar yaratishni ta'minlovchi funktsional, erkin API interfeysiga ega.

Agar siz DatabaseClient-dan o'z kodingiz uchun foydalansangiz, faqat java.util.function interfeyslarini amalga oshirishingiz kerak bo'ladi va ularga aniq belgilangan kontraktni berishingiz kerak bo'ladi. DatabaseClient sinfi tomonidan taqdim etilgan Connectionni hisobga olgan holda, Function qayta chaqiruvi Publisherni yaratadi. Bu Rowdan natijani olish uchun xaritalash funktsiyalari uchun ham amal qiladi.

DatabaseClientni DAO amalga oshirilishi ichida to'g'ridan-to'g'ri ConnectionFactoryga ishora qiluvchi nusxani yaratish orqali ishlatishingiz mumkin yoki uni Spring IoC konteynerida sozlash va DAO ob'ektlariga bog'lovchi sifatida uzatishingiz mumkin.

DatabaseClient ob'ektini yaratishning eng oddiy usuli quyida ko'rsatilgandek statik fabrik metodidan foydalanishdir:

Java
DatabaseClient client = DatabaseClient.create(connectionFactory);
Kotlin
val client = DatabaseClient.create(connectionFactory)
ConnectionFactory har doim Spring IoC konteynerida bean sifatida sozlanishi kerak.

Oldingi metod DatabaseClientni standart sozlamalar bilan yaratadi.

Shuningdek, DatabaseClient.builder()dan Builder nusxasini olish mumkin. Quyidagi metodlarni chaqirish orqali clientni sozlashingiz mumkin:

  • ….bindMarkers(…): Ma'lumotlar bazasi uchun o'rnatilgan markerlarni almashtirish uchun nomlangan parametrni sozlash uchun BindMarkersFactoryni aniqlang.

  • ….executeFunction(…): Statement ob'ektlari bajariladi, deb kutayotgan ExecuteFunctionni o'rnating.

  • ….namedParameters(false): Nomlangan parametrlarni kengaytirishni o'chiring. Standart holda yoqilgan.

Dialektlar BindMarkersFactoryResolver tomonidan aniqlanadi va ConnectionFactorydan kelib chiqadi, odatda ConnectionFactoryMetadatani tekshirish orqali. Spring sizning BindMarkersFactoryni avtomatik ravishda aniqlashiga ruxsat berishingiz mumkin, org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProviderni META-INF/spring.factories orqali ro'yxatga olgan holda. BindMarkersFactoryResolver classpathda joylashgan ulanish markeri provayderlarini SpringFactoriesLoader yordamida aniqlaydi.

Quyidagi ma'lumotlar bazalariga hozirda qo'llab-quvvatlanadi:

  • H2

  • MariaDB

  • Microsoft SQL Server

  • MySQL

  • Postgres

Bu sinf tomonidan chiqariladigan barcha SQL so'rovlari DEBUG darajasida mijoz nusxasi sinfining to'liq nomi bilan mos keladigan kategoriya ostida qayd etiladi (odatda DefaultDatabaseClient). Bundan tashqari, har bir bajarish debuggani osonlashtirish uchun reaktiv ketma-ketlikda nazorat nuqtasini qayd etadi.

Quyidagi bo'limlarda DatabaseClientdan foydalanishning ba'zi bir misollari keltirilgan. Bu misollar DatabaseClient tomonidan taqdim etilgan barcha funksionalliklarning to'liq ro'yxatini emas, balki namoyish etadi. javadocni ko'ring.

Bayonotlarni bajarish

DatabaseClient bayonotni bajarish uchun asosiy funksionallikni ta'minlaydi. Quyidagi misolda minimal, lekin to'liq funksional kodda yangi jadval qanday yaratilishi ko'rsatilgan:

Java
Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
        .then();
Kotlin
client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
        .await()

DatabaseClient qulay, erkin foydalanish uchun mo'ljallangan. Har bir bajarish spetsifikatsiyasi bosqichida oraliq, davomli va yakuniy metodlarni ochib beradi. Oldingi misolda then() foydalanilgan, u so'rovlardan (yoki SQL so'rovi bir nechta bayonotlarni o'z ichiga olsa) biri tugallangach, yakunlanuvchi Publisher qaytaradi.

execute(…) SQL so'rovi qatorini yoki so'rovni Supplier<String>ni qabul qiladi, so'rov yaratishni bajarishga qadar kechiktiradi.

So'rovlar yaratish (SELECT)

SQL so'rovlari Row ob'ektlari orqali qiymatlar qaytarishi yoki ta'sirlangan qatorlar sonini ko'rsatishi mumkin. DatabaseClient so'rovdan kelib chiquvchi yangilangan qatorlar sonini yoki qatorlarning o'zini qaytarishi mumkin.

Quyidagi so'rov id va name ustunlarini jadvaldan qaytaradi:

Java
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
        .fetch().first();
Kotlin
val first = client.sql("SELECT id, name FROM person")
        .fetch().awaitSingle()

Keyingi so'rovda bog'lanish o'zgaruvchisi ishlatilgan:

Java
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
        .bind("fn", "Joe")
        .fetch().first();
Kotlin
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
        .bind("fn", "Joe")
        .fetch().awaitSingle()

Yuqoridagi misolda fetch() ishlatilganini ko'rgan bo'lishingiz mumkin. fetch() – bu davomli operator, u qancha ma'lumot iste'mol qilinishi kerakligini belgilash imkonini beradi.

first() chaqiruvi natijadan birinchi qatordan qaytaradi va qolganlarini chiqarib tashlaydi. Ma'lumotlar quyidagi operatorlar yordamida iste'mol qilinishi mumkin:

  • first() natijadan birinchi qatordan qaytaradi. Uning Kotlin'dagi korutin versiyasi awaitSingle() deb ataladi, qiymatlar bo'sh qiymatni qabul qilmaydi, va awaitSingleOrNull(), agar qiymat majburiy bo'lsa.

  • one() aniq bir natijadan qaytaradi va natija ko'proq qatorlarni o'z ichiga olsa, xatolik tugaydi. Kotlin'dagi korutinlar awaitOne() deb ataladi.

  • all() natijaning barcha qatorlarini qaytaradi. Kotlin'dagi korutinlarni ishlatishda flow()dan foydalaning.

  • 数据库客户端使用的类和方法名保持原样
  • rowsUpdated() ta'sirlangan qatorlar (ko'rsatma INSERT/UPDATE/DELETE hisoblagich) sonini qaytaradi. Uning Kotlin'dagi korutin versiyasi awaitRowsUpdated() deb ataladi.

Qo'shimcha xaritalash tafsilotlarini belgilamasdan so'rovlar natijalarni Map bo'yicha qaytaradi, bu erda kalitlar harf registriga e'tibor bermasdan ustun nomlaridir, ular ustun qiymatlari bilan mos keladi.

Natijalarni xaritalashni boshqarish uchun Function<Row, T> ni taqdim etish mumkin, bu har bir Row uchun chaqiriladi, shu bilan uni ixtiyoriy qiymatlar (yagona qiymatlar, to'plamlar, Map va ob'ektlar) qaytarishi mumkin bo'ladi.

Quyidagi misolda name ustunini olib tashlash va uning qiymatini chiqarish ko'rsatilgan:

Java
Flux<String> names = client.sql("SELECT name FROM person")
        .map(row -> row.get("name", String.class))
        .all();
Kotlin
val names = client.sql("SELECT name FROM person")
        .map{ row: Row -> row.get("name", String.class) }
        .flow()
null haqida nima deyish mumkin?

Relyatsion ma'lumotlar bazalari natijalari null qiymatlarni o'z ichiga olishi mumkin. Reactive Streams spetsifikatsiyasi null qiymatlarni etkazib berishni taqiqlaydi. Bu talab null qiymatlarni extractor funksiyasida to'g'ri ishlashini talab etadi. Rowdan null qiymatlarni olish mumkin bo'lsa ham, null qiymatni yaratmaslik kerak. Optional kabi bitta qiymatlar uchun barcha null qiymatlarni ob'ektga o'ralishi kerak.

Yangilash (INSERT, UPDATE, va DELETE) DatabaseClient yordamida

Modifikatsiya qiluvchi ko'rsatmalarning yagona farqi shundaki, ular odatda jadval ma'lumotlarini qaytarmaydi, shuning uchun rowsUpdated() funksiyasidan natijalarni olish uchun foydalaniladi.

Quyidagi misolda yangilanadigan qatorlar sonini qaytaruvchi UPDATE ko'rsatmasi ko'rsatilgan:

Java
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
        .bind("fn", "Joe")
        .fetch().rowsUpdated();
Kotlin
val affectedRows = client.sql("UPDATE person SET first_name = :fn")
        .bind("fn", "Joe")
        .fetch().awaitRowsUpdated()

So'rovlarga qiymatlarni bog'lash

Tipik dastur ma'lum kirish ma'lumotlariga muvofiq satrlarni tanlash yoki yangilash uchun parametrli SQL ko'rsatmalarini talab qiladi. Odatda bu WHERE ifodasi bilan cheklangan SELECT ko'rsatmalari yoki kirish parametrlarini qabul qiluvchi INSERT va UPDATE ko'rsatmalari. Parametrli ko'rsatmalar SQL injektsiya xavfini saqlaydi, agar parametrlar to'g'ri tarzda tayyorlanmagan bo'lsa. DatabaseClient parametrlar xavfini bartaraf etish uchun R2DBC bind API interfeysini ishlatadi. Siz execute(…) operatori yordamida parametrli SQL ko'rsatmasini ko'rsatishingiz va Statementga parametrlarni bog'lashingiz mumkin. Keyin R2DBC driver s o'rnatilgan ko'rsatma yordamida bajarishni amalga oshiradi.

Parametrlarni bog'lash ikki bog'lash strategiyasini qo'llab-quvvatlaydi:

  • Indeks bo'yicha, nol indeksli parametrlar yordamida.

  • Nom bo'yicha, joy tutuvchi nomidan foydalangan holda.

Quyidagi misol parametrlarni so'rov uchun bog'lashni ko'rsatadi:

db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
    .bind("id", "joe")
    .bind("name", "Joe")
    .bind("age", 34);
R2DBC-ning mahalliy bog'lash markerlari

R2DBC ma'lumotlar bazasi ishlab chiqaruvchisi haqiqiy yordamida mahalliy bog'lash markerlarini ishlatadi. Masalan, Postgres $1, $2, $n kabi indekslangan markerlarni ishlatadi. Boshqa misol SQL Server, u @ prefiksi bilan nomlangan bog'lash markerlarini ishlatadi.

Bu JDBC dan farq qiladi, bu ? ni bog'lash marker sifatida talab qiladi. JDBC'da haqiqiy drayverlar ko'rsatmani bajarish jarayonida ? bog'lash markerlarini ma'lumotlar bazasining mahalliy markerlariga o'zgartiradi.

Spring Framework'da R2DBC-ni qo'llab-quvvatlash mahalliy bog'lash markerlarini yoki :name sintaksisiga ega nomlangan bog'lash markerlarini ishlatishga imkon beradi.

Nomlangan parametrlar qo'llab-quvvatlovchi vositalar BindMarkersFactory nusxasini ishlatib reaktiv bog'lash markerlarini mahalliy markerlarga ajratadi, bu turli ma'lumotlar bazasi ishlab chiqaruvchilari uchun so'rovlarning ma'lum darajada ko'chirilishini ta'minlaydi.

So'rov preprocessor'i Collection nomlangan parametrlarini bog'lash markerlar ketma-ketligiga kengaytirib, argumentlar soniga qarab so'rov yaratishni dinamik ravishda amalga oshirishga ehtiyoj qoldirmaydi. Ob'ektlar massivlari kengaytiriladi, bu (masalan) ochiladigan ro'yxatlar foydalanishni imkonini beradi.

Quyidagi so'rovni ko'rib chiqing:

SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50))

Oldingi so'rov parametrli bo'lishi va quyidagicha bajarilishi mumkin:

Java
List<Object[]> tuples = new ArrayList<>();
tuples.add(new Object[] {"John", 35});
tuples.add(new Object[] {"Ann",  50});
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
    .bind("tuples", tuples);
Kotlin
val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35))
tuples.add(arrayOf("Ann", 50))
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
    .bind("tuples", tuples)
Ochiladigan ro'yxatlarni ishlatish imkoni ishlab chiqaruvchiga bog'liq.

Quyidagi misol IN predikatlarini ishlatishning oddiy variantini ko'rsatadi:

Java
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
    .bind("ages", Arrays.asList(35, 50));
Kotlin
val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35))
tuples.add(arrayOf("Ann", 50))
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
    .bind("tuples", arrayOf(35, 50))
Ayni R2DBC Collection turidagi qiymatlarni qo'llab-quvvatlamaydi. Biroq, yuqoridagi misolda Listni kengaytirish Spring'dagi R2DBC qo'llab-quvvatlash vositalarida masalan, IN ifodalari uchun yuqorida ko'rsatilishicha ishlashiga imkon beradi. Biroq, qatorlar turi bilan ustunlar qo'shish yoki yangilash (masalan, Postgres) uchun qora yoki qator tipi talab qilinadi, odatda bu R2DBC asosiy dastaklari tomonidan qo'llab-quvvatlanadi: odatda Java qatorlari, masalan, String[] text[] ustuni yangilash uchun. Array parametri sifatida Collection<String> yoki boshqa narsalarni qo'ymang.

Bayonot filtrlari

Ba'zan Statementning o'ziga parametrlarni nozik sozlash kerak bo'ladi. DatabaseClient orqali Statement filtri (StatementFilterFunction)ni ro'yxatdan o'tkazing, bu esa bajarish jarayonida bayonotlarni tuzatuvchi va o'zgartiruvchi bo'lib xizmat qiladi, quyidagi misolda ko'rsatilgan:

Java
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
    .filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
    .bind("name", …)
    .bind("state", …);
Kotlin
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
            .filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
            .bind("name", …)
            .bind("state", …)

DatabaseClient xuddi shunday filter(…)ni o'zgartirishni soddalashtiruvchi Function<Statement, Statement>ni qabul qiladi:

Java
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
    .filter(statement -> s.returnGeneratedValues("id"));
client.sql("SELECT id, name, state FROM table")
    .filter(statement -> s.fetchSize(25));
Kotlin
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
    .filter { statement -> s.returnGeneratedValues("id") }
client.sql("SELECT id, name, state FROM table")
    .filter { statement -> s.fetchSize(25) }

StatementFilterFunction implementatsiyalari Statementni va Result ob'ektlarini filtrlaydi.

DatabaseClient bilan optimal ishlash usullari

Sozlangandan so'ng DatabaseClient sinfi instansiyalari ko'p oqimli xavfsizdir. Bu muhim, chunki bu bitta DatabaseClient instansiyasini sozlash, keyin esa ushbu umumiy ulanishni bir nechta DAO (yoki repozitoriylar) ichida xavfsiz tarzda kiritish mumkinligini anglatadi. DatabaseClient holatni saqlaydi, chunki u ConnectionFactoryga ishora qiladi, lekin bu holat dialog holati emas.

DatabaseClient dan foydalanishda odatda qo'llaniladigan usul ConnectionFactoryni Spring konfiguratsiya faylida sozlash va keyin ConnectionFactory bilan umumiy ulanishdan foydalangan holda sinf DAOga kiritish. DatabaseClient ConnectionFactory uchun o'rnatuvchi ichida yaratildi. Ushbu DAO taxminan quyidagi ko'rinishga olib keladi:

Java
public class R2dbcCorporateEventDao implements CorporateEventDao {
    private DatabaseClient databaseClient;
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.databaseClient = DatabaseClient.create(connectionFactory);
    }
    // CorporateEventDao uchun R2DBC qo'llab-quvvatlovchi metodlar amalga oshirilishi quyida amalga oshiriladi...
}
Kotlin
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao {
    private val databaseClient = DatabaseClient.create(connectionFactory)
    // CorporateEventDao uchun R2DBC qo'llab-quvvatlovchi metodlar amalga oshirilishi quyida amalga oshiriladi...
}

Yaqqol konfiguratsiyani bir variant komponentlarni skanerlash va moslashuvchanlik kiritish uchun annotatsiya qo'llab-quvvatlovchi vositalarini ishlatish alternativdir. Ushbu holatda sinfni @Component orqali belgilash (bu uni komponentlarni skanerlash namzodi qiladi) va ConnectionFactory o'rnatuvchi metodini @Autowired orqali belgilash mumkin. Quyidagi misol ushbu jarayonni qanday bajarilishini ko'rsatadi:

Java
@Component 
public class R2dbcCorporateEventDao implements CorporateEventDao {
    private DatabaseClient databaseClient;
    @Autowired 
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.databaseClient = DatabaseClient.create(connectionFactory); 
    }
    // CorporateEventDao uchun R2DBC qo'llab-quvvatlovchi metodlar amalga oshirilishi quyida amalga oshiriladi...
}
  1. Sinfni @Component yordamida belgilaymiz.
  2. ConnectionFactory o'rnatuvchi metodni @Autowired orqali belgilaymiz.
  3. ConnectionFactory yordamida yangi DatabaseClient yaratamiz.
Kotlin
@Component 
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao { 
    private val databaseClient = DatabaseClient(connectionFactory) 
    // CorporateEventDao uchun R2DBC qo'llab-quvvatlovchi metodlar amalga oshirilishi quyida amalga oshiriladi...
}
  1. Sinfni @Component yordamida belgilaymiz.
  2. ConnectionFactory konnector injection.
  3. ConnectionFactory yordamida yangi DatabaseClient yaratamiz.

Yuqorida ko'rsatilgan shablonning boshida sozlash uslubidan foydalanishni yoki ulardan foydalanishni afzal bilmasligingizdan qat'i nazar, SQLni bajarishingiz kerak bo'lgan har safar yangi DatabaseClient sinfining yangi nusxasini yaratishga ehtiyoj juda kamdir. Sozlangandan keyin DatabaseClientning bir nusxasi ko'p oqimli xavfsizdir. Agar sizning dasturingiz bir nechta ma'lumotlar bazalariga murojaat qilsa, shunda bir nechta DatabaseClient nusxalari talab qilinishi mumkin, bu bir nechta ConnectionFactorylarni va shunga muvofiq bir nechta turli sozlangan DatabaseClient nuqtalarini talab qiladi.

Avtomatik ravishda yaratilgan kalitlarni olish

INSERT ko'rsatmalari avtoinkrement qismi yoki identifikatorni aniqlovchi ustunli jadvallarga saqlangan qatorlarda kalitlarni yaratishi mumkin. Yaralgan ustun nomi ustidan to'liq nazoratni olish uchun faqat kerakli ustun uchun kalitni so'ragan StatementFilterFunction ro'yxatga oling.

Java
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
    .filter(statement -> s.returnGeneratedValues("id"))
        .map(row -> row.get("id", Integer.class))
        .first();
// generatedId INSERT ko'rsatmasi tugagandan so'ng yaratilgan kalitni chiqaradi
Kotlin
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
    .filter { statement -> s.returnGeneratedValues("id") }
        .map { row -> row.get("id", Integer.class) }
        .awaitOne()
// generatedId INSERT ko'rsatmasi tugagandan so'ng yaratilgan kalitni chiqaradi
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION