ScalaでBean Class

Posted 2010年11月30日 by

ScalaでもBeanクラスを作る意味があるのか?

データベースを操作するために、Beanクラスは便利です。
「ScalaでもBeanクラスを作る意味があるのか?」という課題です。

ScalaにはAnyRefがあり、ListやArrayはJavaより使い勝手はいいとなると、
コンテナとしてのBeanクラスをわざわざ作るかです。

SELECT文の結果データを格納するのであればListやArrayで十分かもしれません。
でも、INSERTしたりUPDATEに使うには、データベース表の型をみてプログラムを書かねばなりません。

Beanオブジェクトであればデータベースの型定義に基づいてクラスを作りますから、

よく考えられたBeanクラスであれば、プログラマは型をテキストか日付か数値くらいで厳密に見なくてもデータベース操作することができます。

具体的にBeanクラスを作ってみますと、

Oracle Databaseに以下のあるMASTER表を定義します。
この表定義そのものはあまり意味がありません。テスト用です。

CREATE TABLE MASTER (
	RENTALODRID    NUMBER PRIMARY KEY
	, CUSTOMERID   NUMBER(6)
	, SALESPRICE   NUMBER
	, DLVFEETAX    NUMBER
	, DLVFEE       NUMBER
	, RNTODRDATE   DATE
	, LASTUPDATE   TIMESTAMP
	, RATE         NUMBER(3,2)
	, RATE1        NUMBER(6,2)
	, RATE2        FLOAT
	, RATE3        INTEGER
	, OTHER        VARCHAR2(100)
);



OracleDBでの表定義は以下のようになります。

SQL> desc master
 名前                                      NULL?    型
 ----------------------------------------- -------- ----------------

 RENTALODRID                               NOT NULL NUMBER
 CUSTOMERID                                         NUMBER(6)
 SALESPRICE                                         NUMBER
 DLVFEETAX                                          NUMBER
 DLVFEE                                             NUMBER
 RNTODRDATE                                         DATE
 LASTUPDATE                                         TIMESTAMP(6)
 RATE                                               NUMBER(3,2)
 RATE1                                              NUMBER(6,2)
 RATE2                                              FLOAT(126)
 RATE3                                              NUMBER(38)
 OTHER                                              VARCHAR2(100)



Oracle JDBCドライバが返してくるJava型を参考にしながら、Master表のBeanクラスを定義します。

ScalaでBeanクラスを作るからには、Beanクラスを使う側は、Scalaの型やデータベースの型を厳密に意識しなくてもいいようにしたい訳です。

型でいろいろと記述するのが数値と日時で、これが厄介です。

データベースの数値型は、桁に制約を定義できます。
整数なのか実数なのか。

OracleJDBCドライバは、数値型はBigDecimalクラスで値を操作します。
BigDecimalクラスはScalaでもJavaでも普段使わないクラスです。

new BigDecimal(100) とか、new BigDecimal( “3.14” ) とは書きたくないです。
Scalaのプログラムでは、IntやDoubleクラスでコードは書きます。
このクラスで表現できないサイズの数字や、小数点をきっちり保持するときはBigDecimalクラスの出番です。

日付の扱いも、OracleのSQL文では、TO_CHARとTO_DATEはなくてはならないものです。
でも、この構文をSQL文に書くのは面倒な話。
MySQLとは互換性もないし。

日付型やタイムスタンプ型には、直接、String で日付を ”2010/11/30” として渡したいです。
いちいち、TO_DATE( ‘2010/11/30’ , ‘YYYY/MM/DD’ ) とは書きたくないですから。
JavaのDateクラスも日時を指定して生成するもの面倒ですし。

setterには、あらかじめ代表的な引数の型を受け取るメソッドを定義しておけば利用する側でいちいち書かなくてもいいということです。

package seedo

import java.math.BigDecimal
import java.sql.{Date,Timestamp}
import java.text.{DateFormat,SimpleDateFormat}

class MASTER {
	val TABLE = "MASTER";
	val PRIMARY_KEY = "RENTALODRID";
	val timestamp_COLUMN = "LASTUPDATE";

	var RENTALODRID:BigDecimal = null
	var CUSTOMERID:Integer  = null
	var SALESPRICE:BigDecimal = null
	var DLVFEETAX:BigDecimal = null
	var DLVFEE:BigDecimal = null
	var RNTODRDATE:java.sql.Date = null
	var LASTUPDATE:Timestamp = null
	var RATE:java.lang.Double = null
	var RATE1:Double = 0.0
	var RATE2:Float = 0.0F
	var RATE3:Int = 0
	var OTHER:String = null

