ScalikeJDBCを使用したSQL RDBMSアクセス

画像 ScalaプログラムでSQLの使用を容易にするライブラリがありますが、その言及はハブでは見つかりませんでした この不公平を修正したいと思います。 ScalikeJDBCについてです。



SkalikeJDBCの主な競合相手はAnormです。これは、純粋な(ORM不純物を含まない)SQLを使用してRDBMSとの便利な通信というまったく同じタスクを解決するPlayのライブラリです。 ただし、AnormはPlayに深く没頭しており、Play以外のプロジェクトで使用することは困難な場合があります。 私にとってそれが困難になるのを待ちませんでした。 SkalikeJDBCについて聞いたので、私はすぐに試してみることにしました。 このテストの結果は、小さなデモアプリケーションの形で、この記事のすぐ下で紹介します。



ライブラリの使用例に進む前に、次のDBMSでの動作がサポートおよびテストされていることに注意してください。



また、DBMSとのすべての通信は標準JDBCを経由するため、残り(Oracle、MS SQL Server、DB2、Informix、SQLite、 それらの数千 )も動作するはずです。 ただし、それらのテストは機能せず、企業顧客に落胆をもたらす可能性があります。



応用例



ただし、企業のお客様には悲観的な考えをお任せしますので、この記事の目的を達成する方が良いでしょう。 ライブラリに少し飛び込みます。



次に、SkalikeJDBCを使用してPostgresqlにアクセスする簡単なアプリケーションの例を示します。 Typesafe Configを使用して設定し、データベースにテーブルを作成し、このテーブルにCRUDリクエストを作成し、Readリクエストの結果をScalaオブジェクトに変換する方法を示します。 (Typesafe Configを使用せずに)多数の構成オプションを意図的に見逃し、ライブラリを使用して簡潔にしてクイックスタートを提供します。 機能の詳細な説明は、便利でかなり短いドキュメント 、およびgithubのWikiで利用できます。



アプリケーションはSBTを使用して依存関係を構築および管理するため、空のプロジェクトのルートに次の内容のbuild.sbtファイルを作成します。



name := "scalike-demo" version := "0.0" scalaVersion := "2.11.6" val scalikejdbcV = "2.2.5" libraryDependencies ++= Seq( "org.postgresql" % "postgresql" % "9.4-1201-jdbc41", "org.scalikejdbc" %% "scalikejdbc" % scalikejdbcV, "org.scalikejdbc" %% "scalikejdbc-config" % scalikejdbcV )
      
      





次の依存関係を宣言します。



DBMSとして標準(5432)ポートのローカルPostgresqlを使用します。 すでにパスワードが安全なpguserユーザーとdemo_dbデータベースへのフルアクセス権があります。



この場合、次の内容でsrc / main / resources / application.conf構成ファイルを作成します。



 db { demo_db { driver = org.postgresql.Driver url = "jdbc:postgresql://localhost:5432/demo_db" user = pguser password = securepassword poolInitialSize=10 poolMaxSize=20 connectionTimeoutMillis=1000 poolValidationQuery="select 1 as one" poolFactoryName="commons-dbcp" } }
      
      





最初の4つのパラメーターに制限すると、デフォルトの接続プール設定が適用されます。



次に、src / main / scalaフォルダーにデモパッケージを作成します。ここにすべてのscalaコードを配置します。



DemoApp.scala


