簡単なデータ操作
以下、Postgresのtest1テーブルからtest2テーブルへとすべての行をコピーするプログラムの例を見ていきましょう。Postgresのテーブル操作と、Scalaのプログラムは以下のとおりです(LIST31)。
●test1テーブルを作成するSQL id bigserial プライマリキー name text 名前 price integer 値段 CREATE TABLE test1 ( id bigserial NOT NULL, "name" text, price integer, CONSTRAINT test1_pkey PRIMARY KEY (id) ) ●test2テーブルを作成するSQL id Long プライマリキー(DB側で自動生成) name string 名前 price int 値段 CREATE TABLE test2 ( id bigserial NOT NULL, "name" text, price integer, CONSTRAINT test2_pkey PRIMARY KEY (id) ) ●先ほどのサンプルmain関数を、次に置き換えて実行する /** main関数 */ override def main(args:Array[String]) = { val connection = connectPostgreSQL //一般的なSQLの実行方法 val statementTest1 = connection.createStatement val resultSet = statementTest1.executeQuery("SELECT * FROM test1") //PrepareStatementを使用した実行方法 val statementTest2 = connection.prepareStatement("INSERT INTO test2 (name, price) VALUES (?, ?)") while(resultSet.next){ /////PrepareStatementの準備 //test2テーブルの1番目のフィールド(name)に、test1テーブルからSELECTしたデータのnameフィールドの値をセット statementTest2.setString(1, resultSet.getString("name")) //test2テーブルの2番目のフィールド(price)に、test2テーブルからSELECTしたデータのpriceフィールドの値をセット statementTest2.setInt(2, resultSet.getInt("price")) /////PrepareStatementの実行 statementTest2.executeUpdate } //リソースを閉じる resultSet.close statementTest1.close statementTest2.close closePostgreSQL(connection) }
Javaをお使いの方なら、すぐに理解できますね(O/Rマッパーに慣れた方々の目にはいささか原始的な例かもしれませんが)。test1テーブルからSELECTする部分は、java.sql.Statementオブジェクトを使用してSQL文を実行しています。また、test2テーブルへINSERTする部分は、java.sql.PrepareStatementオブジェクトを使用してSQL文を実行しています(実行速度や型を事前チェックできるという利点から、Javaでは、PrepareStatementオブジェクトの利用が推奨されています。こうした点はScalaでも同様です)。
Scalaは、Java向けに書かれたソースコードだけでなく、Web上の情報や書籍など、言語についての情報資産もScalaの文法に読み替えるだけで理解できるということがお分かりいただけたのではないでしょうか。
Liftwebを用いた開発
Liftweb(以下、Lift)とは、Scalaで書かれたWebフレームワークです。Lift自体がScalaを最大限活用して書かれており、高速かつ、スケーラブルなフレームワークになっており、Tomcat、Jettyなど、Javaで書かれたさまざまなAPサーバ上で動作します。
また、snippetと呼ばれる独自のモデルを採用し、HTMLとScalaコードを簡単かつ上手に分割できる仕組みを持っています。また、標準機能としてAjaxを提供しているといった特徴もあります。
JavaのフレームワークにはStrutsなどがありますが、Scalaと同じで高機能かつ手軽に扱え、生産性が高いことから、筆者らはLiftを採用しました。Liftについては、本稿では簡単なサンプルだけに留めておきますが、いずれ機会があれば実案件で用いた開発経験について紹介をしたいと思っています。
LiftのO/Rマッパークラスの使用例
LiftはMavenでの開発を前提としているので、先ほどMavenで作成したLiftwebプロジェクトを基に説明します。
Liftには、デフォルトでO/Rマッパーが付属しているので、¥todo¥src¥main¥scala¥com¥liftworkshop¥model
フォルダ以下に、各自が使用するテーブルごとのマッピングクラスを用意すると簡単にマッピングできます。ここでは、PostgreSQLへの接続例で使用したtest1テーブルを、LiftwebのO/Rマッパーを使用してマッピングするサンプルを示します。
前述のmodelフォルダ内にTest1.scalaという名前のファイルを作成し、LIST32に示すサンプルコードを記述します。マッピングする際には、Liftweb側で用意されたテンプレート(Mapper)の中から、実際のテーブル構造に合ったものを継承してクラスを作成します。
package com.liftworkshop.model import _root_.net.liftweb.mapper._ //objectとする事で、アプリケーション全体で共有して使用出来る object Test1Table extends Test1Table with KeyedMetaMapper[Long, Test1Table] { //データベースのテーブル名を指定する override def dbTableName = "test1" } class Test1Table extends KeyedMapper[Long, Test1Table] with CRUDify[Long, Test1Table]{ def getSingleton = Test1Table def primaryKeyField = id //プライマリキーを指定 //以下、フィールドのマッピング //net.liftweb.mapper.MappedField クラスのサブクラスから型の合う物を選択して、DBの列の型と、lift上での型を合わせる object id extends MappedLongIndex(this) object name extends MappedText(this) object price extends MappedInt(this) }
継承するMapperによって使用できる関数が異なるため、特に問題がない限りはテーブル側にプライマリキーを付与して、KeyedMapperクラス(または、そのサブクラス)を継承することを推奨します。ここで、classとobjectに同一の名称(Test1Table)が付けられていることが気になったものと思います。Scalaでは、同一ファイル内でclassとobjectに同一の名称をつけることで、お互いのプライベート・クラスにアクセスできるようになります(コンパニオンオブジェクトと呼んでいます)。