	def setRENTALODRID(value:BigDecimal) = RENTALODRID = value
	def setCUSTOMERID(value:Integer ) = CUSTOMERID = value
	def setSALESPRICETAX(value:BigDecimal) = RENTALODRID = value
	def setSALESPRICE(value:BigDecimal) = RENTALODRID = value
	def setDLVFEETAX(value:BigDecimal) = RENTALODRID = value
	def setDLVFEE(value:BigDecimal) = RENTALODRID = value
	def setDLVFEE(value:Double) = RENTALODRID = new BigDecimal(value)
	def setDLVFEE(value:java.lang.Double) = RENTALODRID = new BigDecimal(value.toString)
	def setDLVFEE(value:Integer) = RENTALODRID = new BigDecimal(value.toString)
	def setRNTODRDATE(value:java.sql.Date) = RNTODRDATE = value
	def setRNTODRDATE(value:java.util.Date) = RNTODRDATE = new java.sql.Date(value.getTime)
	def setRNTODRDATE(value:String) = RNTODRDATE = new java.sql.Date(DateFormat.getDateInstance().parse(value).getTime)
	def setLASTUPDATE(value:Timestamp) = LASTUPDATE = value
	def setLASTUPDATE(value:String) = LASTUPDATE = new Timestamp(DateFormat.getDateInstance().parse(value).getTime)
	def setRATE(value:java.lang.Double) = RATE = value
	def setRATE1(value:Double) = RATE1 = value
	def setRATE2(value:Float) = RATE2 = value
	def setRATE3(value:Int) = RATE3 = value
	def setOTHER(value:AnyRef) = {
		println("setOTHER(value:AnyRef)")
		OTHER = value match {
		//		case x : Class[Int] => value.toString
			case x : Class[Date] => {
				println("setOTHER(value:AnyRef) Class[Date]")
				val f = new SimpleDateFormat("yyyy/MM/dd")
				f.format(value)
			}
			case x : Class[String] => value.asInstanceOf[String]
			case _ => {
				println("setOTHER(value:AnyRef) _")
				if(value.getClass == classOf[java.sql.Date]){
					new SimpleDateFormat( "yyyy/MM/dd" ).format(value)
				}else if(value.getClass == classOf[java.util.Date]){
					new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" ).format(value)
				}else{
					value.toString
				}
			}
		}
	}
	def setOTHER(value:Int) = {
		OTHER = value.toString
		println("setOTHER(value:Int)")
	}
	def setOTHER(value:String) = {
		OTHER = value
		println("setOTHER(value:String)")
	}
	var modified:Boolean = true
	var modifiedProperty = new Array[Boolean](12)

	override def toString :String  = {
		var buf = "["
		buf += RENTALODRID
		buf += "," + CUSTOMERID
		buf += "," + SALESPRICE
		buf += "," + DLVFEETAX
		buf += "," + DLVFEE
		buf += "," + RNTODRDATE
		buf += "," + LASTUPDATE
		buf += "," + OTHER
		buf += "]"
		buf
	}
	override def hashCode:Int = RENTALODRID.hashCode
}

setterには、それぞれの引数の型にあったものが呼び出されます。

Scalaですから、setter引数にAnyRefを定義した場合の動きに興味があります。

def setOTHER(value:AnyRef) と def setOTHER(value:String) とがあった場合は、引数にStringクラスのものを渡すと後者が呼び出されます。

def setOTHER(value:AnyRef)のなかのクラスの判定をmatch/case で書いてみました。

OTHER = value match {
     case x : Class[Date] => new SimpleDateFormat("yyyy/MM/dd ").format(value)
     case x : Class[Integer] => value.asInstanceOf[Integer].toString
     case x : Class[String] => value.asInstanceOf[String]
     case _ =>  value.toString
}

引数に、java.util.Date の値でも、java.sql.Dateの値 で渡してみても、Class[Date] が実行されません。
case _ => になってしまいました。

それと、Class[java.sql.Date] やClass[java.util.Date] と書くと、エラーとなります。

Scalaでデータベースを操作する限り Java JDBC のクラスを使う訳です。

Dateクラスは、Javaの借用となるとこれは難しいところです。

Post Details

コメントを残す