メインの起動オブジェクトから始めましょう:



 package demo import scalikejdbc.config.DBs object DemoApp extends App { DBs.setup('demo_db) }
      
      





オブジェクト内の唯一の行は、構成ファイルからdemo_dbデータベースのアクセス設定を読み取るための命令です。 DBオブジェクトは、Typesafe Configによって読み取られたすべての構成ファイルのdb.demo_dbノードで、すべての適切な構成キー(ドライバー、URL、ユーザー、パスワード、...)を検索します。 Typesafe Configは、慣例により、アプリケーションのクラスパスにあるapplication.confを自動的に読み取ります。



その結果、データベースに対して構成されたConnectionPoolが作成されます。



Dbconnected.scala


次に、プールからデータベースへの接続をカプセル化する特性を作成します



 package demo import java.sql.Connection import scalikejdbc.{ConnectionPool, DB, DBSession} import scalikejdbc._ trait DbConnected { def connectionFromPool : Connection = ConnectionPool.borrow('demo_db) // (1) def dbFromPool : DB = DB(connectionFromPool) // (2) def insideLocalTx[A](sqlRequest: DBSession => A): A = { // (3) using(dbFromPool) { db => db localTx { session => sqlRequest(session) } } } def insideReadOnly[A](sqlRequest: DBSession => A): A = { // (4) using(dbFromPool) { db => db readOnly { session => sqlRequest(session) } } } }
      
      





(1)では、最後の手順で作成および構成されたプールから接続(java.sql.Connection)を取得します。

(2)では、結果の接続をscalikeJDBC(Basic Database Accessor)の便利なデータベースアクセスオブジェクトにラップします。

(3)および(4)では、SQLクエリの実行に便利なラッパーを作成します。 (3)変更要求の場合、(4)読み取り要求の場合。 それらがなくてもできますが、どこにでも書かなければなりません:



 def delete(userId: Long) = { using(dbFromPool) { db => db localTx { implicit session => sql"DELETE FROM t_users WHERE id = ${userId}".execute().apply() } } }
      
      





代わりに:



 def delete(userId: Long) = { insideLocalTx { implicit session => sql"DELETE FROM t_users WHERE id = ${userId}".execute().apply() } }
      
      





、DRYはまだキャンセルされていません。



パラグラフ(3)および(4)で何が起こるかをさらに詳しく調べてみましょう。



using(dbFromPool)-データベース接続の開始と終了を1つのリクエストでラップできます。 これがなければ、自分で接続を開き(val db = ThreadLocalDB.create(connectionFromPool))、忘れずに接続を閉じる(db.close())必要があります。



db.localTx-要求が行われるブロッキングトランザクションを作成します。 ブロック内で例外が発生すると、トランザクションはロールバックされます。 詳細。



db.readOnly-読み取りモードで要求を実行します。 詳細。



この特性は、DAOクラスで使用できます。トレーニングアプリケーションには、この特性がちょうど1つあります。



User.scala


DAOクラスの作成を開始する前に、それが機能するドメインオブジェクトを作成します。 これは、3つの会話フィールドを持つシステムユーザーを定義する単純なケースクラスになります。



 package demo case class User(id: Option[Long] = None, name: String, email: Option[String] = None, age: Option[Int] = None)
      
      





名前フィールドのみが必須です。 id == Noneの場合、これはオブジェクトがまだデータベースに保存されていないことを示します。



UserDao.scala


これで、DAOオブジェクトを作成する準備がすべて整いました。



 package demo import scalikejdbc._ class UserDao extends DbConnected { def createTable() : Unit = { insideLocalTx { implicit session => sql"""CREATE TABLE t_users ( id BIGSERIAL NOT NULL PRIMARY KEY , name VARCHAR(255) NOT NULL , email VARCHAR(255), age INT)""".execute().apply() } } def create(userToSave: User): Long = { insideLocalTx { implicit session => val userId: Long = sql"""INSERT INTO t_users (name, email, age) VALUES (${userToSave.name}, ${userToSave.email}, ${userToSave.age})""" .updateAndReturnGeneratedKey().apply() userId } } def read(userId: Long) : Option[User] = { insideReadOnly { implicit session => sql"SELECT * FROM t_users WHERE id = ${userId}".map(rs => User(rs.longOpt("id"), rs.string("name"), rs.stringOpt("email"), rs.intOpt("age"))) .single.apply() } } def readAll() : List[User] = { insideReadOnly { implicit session => sql"SELECT * FROM t_users".map(rs => User(rs.longOpt("id"), rs.string("name"), rs.stringOpt("email"), rs.intOpt("age"))) .list.apply() } } def update(userToUpdate: User) : Unit = { insideLocalTx { implicit session => sql"""UPDATE t_users SET name=${userToUpdate.name}, email=${userToUpdate.email}, age=${userToUpdate.age} WHERE id = ${userToUpdate.id} """.execute().apply() } } def delete(userId: Long) :Unit= { insideLocalTx { implicit session => sql"DELETE FROM t_users WHERE id = ${userId}".execute().apply() } } }
      
      







ここでは、各機能の動作を簡単に推測できます。



SQLオブジェクトは、表記法を使用して作成されます。

 sql"""<SQL Here>""" sql"<SQL Here>"
      
      





このオブジェクトは次のメソッドを使用します。



チェーンの最後はapply()操作で、宣言された暗黙セッションを通じて作成された要求を実行します。



また、$ {userId}のようなパラメーター挿入はすべてPreparedStatementのパラメーター挿入であり、SQLインジェクションを恐れてはならないことに注意してください。



フィニータ


さて、DAOオブジェクトの準備ができました。 もちろん、その中にテーブルを作成する方法を見るのは奇妙です...それは単なる例として追加されました。 トレーニングアプリケーション-余裕があります。 このDAOオブジェクトを適用するだけです。 これを行うには、最初に作成したDemoAppオブジェクトを変更します。 たとえば、次の形式を取ることができます。

 package demo import scalikejdbc.config.DBs object DemoApp extends App { DBs.setup('demo_db) val userDao = new UserDao userDao.createTable() val userId = userDao.create(User(name = "Vasya", age = Some(42))) val user = userDao.read(userId).get val fullUser = user.copy(email = Some("vasya@domain.org"), age = None) userDao.update(fullUser) val userToDeleteId = userDao.create(User(name = "Petr")) userDao.delete(userToDeleteId) userDao.readAll().foreach(println) }
      
      





おわりに


この短いレビューでは、SkalikeJDBCライブラリの機能に注目し、リレーショナルデータにアクセスするためのオブジェクトを作成できる軽さとパワーを感じました。 ORMの優位性の時代には、割り当てられたタスクを適切に解決し、同時に積極的に開発し続けるツールがあります。



ご清聴ありがとうございました。 Scalaがあなたと共に来ますように!



All Articles