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の借用となるとこれは難しいところです。
コメントを残す
コメントを投稿するにはログインしてください。