Tauchen Sie ein in Berkeley DB JE. Einführung in die Collections-API

  • Tutorial

Einleitung


Ein wenig über das Thema. BerkleyDB ist ein eingebettetes Hochleistungs-DBMS, das als Bibliothek für verschiedene Programmiersprachen bereitgestellt wird. Diese Lösung umfasst das Speichern von Schlüssel-Wert-Paaren sowie die Möglichkeit, einem Schlüssel mehrere Werte zuzuweisen. BerkeleyDB unterstützt Multithreading, Replikation und mehr. Die Aufmerksamkeit dieses Artikels wird in erster Linie auf die Nutzung der von Sleepycat Software in den bärtigen 90ern bereitgestellten Bibliothek gelenkt.

Im vorherigen ArtikelWir haben die wichtigsten Aspekte der Arbeit mit der Direct Persistence Layer-API untersucht, dank derer Sie mit Berkeley als relationale Datenbank arbeiten können. Heutzutage wird jedoch der Collections-API Aufmerksamkeit geschenkt, die die Möglichkeit bietet, alle bekannten Schnittstellenadapter für Java Collections zu verarbeiten.

Hinweis : Alle Beispiele in diesem Artikel werden in der Kotlin-Sprache angegeben.

Einige allgemeine Informationen


Wir alle wissen, dass das Arbeiten mit Annotationen, verzögerten Initialisierungen und nullfähigen Typen in Kotlin ein großer Schmerz ist. Aufgrund seiner Spezifität lässt DPL nicht zu, dass diese Probleme beseitigt werden, und die einzige Lücke besteht in der Erstellung seiner Implementierung EntityModel- einem Mechanismus, der bestimmt, wie mit Behältern gearbeitet wird. Der Hauptanreiz für mich, die Collections-API zu verwenden, ist die Fähigkeit, mit völlig sauberen, vertrauten Datenklassen-Beans zu arbeiten. Schauen wir uns an, wie der Code aus dem vorherigen Artikel in dieses Framework übertragen wird.

Beschreibung der Entitäten


Um ein Objekt in der Datenbank zu beschreiben, das als separate Entität dargestellt werden kann, müssen drei Klassen erstellt werden: eine gesamte Entität, die außerhalb des Kontexts der Datenbank, ihres Schlüssels und ihrer Daten ausgeführt wird.

Es werden keine Anforderungen an das Wesentliche gestellt, und der Schlüssel und der Wert (in der Standardarbeit mit der Datenbank, die in diesem Artikel behandelt wird) sind erforderlich, um die Schnittstelle zu implementieren Serializable. Hier ist alles Standard, wir möchten In-Memory-Felder - fügen Sie Anmerkungen hinzu @Transient. Alles, was nicht als @Transientserialisiert markiert ist, wird serialisiert.

Wie wir uns erinnern, ist es zum Organisieren von Datensätzen in einem Beispiel erforderlich, eine Schnittstellenimplementierung als Schlüssel zu verwenden Comparable. Hier ist das Prinzip dasselbe: Die Auswahl wird nach Schlüssel sortiert.

Bean Description Beispiel
data class CustomerDBO(
        val id: String,
        val email: String,
        val country: String,
        val city: String,
        var balance: Long
)
data class CustomerKey(
        val id: String
): Serializable
data class CustomerData(
        val email: String,
        val country: String,
        val city: String,
        val balance: Long
): Serializable


Unternehmenstätigkeit


Um eine Verbindung mit der Collections-API herzustellen, müssen Sie ein wenig schwitzen. Zunächst sollten Sie das Prinzip der Arbeit im häufigsten Fall berücksichtigen - N: 1.

Wir EnvironmentConfiglassen die Standardschritte für die Erstellung aus , da sie sich nicht grundlegend von der Konfiguration für DPL unterscheiden. Die Unterschiede beginnen unmittelbar danach.

Für jede Entität müssen wir eine separate Datenbank erstellen, die einen eindeutigen Namen hat. Außerdem müssen wir eine separate Datenbank erstellen, in der Informationen zu den Entitäten in dieser Entität gespeichert Environmentund eingeschlossen werden ClassCatalog. Wir können sagen, dass Datenbanken in Berkeley ungefähr dasselbe Wesen haben wie Tabellen in SQL. Ein Beispiel unter einer Katze.

Erstellen einer Datenbank für eine Entität und ein Verzeichnis
    private val databaseConfig by lazy {
        DatabaseConfig().apply {
            transactional = true
            allowCreate = true
        }
    }
    private val catalog by lazy {
        StoredClassCatalog(environment.openDatabase(null, STORAGE_CLASS_CATALOG, databaseConfig))
    }
    val customersDatabase by lazy {
        environment.openDatabase(null, STORAGE_CUSTOMERS, databaseConfig)
    }


Darüber hinaus ist es logisch, dass wir eine bequeme Kontaktstelle mit dem Framework benötigen, da es selbst Databaseeine sehr unangenehme Low-Level-API hat. Diese Adapter sind StoredSortedMapund Klassen StoredValueSet. Es ist am bequemsten, den ersten als unveränderlichen Kontaktpunkt mit der Datenbank und den zweiten als veränderlichen zu verwenden.

Sammeladapter

    private val view = StoredSortedMap(
           customersDatabase,
           customerKeyBinding,
           customerDataBinding,
           false
    )
    private val accessor = StoredValueSet(
           customersDatabase,
           customerBinding,
           true
    )


