ORMのない生活はありますか?

プロジェクトをjavaからclojureに移行した後、データベースを操作する通常の手段に代わるものを見つける必要がありました。



Clojureには、clojure.java.jdbcデータベースを操作するための標準ライブラリと、それに基づいて提供されるeDSLでクエリを作成できるいくつかのライブラリがあります。 しかし、「毎日の使用」のために、JPAに似た便利なものが必要であり、IDEでそれを使用しました。



私たちは、理想的に私たちの要件に合った独自のライブラリを書かないと考えました。 また、要件は次のとおりです。





その後、データベースの操作は以前と同じくらい便利になります。 そのため、2〜3日後にライブラリの準備が整いました。



次の公開機能が実装されました。

-generate-table-column-names :テーブル名の生成と特定のネームスペースの変数としてのフィールドに関する情報。

-generate-column-value-constants :特定の名前空間の特定のテーブルのフィールドからの定数の生成。

-with-db :新しい接続を作成し、例外がスローされたときに本体がロールバックを実行するトランザクションを実行するマクロ。

-select :現在の接続を使用して選択します。

-select-with-db :指定された説明に従って新しい接続を作成する選択。

-select-deep :外部キーによる他のテーブルへのバインドを使用して、指定された説明に従って新しい接続を作成する選択。

-get-field-from-row :外部キーによってリンクされたテーブルフィールドのチェーンを操作する機能を持つレコードからフィールドを取得します。

- 更新 、 挿入 。



使用例





たとえば、接続の説明があります。

( def db { : クラス名 "com.mysql.jdbc.Driver"

: サブプロトコル 「mysql」

: サブネーム "// localhost:3306 / clj_query"

: ユーザー 「clj」

: パスワード "clj" } )




作業ベースのテーブル、そのフィールド、およびテーブルからいくつかの定数を生成します。



user > ( ' [ libs.db.gentablecolumns : as gen ]が必要 )

なし

user > ( ' [ libs.db.gencolumnvalues : as genval ]が必要 )

なし

ユーザー> ( gen / generate-table-column-names db )

なし

ユーザー> ( genval / generate-column-value-constants db table-recordtypes ( : name recordtypes- name ) )

なし




その結果、次のソースが得られます。



entities.clj:

( ns db。entities )



;;;; 選手

( def table-players "players" )

( def player-id { : タイプ { : サイズ 10 、: name "INT UNSIGNED" } 、: テーブル "players" 、: name "id" } )

( def player- name { : タイプ { : サイズ 255 、: name "VARCHAR" } 、: テーブル "players" ,: name "name" } )

( def player-type_id { : タイプ { : サイズ 10 、: 名前 "INT UNSIGNED" } 、: テーブル "players "、: 名前 "type_id" } )



;;;; プレイヤータイプ

( def table-playertypes "playertypes" )

( def playertypes-id { : タイプ { : サイズ 10 、: 名前 " INT UNSIGNED" } 、: テーブル "playertypes" ,: 名前 "id" } )

( def playertypes- name { : type { : size 255 、: name "VARCHAR" } 、: テーブル "playertypes" ,: name "name" } )



;;;; 記録

( def table-records "records" )

( def records-id { : タイプ { : サイズ 10 、: 名前 "INT UNSIGNED" } 、: テーブル "records" 、: 名前 "id" } )

( def records-type_id { : タイプ { : サイズ 10 、: 名前 "INT UNSIGNED" } 、: テーブル "records "、: 名前 "type_id" } )

( def records-score { : タイプ { : サイズ 19 、: 名前 "BIGINT" } 、: テーブル "records" 、: 名前 "score" } )

( def records-player_id { : タイプ { : サイズ 10 、: 名前 "INT UNSIGNED" } 、: テーブル "records "、: 名前 "player_id" } )



;;;; レコードタイプ

( def table-recordtypes "recordtypes" )

( def recordtypes-id { : タイプ { : サイズ 10 、: 名前 " INT UNSIGNED" } 、: テーブル "recordtypes" ,: 名前 "id" } )

( def recordtypes- name { : type { : size 255 、: name "VARCHAR" } 、: テーブル "recordtypes" ,: name "name" } )




recordtypes.clj:

( ns db。recordtypes )



( def name-kills-per-round-id 1 )

( def name-kills-per-round "kills per round" )

( def name-kills-per-game-id 2 )

( def name-kills-per-game "ゲームごとのキル数" )

( def name-longest-undead-time-id 3 )

( def name-longest-undead-time "最長アンデッド時間" )




ユーザーまたはボットがラウンド中に最も多くのキルを記録したことを知りたいとしましょう:



user > ( ' [ db。recordtypes : as rectypes ]が必要 )

なし

user > ( 'db。entitiesを使用)

なし

user > ( def record ( first ( q / select-deep db table-records

: 結合 [ [ table-recordtypes [ := recordtypes-id records-type_id ] ] ]

: [ := recordtypes-id rectypes / name-kills-per-round-id ] ) ) )

# 'ユーザー/レコード




外部キーによってリンクされたテーブルを介してプレーヤーのタイプを取得します。



ユーザー> ( q / get-field-from-rowレコードrecords-player_idplayers-type_id playertypes- name )

「ボット」




結果:レコードの所有者は「ボット」です。

そして最も重要なことは、アドオンはどこでも機能していることです(キーワードのみを補完するものではありません)。



図書館プラス



-定数、テーブル、およびフィールドの自動補完。

-テーブル、フィールド、または定数の名前を変更するときに上記の世代を特定の方法でマクロに挿入すると、実行段階ではなくコンパイル時に正確にエラーを取得できます。

-生成されたデータを処理できる便利なeDSL(更新、挿入、選択-...、with-db)。



その結果、clojureでのプロジェクトでのデータベースの操作は、以前よりも簡単、柔軟、便利になりました。



All Articles