Berkeley DB JEに飛び込みます。 コレクションAPIの概要

はじめに



主題について少し。 BerkleyDBは、さまざまなプログラミング言語のライブラリとして提供される高性能な組み込みDBMSです。 このソリューションには、キーと値のペアの保存が含まれ、複数の値を1つのキーに割り当てる機能もサポートしています。 BerkeleyDBは、マルチスレッド、レプリケーションなどをサポートしています。 この記事の注意は、主にひげを生やした90年代にSleepycat Softwareによって提供されたライブラリの使用に向けられます。



前回の記事では、Direct Persistence Layer APIを使用する主な側面について説明しました。これにより、Berkeleyをリレーショナルデータベースとして使用できます。 ただし、今日、Collections APIに注意が払われます。CollectionsAPIは、使い慣れたすべてのJava Collectionsインターフェースアダプターを介して作業する機能を提供します。



:この記事のすべての例は、Kotlin言語で提供されます。



一般的な情報



Kotlinでアノテーション、遅延初期化、null許容型を操作することは、大きな苦痛であることは誰もが知っています。 その特異性により、DPLではこれらの問題を排除することはできず、唯一の抜け穴はEntityModel



実装の作成です。これは、ビンの処理方法を決定するメカニズムです。 私にとって、Collections APIを使用する主な動機は、完全にクリーンで使い慣れたデータクラスBeanを操作できることです。 前の記事のコードをこのフレームワークに転送する方法を見てみましょう。



エンティティの説明



個別のエンティティとして表すことができるデータベース内のオブジェクトを記述するには、3つのクラスを作成する必要があります。データベースのコンテキスト外で操作するエンティティ全体、そのキーおよびデータです。



本質に要件はありませんSerializable



インターフェイスを実装するには、キーと値(この記事で取り上げるデータベースとの標準的な作業)が必要です。 ここではすべてが標準であり、インメモリフィールドが必要です- @Transient



アノテーションを追加します。 @Transient



としてマークされていないものはすべてシリアル化されます。



覚えているように、選択範囲内のレコードを整理するには、 Comparable



インターフェースの実装をキーとして使用する必要があります。 ここでは、原則は同じです。選択はキーでソートされます。



Beanの説明の例
 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
      
      







エンティティの操作



Collections APIとの接続を作成するには、少し汗をかかなければなりません。 まず、最も一般的な場合の作業の原則を検討する必要があります-N:1。



EnvironmentConfig



を作成するための標準手順は、DPLの構成と基本的に変わらないため、省略します。 違いはそれらの直後に始まります。



エンティティごとに、一意の名前を付けて個別のデータベースを作成する必要があります。さらに、このEnvironment



のエンティティに関する情報を格納する個別のデータベースを作成し、 ClassCatalog



ラップするClassCatalog



ます。 バークレーでは、データベースの本質はSQLのテーブルとほぼ同じであると言えます。 猫の下の例。



エンティティとディレクトリのデータベースを作成する
  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) }
      
      







さらに、 Database



自体には非常に厄介な低レベルAPIがあるため、フレームワークとの何らかの便利な連絡先が必要であることは論理的です。 そのようなアダプターは、クラスStoredSortedMap



およびStoredValueSet



です。 1つ目をデータベースとの不変の接点として使用し、2つ目を可変にするのが最も便利です。



収集アダプター


  private val view = StoredSortedMap<CustomerKey, CustomerData>( customersDatabase, customerKeyBinding, customerDataBinding, false ) private val accessor = StoredValueSet<CustomerDBO>( customersDatabase, customerBinding, true )
      
      







現時点では、Berkeleyは(key, data) -> (dbo)



およびdbo -> (key, data)



マッピング方法を知らないことに気付くかもしれません。 マッピングが機能するためには、ビンごとにもう1つのメカニズム(バインディング)を実装する必要があります。 インターフェイスは非常にシンプルです。データとキーへのマッピングには2つの方法があり、エッセンスへのマッピングは1つです。



バインドの例
 class CustomerBinding( catalog: ClassCatalog ): SerialSerialBinding<CustomerKey, CustomerData, CustomerDBO>(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 ) }
      
      







これで、データベース内のデータが変更されると自動的に同期される作業コレクションを安全に使用できます。 同時に、「パルプ」とは、異なるストリームからの並列書き込みおよび読み取りを冷静に使用する能力です。 この可能性は主にiterator



これらのコレクションのiterator



現在の状態のコピーであり、コレクションが変更されてもコレクション自体は変更可能であるにもかかわらず変更されないという事実によるものです。 したがって、プログラマが考えるべき唯一のことは、データの関連性を監視することです。



それでは、通常のCRUDを使用して、接続に移動しました。



エンティティ間の関係



リレーションシップを使用するには、 SecondaryDatabase



を追加で作成する必要があります。SecondaryDatabaseは、他のエンティティのキ​​ーを使用して一部のエンティティへのアクセスを提供します。 重要な注意点は、接続が1:1または1:Mでない場合、 DatabaseConfig



sortedDuplicates



true



として指定する必要があることです。 このアクションは、インデックス付けが外部キーで発生し、複数のエンティティが1つのキーに対応するという事実に基づいて、非常に論理的です。



構成を含むセカンダリDBの例
  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 }) }
      
      







外部キーとして、接続を設定するフィールドだけでなく、任意のデータ型も選択できることに注意してください。 キー作成の役割は、 SecondaryKeyCreator



またはSecondaryMultiKeyCreator



インターフェースの実装によって引き継がれます(より具体的なオプションがありますが、これら2つのうちの1つを実装すれば十分です)。



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







少し残っています-外部キーからサンプルを取得するためのコレクションを作成するには、カットの下のコードは非セカンダリデータベースのコレクションを作成するのと同じです。



サンプリングNのコレクションの作成:1
 private val byCustomerKeyView = StoredSortedMap<CustomerKey, OrderData>( database.ordersByCustomerIdDatabase, database.customerKeyBinding, database.orderDataBinding, false )
      
      







結論の代わりに



この記事は、BerkeleyDBでの作業の基本を紹介するシリーズの最後でした。 この記事と前回の記事を読んだ後、読者はプロジェクトでDBMSをローカルストレージとして、たとえばクライアントアプリケーションで使用できます。 次の記事では、より興味深い側面(移行、複製、いくつかの興味深い構成パラメーター)について検討します。



いつものように-誰かが私の株に追加または修正を加えた場合-あなたはコメントを歓迎します。 建設的な批判にはいつも満足しています!



All Articles