Útmutató Cassandra Java-val

1. Áttekintés

Ez az oktatóanyag bemutatja az Apache Cassandra adatbázist a Java használatával.

Megtalálja a legfontosabb fogalmakat, valamint egy működő példát, amely az alapvető lépéseket foglalja magában, hogy csatlakozzon ehhez a NoSQL adatbázishoz és kezdje el kezelni azt a Java-ból.

2. Cassandra

A Cassandra egy skálázható NoSQL adatbázis, amely folyamatos rendelkezésre állást biztosít egyetlen hibapont nélkül, és lehetőséget nyújt nagy mennyiségű adat kivételes teljesítményű kezelésére.

Ez az adatbázis gyűrűs kialakítást használ a master-slave architektúra helyett. A gyűrű kialakításában nincs főcsomópont - az összes résztvevő csomópont azonos és társaként kommunikál egymással.

Ezáltal a Cassandra egy vízszintesen méretezhető rendszerré válik, mivel lehetővé teszi a csomópontok inkrementális hozzáadását újrakonfigurálás nélkül.

2.1. Kulcsfogalmak

Kezdjük egy rövid áttekintéssel Cassandra néhány kulcsfogalmáról:

  • Fürt - csomópontok vagy adatközpontok gyűrűs architektúrába rendezett gyűjteménye. Minden fürthöz hozzá kell rendelni egy nevet, amelyet később a résztvevő csomópontok használni fognak
  • Kulcstér - Ha relációs adatbázisból származik, akkor a séma a megfelelő kulcstér Cassandrában. A kulcstér a Cassandra adatainak legkülső tárolója. A kulcstérenként beállítandó fő attribútumok a Replikációs tényező, a Replika elhelyezési stratégia és a Oszlopos családok
  • Oszlopcsalád - A Cassandra oszlopcsaládok olyanok, mint a Relációs adatbázisok táblázatai. Minden oszlopcsalád sorok gyűjteményét tartalmazza, amelyeket a Térkép. A kulcs lehetőséget ad a kapcsolódó adatok együttes elérésére
  • Oszlop - A Cassandra oszlopa egy adatstruktúra, amely oszlop nevét, értékét és időbélyegét tartalmazza. Az oszlopok és az oszlopok száma az egyes sorokban eltérhet egy relációs adatbázistól, ahol az adatok jól strukturáltak

3. A Java kliens használata

3.1. Maven-függőség

Meg kell határoznunk a következő Cassandra függőséget a pom.xml, amelynek legújabb verziója itt található:

 com.datastax.cassandra cassandra-driver-core 3.1.0 

A kód beágyazott adatbázis-kiszolgálóval történő teszteléséhez hozzá kell adnunk a cassandra-egység függőség, amelynek legújabb verziója itt található:

 org.cassandraunit cassandra-egység 3.0.0.1 

3.2. Csatlakozás Cassandrához

Ahhoz, hogy a Java-ról csatlakozzunk a Cassandrához, ki kell építenünk egy Fürt tárgy.

Egy csomópont címét meg kell adni kapcsolattartó pontként. Ha nem adunk meg portszámot, akkor az alapértelmezett portot (9042) kell használni.

Ezek a beállítások lehetővé teszik az illesztőprogram számára, hogy felfedezze a fürt aktuális topológiáját.

public class CassandraConnector {private Cluster cluster; privát ülésszak; public void connect (Karakterlánc-csomópont, Egész port) {Builder b = Cluster.builder (). addContactPoint (csomópont); if (port! = null) {b.Port (port); } klaszter = b.build (); session = cluster.connect (); } public Session getSession () {return this.session; } public void bezárás () {session.close (); cluster.close (); }}

3.3. A Kulcsterület létrehozása

Hozzuk létre akönyvtár”Kulcstér:

public void createKeyspace (Karakterlánc kulcstér neve, Karakterlánc replikációStrategy, int replikációs Faktor) {StringBuilder sb = új StringBuilder ("CREATE KEYSPACE HA NEM LÉTEZIK") .append (keyspaceName) .append ("Replikációval = {") .append ("'osztály' : '"). append (replicationStrategy) .append ("', 'replication_factor': "). append (replicationFactor) .append ("}; "); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Kivéve a keyspaceName meg kell határoznunk még két paramétert, a replicationFactor és a replikációStrategy. Ezek a paraméterek határozzák meg a replikák számát, és azt, hogy a replikák hogyan oszlanak el a gyűrűn.

A replikációval a Cassandra biztosítja a megbízhatóságot és a hibatűrést azáltal, hogy az adatok másolatait több csomópontban tárolja.

Ezen a ponton tesztelhetjük, hogy a kulcsterületünk sikeresen elkészült-e:

privát KeyspaceRepository schemaRepository; privát ülésszak; @ Mielőtt nyilvános void connect () {CassandraConnector kliens = új CassandraConnector (); client.connect ("127.0.0.1", 9142); this.session = ügyfél.getSession (); schemaRepository = új KeyspaceRepository (munkamenet); }
@Test public void whenCreatingAKeyspace_thenCreated () {String keyspaceName = "könyvtár"; schemaRepository.createKeyspace (keyspaceName, "SimpleStrategy", 1); ResultSet result = session.execute ("SELECT * FROM system_schema.keyspaces;"); List matchedKeyspaces = result.all () .stream () .filter (r -> r.getString (0) .equals (keyspaceName.toLowerCase ())) .map (r -> r.getString (0)) .collect ( Collectors.toList ()); assertEquals (matchedKeyspaces.size (), 1); assertTrue (matchedKeyspaces.get (0) .equals (keyspaceName.toLowerCase ())); }

3.4. Oszlopcsalád létrehozása

Most hozzáadhatjuk az első oszlopcsalád „könyveket” a meglévő kulcsterülethez:

privát statikus végleges karakterlánc TABLE_NAME = "könyvek"; privát ülésszak; public void createTable () {StringBuilder sb = new StringBuilder ("TABLE létrehozása, ha nem létezik") .append (TABLE_NAME) .append ("(") .append ("id uuid PRIMARY KEY,") .append ("címszöveg, ") .append (" tárgy szöveg ";"); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Az oszlopcsalád létrehozásának tesztelésére szolgáló kód az alábbiakban található:

privát BookRepository bookRepository; privát ülésszak; @ Mielőtt nyilvános void connect () {CassandraConnector kliens = új CassandraConnector (); client.connect ("127.0.0.1", 9142); this.session = client.getSession (); bookRepository = új BookRepository (munkamenet); }
@Test public void whenCreatingATable_thenCreatedCorrectly () {bookRepository.createTable (); ResultSet result = session.execute ("SELECT * FROM" + KEYSPACE_NAME + ".books;"); List columnNames = result.getColumnDefinitions (). AsList (). Stream () .map (cl -> cl.getName ()) .collect (Collectors.toList ()); assertEquals (oszlopNevek.size (), 3); assertTrue (columnNames.contains ("id")); assertTrue (oszlopNevek.tartalmaz ("cím")); assertTrue (columnNames.contains ("tárgy")); }

3.5. Az oszlopcsalád megváltoztatása

Egy könyvnek van kiadója is, de a létrehozott táblázatban nem található ilyen oszlop. A következő kód segítségével módosíthatjuk a táblázatot és hozzáadhatunk egy új oszlopot:

public void alterTablebooks (String columnName, String columnType) {StringBuilder sb = new StringBuilder ("ALTER TABLE") .append (TABLE_NAME) .append ("ADD") .append (columnName) .append ("") .append (columnType) .mellékel(";"); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Győződjünk meg arról, hogy az új oszlop kiadó hozzá lett adva:

@Test public void whenAlteringTable_thenAddedColumnExists () {bookRepository.createTable (); bookRepository.alterTablebooks ("kiadó", "szöveg"); ResultSet result = session.execute ("SELECT * FROM" + KEYSPACE_NAME + "." + "Books" + ";"); logikai oszlopExists = result.getColumnDefinitions (). asList (). stream () .anyMatch (cl -> cl.getName (). egyenlő ("kiadó")); assertTrue (columnExists); }

3.6. Adatok beszúrása az oszlopcsaládba

Most, hogy a könyveket táblázat elkészült, készen állunk az adatok hozzáadására a táblához:

public void insertbookByTitle (Book book) {StringBuilder sb = new StringBuilder ("INSERT INTO") .append (TABLE_NAME_BY_TITLE) .append ("(id, title)") .append ("VALUES (") .append (book.getId ( )) .append ("," ") .append (book.getTitle ()). append (" '); "); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Új sor került a „könyvek” táblázatba, így tesztelhetjük, hogy létezik-e a sor:

@Test public void whenAddingANewBook_thenBookExists () {bookRepository.createTableBooksByTitle (); Karakterlánc címe = "Hatékony Java"; Könyvkönyv = új könyv (UUIDs.timeBased (), cím, "Programozás"); bookRepository.insertbookByTitle (könyv); Book savedBook = bookRepository.selectByTitle (cím); assertEquals (book.getTitle (), savedBook.getTitle ()); }

A fenti tesztkódban egy másik módszert alkalmaztunk egy nevű tábla létrehozására booksByTitle:

public void createTableBooksByTitle () {StringBuilder sb = new StringBuilder ("Táblázat létrehozása, ha nem létezik") .append ("booksByTitle"). append ("(") .append ("id uuid,") .append ("címszöveg, ") .append (" ELSŐKULCS (cím, id)); "); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Cassandrában az egyik legjobb gyakorlat az, ha lekérdezésenként egy táblázatot használunk. Ez azt jelenti, hogy egy másik lekérdezéshez más táblára van szükség.

Példánkban úgy döntöttünk, hogy egy könyvet a címe alapján választunk ki. Annak érdekében, hogy kielégítse a selectByTitle lekérdezéssel létrehoztunk egy táblázatot egy vegyülettel ELSŐDLEGES KULCS az oszlopok használatával, cím és id. Az oszlop cím a partíciókulcs, míg a id oszlop a fürtözési kulcs.

Így az adatmodelljének számos táblázata duplikált adatokat tartalmaz. Ez nem hátránya ennek az adatbázisnak. Épp ellenkezőleg, ez a gyakorlat optimalizálja az olvasások teljesítményét.

Lássuk a táblázatunkba jelenleg mentett adatokat:

public List selectAll () {StringBuilder sb = new StringBuilder ("SELECT * FROM") .append (TABLE_NAME); Karakterlánc lekérdezés = sb.toString (); ResultSet rs = session.execute (lekérdezés); Könyvek listája = new ArrayList (); rs.forEach (r -> {books.add (új könyv (r.getUUID ("id"), r.getString ("title"), r.getString ("subject"));}); Könyvek visszaküldése; }

A várt eredményeket visszaadó lekérdezés tesztje:

@Test public void whenSelectingAll_thenReturnAllRecords () {bookRepository.createTable (); Könyvkönyv = új könyv (UUIDs.timeBased (), "Hatékony Java", "Programozás"); bookRepository.insertbook (könyv); könyv = új könyv (UUIDs.timeBased (), "Tiszta kód", "Programozás"); bookRepository.insertbook (könyv); Könyvek listája = bookRepository.selectAll (); assertEquals (2, könyvek.méret ()); assertTrue (books.stream (). anyMatch (b -> b.getTitle () .egyenlő ("Hatékony Java"))); assertTrue (books.stream (). anyMatch (b -> b.getTitle () .equals ("Clean Code"))); }

Eddig minden rendben van, de egyet meg kell valósítani. Az asztallal kezdtünk dolgozni könyvek, de addig is annak kielégítése érdekében válassza lekérdezése cím oszlopban, létre kellett hoznunk egy másik nevű táblázatot booksByTitle.

A két táblázat megegyezik, amelyek duplikált oszlopokat tartalmaznak, de csak adatokat illesztettünk be a booksByTitle asztal. Ennek következtében a két táblázat adatai jelenleg következetlenek.

Megoldhatjuk ezt a tétel lekérdezés, amely két beszúrási utasítást tartalmaz, mindegyik táblához egyet. A tétel A lekérdezés több DML utasítást hajt végre egyetlen műveletként.

Példa egy ilyen lekérdezésre:

public void insertBookBatch (Book book) {StringBuilder sb = new StringBuilder ("BEGIN BATCH") .append ("INSERT INTO") .append (TABLE_NAME) .append ("(id, title, subject)") .append ("VALUES (") .append (book.getId ()). append (", "") .append (book.getTitle ()). append ("','") .append (book.getSubject ()). append ( "');") .append ("INSERT INTO") .append (TABLE_NAME_BY_TITLE) .append ("(id, title)") .append ("VALUES (") .append (book.getId ()). append ( "," ") .append (book.getTitle ()). append (" '); ") .append (" APPLY BATCH; "); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Ismét a kötegelt lekérdezés eredményeit teszteljük így:

@Test public void whenAddingANewBookBatch_ThenBookAddedInAllTables () {bookRepository.createTable (); bookRepository.createTableBooksByTitle (); Karakterlánc címe = "Hatékony Java"; Könyvkönyv = új könyv (UUIDs.timeBased (), cím, "Programozás"); bookRepository.insertBookBatch (könyv); Könyvek listája = bookRepository.selectAll (); assertEquals (1, books.size ()); assertTrue (books.stream (). anyMatch (b -> b.getTitle (). egyenlő ("Effektív Java"))); KönyvekByTitle = bookRepository.selectAllBookByTitle () felsorolása; assertEquals (1, booksByTitle.size ()); assertTrue (booksByTitle.stream (). anyMatch (b -> b.getTitle (). egyenlő ("Effektív Java"))); }

Megjegyzés: A 3.0 verziótól kezdve elérhető egy új „Materializált nézetek” nevű szolgáltatás, amelyet használhatunk tétel lekérdezések. A „Materializált nézetek” dokumentált példája itt érhető el.

3.7. Az oszlopcsalád törlése

Az alábbi kód megmutatja, hogyan kell törölni a táblázatot:

public void deleteTable () {StringBuilder sb = new StringBuilder ("DROP TABLE HA LÉTEZIK") .append (TABLE_NAME); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

Egy olyan tábla kiválasztása, amely nem létezik a kulcstérben, egy InvalidQueryException: nem konfigurált asztali könyvek:

@Test (várható = InvalidQueryException.class) public void whenDeletingATable_thenUnconfiguredTable () {bookRepository.createTable (); bookRepository.deleteTable ("könyvek"); session.execute ("SELECT * FROM" + KEYSPACE_NAME + ".books;"); }

3.8. A Kulcstér törlése

Végül töröljük a kulcsteret:

public void deleteKeyspace (String keyspaceName) {StringBuilder sb = new StringBuilder ("DROP KEYSPACE") .append (keyspaceName); Karakterlánc lekérdezés = sb.toString (); session.execute (lekérdezés); }

És tesztelje, hogy a kulcstér törölve lett-e:

@Test public void whenDeletingAKeyspace_thenDoesNotExist () {String keyspaceName = "könyvtár"; schemaRepository.deleteKeyspace (keyspaceName); ResultSet result = session.execute ("SELECT * FROM system_schema.keyspaces;"); logikai isKeyspaceCreated = result.all (). stream () .anyMatch (r -> r.getString (0) .equals (keyspaceName.toLowerCase ())); assertFalse (isKeyspaceCreated); }

4. Következtetés

Ez az oktatóanyag a Cassandra adatbázishoz való csatlakozás és a Java használatával végzett alapvető lépéseket ismertette. Ennek az adatbázisnak néhány kulcsfontosságú fogalmát szintén megvitatták annak érdekében, hogy elősegítsék az indulást.

A bemutató teljes megvalósítása megtalálható a Github projektben.