Möglicherweise stellen Sie fest, dass Berkeley im Moment nicht weiß, wie das Mapping durchgeführt wird (key, data) -> (dbo)und dbo -> (key, data). Damit die Zuordnung funktioniert, muss für jede der Klassen ein weiterer Mechanismus implementiert werden - die Bindung. Die Benutzeroberfläche ist äußerst einfach - zwei Methoden für die Zuordnung zu Daten und zu Schlüsseln sowie eine für das Wesentliche.

Ein verbindliches Beispiel
class CustomerBinding(
        catalog: ClassCatalog
): SerialSerialBinding(catalog, CustomerKey::class.java, CustomerData::class.java) {
    override fun entryToObject(key: CustomerKey, data: CustomerData): CustomerDBO = CustomerDBO(
            id = key.id,
            email = data.email,
            country = data.country,
            city = data.city,
            balance = data.balance
    )
    override fun objectToData(dbo: CustomerDBO): CustomerData = CustomerData(
            email = dbo.email,
            country = dbo.country,
            city = dbo.city,
            balance = dbo.balance
    )
    override fun objectToKey(dbo: CustomerDBO): CustomerKey = CustomerKey(
            id = dbo.id
    )
}


Jetzt können wir sicher funktionierende Sammlungen verwenden, die automatisch synchronisiert werden, wenn sich Daten in der Datenbank ändern. Gleichzeitig ist der „Brei“ die Fähigkeit, in Ruhe paralleles Schreiben und Lesen aus verschiedenen Streams zu verwenden. Diese Möglichkeit beruht hauptsächlich auf der Tatsache, dass iteratordiese Sammlungen eine Kopie des aktuellen Status sind und sich nicht ändern, wenn sich die Sammlungen ändern, während die Sammlungen selbst veränderbar sind. Das einzige, woran ein Programmierer denken sollte, ist die Überwachung der Datenrelevanz.

Nun, dann haben wir mit dem üblichen CRUD herausgefunden, dass wir zu den Verbindungen gehen!

Beziehungen zwischen Entitäten


Um mit Beziehungen arbeiten zu können, müssen wir zusätzlich eine erstellen SecondaryDatabase, die den Zugriff auf eine Entität mithilfe des Schlüssels einer anderen ermöglicht. Eine wichtige Beobachtung ist die Notwendigkeit , in dem zu spezifizieren DatabaseConfigWert sortedDuplicatessowohl true, wenn das Verhältnis nicht 1: 1 oder 1: M. Diese Aktion ist sehr logisch, da die Indizierung für einen Fremdschlüssel erfolgt und mehrere Entitäten einem Schlüssel entsprechen.

Sekundäres DB-Beispiel mit Konfiguration
    val ordersByCustomerIdDatabase by lazy {
        environment.openSecondaryDatabase(null, STORAGE_ORDERS_BY_CUSTOMER_ID, ordersDatabase, SecondaryConfig().apply {
            transactional = true
            allowCreate = true
            sortedDuplicates = true
            keyCreator = OrderByCustomerKeyCreator(catalog = catalog)
            foreignKeyDatabase = customersDatabase
            foreignKeyDeleteAction = ForeignKeyDeleteAction.CASCADE
        })
    }


Es ist bemerkenswert, dass Sie als Fremdschlüssel nicht nur das Feld auswählen können, über das die Verbindung hergestellt wird, sondern auch einen beliebigen Datentyp. Die Implementierung der Schnittstelle übernimmt die Rolle des Erstellens von Schlüsseln SecondaryKeyCreatoroder SecondaryMultiKeyCreator(spezifischere Optionen sind vorhanden, es ist jedoch ausreichend, eine dieser beiden zu implementieren).

SecondaryKeyCreator Beispiel
class OrderByCustomerKeyCreator(
        catalog: ClassCatalog
): SerialSerialKeyCreator(catalog, OrderKey::class.java, OrderData::class.java, CustomerKey::class.java) {
    override fun createSecondaryKey(key: OrderKey,
                                    data: OrderData): CustomerKey = CustomerKey(
            id = data.customerId
    )
}


Es bleibt ein bisschen - um eine Sammlung zum Abrufen von Mustern mit unseren Fremdschlüsseln zu erstellen, unterscheidet sich der Code unter dem Schnitt nicht vom Erstellen einer Sammlung für nicht sekundäre Datenbanken.

Erstellen einer Sammlung für die Stichprobe N: 1
private val byCustomerKeyView = StoredSortedMap(
            database.ordersByCustomerIdDatabase,
            database.customerKeyBinding,
            database.orderDataBinding,
            false
    )


Anstelle einer Schlussfolgerung


Dieser Artikel war der letzte einer Reihe über die Einführung in die Grundlagen der Arbeit mit BerkeleyDB. Nach dem Lesen dieses und des vorherigen Artikels kann der Leser das DBMS in seinen Projekten als lokalen Speicher verwenden, z. B. in einer Client-Anwendung. In den folgenden Artikeln werden weitere interessante Aspekte behandelt - Migration, Replikation und einige interessante Konfigurationsparameter.

Wie üblich - falls jemand Ergänzungen oder Korrekturen an meinen Lagerbeständen hat - können Sie dies gerne kommentieren. Über konstruktive Kritik freue ich mich immer!

Jetzt auch beliebt: