Scala+DBMS+Web スカラ座の夜

2011年8月31日

Ruby on Rails のModel classのようにDB操作する

Filed under: Scala — admin @ 10:42 PM

Ruby on Rails

平均的な Ruby on Rails プログラムは SQL コードがあったとしてもその量は極めて限られています。

Ruby on Rails はモデル・ビュー・コントローラー (MVC) パターンをベースにしていることから、大半の Rails アプリケーションは 3 つの構成部分にはっきりと分かれます。
アプリケーションのデータを管理するために必要な振る舞いを受け持つのがモデル(M)です。

一般的に、Ruby on Rails アプリケーションではモデルとデータベース・テーブルの間に 1 対 1 の関係によって実装される傾向にあります。これはデータベースを使う上で重要なポイントです。

この方針は、アプリケーションの構造をシンプルにします。

Ruby on Rails がデフォルトでモデルのデータベース操作に使用するのが、 ActiveRecord というオブジェクト・リレーション・マッピング (ORM) です。



Rails アプリケーション

Ruby on Rails 開発者のスタイルとスタンスは、ActiveRecord が多くの処理を極めて簡単に記述できるため、Rails 開発者は「反 SQL」のスタンスを取りがちです。
このようなスタンスの Rails 開発者は、SQL のほうが理にかなっているとしても、SQL を使いたがりません。



Scala DB アプリケーション

ScalaでDB操作するシステムを開発するとき、
SQL文を意識してプログラムを書くのか、Rails アプリケーションの開発者のように反SQLのスタイルで書くのかは、データベースのスキルに依存します。

ScalaをRuby on Railsのように使いたのであれば、
SQLをこれから覚えるよりは、新しいサービスをとりあえず開発してリリースする方を優先するというスタンスでしょう。




データベースに対するActiveRecordの戦略は、DBのスキーマ情報からモデルクラスを生成することにつきます。
とすれば、Scalaプログラムも同じ生成機能があれば、Ruby on Rails 開発者とのハンディは帳消しできます。

Javaプログラマーは、JavaBeanによってビジネスアプリケーションを書くことに慣れています。
しかし、操作となると DAO (Data Access Object) で相変わらずSQL文と格闘。

ScalaにおいてモデルとなるBeanクラスと、find,create,update,delete操作系関数が生成できれば、いままでこの領域のコードを記述していたよりプログラマーが不要になります。
少人数のプログラマーでより多くの機能をより短時間に生み出すことができると主張するRuby on Rails 開発者と同じです。
結果、ソフトウェア開発費あたりのビジネス価値が増大することに他なりません。



Scalaで使えるBeanクラスは、Seedoフレームワークですでに生成することができるので、操作系のクラスも生成してみよと試みようと次のようなコードを書いてみました。

EMP表を操作するプログラムです。



package ews.servlet.ajax

import scala.collection.mutable.{HashMap,LinkedHashMap,HashSet,ArrayBuffer}
import seedo._
import seedo.database._
import scott.bean._
/**
 * The EMP table is operated. 
 */
class User {
	var key:String = null
	var column:String = "*"
	var results:ArrayBuffer[EMP] = null
	// DELET
	def remove(userids:Int*) : Int = {
	  var db:Db=null
	  var si = -1
	  try {
		  db = new Db
		  var sql:DL = db.deleteFrom("emp")
		  if(userids != null) {
			  var str = ""
			  var i = 0
			  userids.foreach(v =>{
				if(i != 0){
				  str += ","
				}
				str += "?"
				i += 1
			  })
			  sql.where("empno in ("+str+")",userids:_*)
		  }
		  si = db.execute.getResultSize
		  db.commit
	  } catch {
		case e:Exception => {
		  db.rollback
		  println(e.getMessage)
		  throw e
		}
	  } finally {
		  db.close
	  }
	  si
	}
	def remove(userids:List[Int]) : Int = {
	  var db:Db=null
	  var si = -1
	  try {
		  db = new Db
		  var sql:DL = db.deleteFrom("emp")
		  if(userids != null) {
			  var str = ""
			  var i = 0
			  userids.foreach(v =>{
				if(i != 0){
				  str += ","
				}
				str += "?"
				i += 1
			  })
			  sql.where("empno in ("+str+")",userids:_*)
		  }
		  si = db.execute.getResultSize
		  db.commit
	  } catch {
		case e:Exception => {
		  db.rollback
		  println(e.getMessage)
		  throw e
		}
	  } finally {
		  db.close
	  }
	  si
	}
	def remove(user:EMP) : Int = {
	  var db:Db=null
	  var si = -1
	  try {
		  db = new Db
		  db.deleteBean(user)
		  si = db.execute.getResultSize
		  db.commit
	  } catch {
		case e:Exception => {
		  db.rollback
		  println(e.getMessage)
		  throw e
		}
	  } finally {
		  db.close
	  }
	  si
	}

	// INSERT
	def create(user:EMP) : Unit = {
	  var db:Db=null
	  try {
		  db = new Db
		  db.insertBean(user)
		  val si = db.execute.getResultSize
		  db.commit
	  } catch {
		case e:Exception => {
		  db.rollback
		  println(e.getMessage)
		  throw e
		}
	  } finally {
		  db.close
	  }
	}
	// UPDATE
	def update(user:EMP) : Unit = {
	  var db:Db=null
	  try {
		  db = new Db
		  db.updateBean(user)
		  val si = db.execute.getResultSize
		  db.commit
	  } catch {
		case e:Exception => {
		  db.rollback
		  println(e.getMessage)
		  throw e
		}
	  } finally {
		  db.close
	  }
	}

	// SELECT
	def findAll : ArrayBuffer[EMP] = {
	  val db = new Db
	  if(column==null)
		column = "*"
	  var sql:DL = db.select(column).from("emp")
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  results = db.executeQuery(classOf[EMP])
	  db.close
	  return results
	}
	def find(userids:Int*) : ArrayBuffer[EMP] = {
	  val db = new Db
	  if(column==null)
		column = "*"
	  var sql:DL = db.select(column).from("emp")
	  if(userids != null) {
		  var str = ""
		  var i = 0
		  userids.foreach(v =>{
			if(i != 0){
			  str += ","
			}
			str += "?"
			i += 1
		  })
		  sql.where("empno in ("+str+")",userids:_*)
	  }
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  results = db.executeQuery(classOf[EMP])
	  db.close
	  return results
	}
	def find(userids:List[Int]) : ArrayBuffer[EMP] = {
	  val db = new Db
	  if(column==null)
		column = "*"
	  var sql:DL = db.select(column).from("emp")
	  if(userids != null) {
		  var str = ""
		  var i = 0
		  userids.foreach(v =>{
			if(i != 0){
			  str += ","
			}
			str += "?"
			i += 1
		  })
		  sql.where("empno in ("+str+")",userids:_*)
	  }
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  results = db.executeQuery(classOf[EMP])
	  db.close
	  return results
	}
	
	def getUser(userid:Int) : EMP = {
	  val db = new Db
	  if(column==null)
		column = "*"
	  var sql:DL = db.select(column).from("emp")
	  sql.where("empno=?",userid)
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  results = db.executeQuery(classOf[EMP])
	  db.close
	  return results(0)
	}
	def setSortkey(key:String) :Unit = {
	  this.key = key
	}
	def setColumn(col:String) :Unit = {
	  this.column = col
	}
}



このコードは、モデルとしてBeanと操作関数を別々のクラスとして実装しています。
その理由は、Active RecordsとしてSQLの特徴である集合として複数レコードを扱いたいからです。

しかし、単数としてActiveRecordを扱うのであれば、EMPクラスを継承という形に。
あるいは、ArrayBuffer[EMP]をメンバーにする、いろいろあります。

まだまだ、ActiveRecordのレベルにはありませんが、
Scalaの可変長引数を使ってEMP表のプライマリキーのカラムのEMPNOを引数にして操作します。
EMPNOを要素にしたListを引数にして、可変長引数と同様の操作ができるコードも合わせて書いてみました。



find関数では、データベースをSELECTした結果のレコードデータは、BeanクラスのオブジェクトをArrayBufferに格納して返します。

INSERTするデータもBeanに格納すればいい。
EMPNOによってSELECT、UPDATE、DELETEも問題なし。


問題は、条件指定したSELECT、UPDATE、DELETEする操作です。
リストで条件を記述するようにすればいいと考えているのですが、
実際にScalaでコードを書くプログラマに受け入れられる仕様となるのでしょうか?

Comments

comments

Powered by Facebook Comments

コメントはまだありません »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

コメントを投稿するにはログインしてください。

Powered by WordPress