Scala+DBMS+Web スカラ座の夜

2011年9月11日

ActiveRecordのようにDB操作

Filed under: Scala — admin @ 1:49 AM

ActiveRecordのようにDB操作ということで、前回コードを書いてみましたが、EMP表に依存したクラスになってしまいました。
新しいコードはテーブルに依存しない汎用のBeanTalkクラスを書いてみました。

このクラスはかなり複雑な内容になってます。
implicitという表記を使っています。
リフレクションするときの定番の書き方です。
もっと綺麗な表記もあるのでしょうがこれ以上わかりにくくしたくないのでこのままにします。


package seedo.database
import scala.collection.mutable.{HashMap,LinkedHashMap,HashSet,ArrayBuffer}
import scala.reflect._
import scala.collection.mutable.Map
import seedo._

class BeanTalk [T] (implicit m : ClassManifest[T]) {
	var beanObj = m.erasure.newInstance().asInstanceOf[T]	// TのclassのBean を生成
	var joinEntity = Map.empty[String,Array[AnyRef]]	// joinしているテーブルのレコード
	var column:String = "*"		// SELECTするときに読み込むカラムを指定
	// SELECT文の構成要素を初期化
	def clean:Unit = {
		column = "*"
	}
	var db:Db = null	// Dbクラス
	var tran = true		// トランザクション制御する。外部からDbクラスのオブジェクトが渡されたときはトランザクションモード(false)
	def bean : T = beanObj
	def entity : T = beanObj
	var joins = Map.empty[String,List[List[String]]]	// join先のBean名とjoinの条件
	/**
	 * Joinしているテーブルのレコードを返す
	 */
	def getjoinEntity(joinBeanName:String) :Option[Array[AnyRef]] = {
	  	joinEntity.get(joinBeanName)
	}
	// join has_one / has_many
	def addJoin(beanName:String,on:List[String]*) : Unit = {
	  joins.put(beanName,on.toList)
	}
	def removeJoin(beanName:String) : Unit = {
	  joins.remove(beanName)
	  joinEntity.remove(beanName)
	}
	def removeAllJoin() : Unit = {
	  joins.clear
	  joinEntity.clear
	}
	def setDb(dbx:Db):Unit = {
		db = dbx
		tran = false
	} 
	// SELECT One record. praimaryKey is some columns.
	def getEntity(pk:Any*) : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
	    column = "*"
	  var sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef],pk:_*)
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// set bean object.
	def setEntity(e:T) : Unit = {
		beanObj = e
	}
	// select One record
	def find : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
		  column = "*"
	  val sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef])
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// select join records.
	private def joinsRecords(beanO:T,joinE:Map[String,Array[AnyRef]]) :Unit = {
	  joinE.clear
	  joins.foreach(j => {
		  val clazz = beanO.getClass
		  val k = j._2
		  var whereStr = ""
		  val whereVal = new ArrayBuffer[Any]
		  var columnStr = "*"
		  var n = 0
		  k.foreach(i => {
			  if(i.length > 2){
				  val methodName = "get" + i(0)	// Master側のJOINカラム BENAからそのカラムの値を取り出す
				  val v = clazz.getMethod(methodName).invoke(beanO)
				  whereVal += v
				  if(n != 0){
					  whereStr += " and "
				  }
				  whereStr += i(2) + i(1) + "?"	// Detail側のJOINカラム 抽出条件を作る。i(2)は、JOIN側のカラム名、i(1)は比較演算子「'=','<>','<','>','<=','>='」
				  
				  if(i.length > 3){
				    columnStr = i(3)	// Detail側のSELECTしたときに取るカラム
				  }
			  }else{
				  throw new Exception("Join sentence should be 'Master.column name' + '=' + 'Detail.column name'. <- " + i.toString)
			  }
			  n += 1
		  })
		  val dl = db.select(columnStr)
		  if(whereVal.length > 0){
			  dl.whereParam(whereStr, whereVal.toArray)
		  }
		  val result = db.executeQueryBean(j._1)
		  joinE.put(j._1,result.toArray)
	  })	  
	}
	// UPDATE one record
	def update : Int = {
		if(tran)
			db = new Db
		try {
			return db.updateBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	def save : Int = {
		update
	}
	// DELETE one record
	def delete : Int = {
		if(tran)
			db = new Db
		try {
			return db.deleteBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// INSERT one record
	def create : Int = {
		if(tran)
			db = new Db
		try {
			return db.insertBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// SQL SELECT sentence
	def column(col:String) :BeanTalk[T] = {
		this.column = col
		this
	}
	def commit : BeanTalk[T] = {
		if(!tran)
			db.commit
		this
	}
	def rollback : BeanTalk[T] = {
		if(!tran)
			db.rollback
		this
	}
	def dump : Unit = {
		println(beanObj.toString)
		if(joinEntity != null) {
			joinEntity.foreach(w => {
				println(" " + w._1)
				w._2.foreach(x =>{
					println("  " + x)
				})
			})
		}
	}
}



ActiveRecordでは、1つのクラスが 基本的にDBの1テーブルに対応し、クラスの属性(attribute)は、テーブルの各カラムに対応します。
ActiveRecordのDB操作についての私の見方は、MS-ACCESSのような操作イメージです。
数値型のプライマリーキー・カラムが1つあり、このキーによってレコードを特定します。
UPDATE操作やDELETE操作は、ACCESSの画面で操作するためテーブルのレコードデータをエクセルのシートのような表に読み込んでから操作します。人がPCでマウスで操作しますから当然です。

ACCESSはデータベースシステム自身であり、VBAでそのデータを操作するのであればこれでよいのですが、クライアント・サーバモデルでデータ操作するときに、オブジェクトとしてレコードデータをfindしてからUPDATEやDELETEというのはロジックのわかりやすさという点ではいいのですが、処理としては非効率です。

非手続き型言語のSQLと、RubyやJava、Scalaのような手続き型言語の折り合いの悪さは、リレーショナルデータベースとプログラミング言語の中のインピーダンスミスマッチとして議論され続けています。


話が違う方向に行きそうなので、話を戻すと、
BeanTalkクラスの戦略は、DBの1テーブルは、Beanクラスが対応します。
BeanTalkクラスでは、var beanobj = new BeansTalk[scott.bean.EMP]によってBeanをラッピングして、DB操作を共通化しますがSQL文をプログラマに見せません。

BeanTalkクラスが生成されると、内部にBeanオブジェクトが1つ生成されます。




Dbクラス、Beanを介してのSELECTなどのSQL操作は、SeedoフレームワークにあるDB操作のためのJarにあります。




早速、このクラスのテストコードを見ましょう。

@Beforeinitialize関数beanobj = new BeanTalk[scott.bean.EMP]によってEMP表のBeanTalkオブジェクトを生成します。
DEPT表についても、beanobjDept = new BeanTalk[scott.bean.DEPT]でBeanTalkオブジェクトを生成し、JDBCドライバによってデータベース接続を行うdb = new Db(driver, dsn, user, password)を生成し、各BeanTalkオブジェクトにセットします。


このテストコードは、junitなので明示的にDbクラスを生成していますが、TOMCATでBeanTalkクラスを使う場合は、TOMCATのコネクションプールからDB接続を入手しますので、driver,dsn,user,passwordは明示する必要はありません。

package test
import org.scalatest.junit.{JUnitSuite,ShouldMatchersForJUnit}
import org.junit.{Test,Before,After}
import seedo.database._

class BeanTalkTest extends JUnitSuite with ShouldMatchersForJUnit{
	val driver = "com.mysql.jdbc.Driver"
	val dsn = "jdbc:mysql://127.0.0.1:3306/seedo?useUnicode=true&characterEncoding=UTF-8"
	val user ="scott"
	val password ="tiger"
	var db:Db = null
	var beanobj:BeanTalk[scott.bean.EMP] = null
	var beanobjDept:BeanTalk[scott.bean.DEPT] = null
	@Before def initialize() {
		println("--- Start TSET ---")
		beanobj = new BeanTalk[scott.bean.EMP]
		println("getClass=" + beanobj.getClass)
		beanobjDept = new BeanTalk[scott.bean.DEPT]
		db = new Db(driver, dsn, user, password)
		beanobj.setDb(db)
		beanobjDept.setDb(db)
	}
	@Test def test1 {
		println("--- TSET 1 ---")
		beanobj.bean.setEMPNO(7788)
		val r = beanobj.find
		beanobj.dump
	}
	@Test def test2 {
		println("--- TSET 2 ---")
		try {
			beanobj.getEntity(7788)
			println("getEntity=" + beanobj.entity)
			beanobj.dump
			val entityCopy = beanobj.entity.clone
			
			println("--update op")
			beanobj.bean.setDEPTNO(10)
			beanobj.bean.setSAL(3200)
			beanobj.update
			println("--find")
			beanobj.find
			beanobj.dump

			println("---update op")
			beanobj.bean.setDEPTNO(20)
			beanobj.bean.setSAL(3000)
			beanobj.update
			println("---find")
			beanobj.find
			beanobj.dump
			
			println("---delete op")
			beanobj.delete
			println("---find")
			val b = beanobj.find
			if(b != null)
				beanobj.dump
			
			beanobj.setEntity(entityCopy)
			
			println("---create op")
			beanobj.create
			println("---find")
			beanobj.find
			beanobj.dump
		} catch {
		 case e:Exception => {
            println("Exception "+e.getMessage)
		 }
		}
	}
	@Test def test3 {
		println("--- TSET 3 ---")
		beanobj.bean.setEMPNO(7788)
		beanobj.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO","DEPTNO,DNAME,LOC"))
		beanobj.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"),List("JOB","=","JOB"))
		val r = beanobj.find
		println(r.toString)
		val s = beanobj.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
		val s3 = beanobj.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
	}
	@Test def test4 {
		println("--- TSET 4 ---")
		beanobj.removeAllJoin
		beanobj.bean.setEMPNO(7782)
		val r3 = beanobj.find
		println(r3.toString)
		val s4 = beanobj.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
		val s5 = beanobj.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
	}
	@Test def test5 {
		println("--- TSET 5 ---")
		beanobjDept.bean.setDEPTNO(10)
		beanobjDept.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"))
		beanobjDept.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO"))
		val r1 = beanobjDept.find
		println(r1.toString)
		val s1 = beanobjDept.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
		val s2 = beanobjDept.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
	}
	@After def dest {
		db.close
		println("--- End TEST ---\n")
	}
}



JUNITの実行結果

最初のコードは、EMP表からEMPNOが7788の従業員をSELECTします。
BeanTalkの中でEMP表のBEANに対してEMPNOをSETします。
次にfind関数を呼び出せば、BEANのレコード内容が設定されます。
その内容は、dump関数によってプリントします。

beanobj.bean.setEMPNO(7788)
val r = beanobj.find
beanobj.dump




7788番のスコットさんがSELECTされています


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 1 ---
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
--- End TEST ---



次のコードは、SELECT,UPDATE,DELETE,INSERTの一連のDB操作です。

先ほどと同様に7788番のレコードをgetEntity関数でSELECTします。なぜfind関数ではないかは置いておいて、

beanobj.entity.clone関数によって7788のBEANの複製を作ります。


beanobj.getEntity(7788)
println("getEntity=" + beanobj.entity)
beanobj.dump
val entityCopy = beanobj.entity.clone




その結果が以下です。
dump関数によってスコットさんのレコードがプリントされています。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 2 ---
getEntity=7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20




次に、スコットさんの所属を20番から10番に、給料を3000から3200に更新します。BEANのSETTER関数で値を設定し、update関数を実行します。

更新した内容を再び、find関数でSELECTしてdump関数でプリントします。


println("--update op")
beanobj.bean.setDEPTNO(10)
beanobj.bean.setSAL(3200)
beanobj.update
println("--find")
beanobj.find
beanobj.dump




結果は、スコットさんの所属と給料が変更されています。


--update op
--find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3200,,10




次に、再び所属と給料を設定してデータを元に戻します


println("---update op")
beanobj.bean.setDEPTNO(20)
beanobj.bean.setSAL(3000)
beanobj.update
println("---find")
beanobj.find
beanobj.dump




データは、元に戻っています。


---update op
---find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20




次の操作は、
delete関数によってスコットさんのレコードを削除します。
find関数で削除された7788番のレコードを検索します。


println("---delete op")
beanobj.delete
println("---find")
val b = beanobj.find
if(b != null)
	beanobj.dump




結果は、レコードが削除されたのでプリントされません。

find関数は、レコードが検索できなかった場合はnullを返します。


---delete op
---find




beanobj.setEntity(entityCopy)関数によって先ほど作ったBeanクーロン(複製)をbeanobjにセットします。
create関数を呼び出し、EMP表にINSERTします。


beanobj.setEntity(entityCopy)
println("---create op")
beanobj.create
println("---find")
beanobj.find
beanobj.dump




再びスコットさんのレコードが検索できます。


---create op
---find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
--- End TEST ---




この操作では、まったくSQL文は見えません。


マスター・ディテール



3つ目のテストは、マスター・ディテールの1:N関係です。



addJoin関数によってEMP表とDEPT表をSELECTします。
この処理は決して、EMP表とDEPT表をWHERE句でジョイン(結合)してSELECTしているわけではありません。



addJoin関数の
第一引数は、DEPT表用のBEANクラスの名称(scott.bean.DEPT)。
第二引数は、EMP表とDEPT表の結合関係をListによって表します。



関係は、
Listの
第一要素がEMP表のカラム
第二要素が関係式。以下の処理では’=’
第三要素がDEPT表のカラム
第四要素はDEPT表のBEANにSELECTして値を取ってくるカラムをString型で表記します。この第四要素は省略可能で、省略するとすべてのレコード値がBEANにセットされます。SQL文で書けば SELECT * FROM DEPT ということです。



関係式の意味をもう少し詳しく説明すると、
EMPのBEANには、スコットさんの従業員IDである7788となっていて、
所属は10です。この値は部門IDであり、DEPT表のプライマリーキーでもあります。
addJoin関数で結合関係を定義するということは、DEPT表をSELECTするSQL文は以下のようになります。



SELECT * FROM DEPT WHERE DEPTNO=10;



以下のSQL文が発行されるわけではありません。



SELECT * FROM EMP,DEPT WHERE EMP.DEPTNO=DEPT.DEPTNO;



以下のテストケースでは、さらにEMP表とも関係させています。
EMP表とEMP表の関係は、所属がスコットさんと同じで、かつ、同じ仕事をしている従業員をSELECTします。
スコットさんのJOBはANALYSTですね。

addJoin関数の第二引数以降はListの可変引数ですので、複数の関係条件を指定することができます。


beanobj.bean.setEMPNO(7788)
		beanobj.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO","DEPTNO,DNAME,LOC"))
		beanobj.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"),List("JOB","=","JOB"))
val r = beanobj.find
println(r.toString)
val s = beanobj.getjoinEntity("scott.bean.DEPT") match {
  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
  case None => Nil
}
val s3 = beanobj.getjoinEntity("scott.bean.EMP") match {
  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
  case None => Nil
}




結果です



所属はRESEARCH部門。部門の所在地はDALLASです。
フォードさんが同じ所属で仕事がANALYSTです。スコットさんもSELECTされます。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 3 ---
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
 DEPT 20,RESEARCH,DALLAS
 EMP 7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
 EMP 7902,FORD,ANALYST,7566,1981-12-03 00:00:00.0,3000,,20
--- End TEST ---



次は、JOIN条件を外します
removeAllJoin関数を呼び出すと、EMPとDEPTの1:Nと、EMPとEMPの1:Nの関係が削除されます。

beanobj.removeAllJoin
beanobj.bean.setEMPNO(7782)
val r3 = beanobj.find




結果は、7782番のクラークさんがSELECTされますが、
先ほどの関係はないので、DEPTとEMPのディテールはSELECTされません。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 4 ---
7782,CLARK,MANAGER,7839,1981-06-09 00:00:00.0,2450,,10
--- End TEST ---



次のテストケースでは、マスターがDEPT表、ディテールがEMP表です。
ここでは部門IDが10番に所属する従業員がSELECTされます。
データとしての意味はないですが、DEPT表とDEPT表も関係づけします。


beanobjDept.bean.setDEPTNO(10)
beanobjDept.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"))
beanobjDept.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO"))
val r1 = beanobjDept.find




ACCOUNTING部門はニューヨークにあり、3人が所属していることがわかります。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 5 ---
10,ACCOUNTING,NEW YORK
 EMP 7782,CLARK,MANAGER,7839,1981-06-09 00:00:00.0,2450,,10
 EMP 7839,KING,PRESIDENT,,1981-11-17 00:00:00.0,5000,,10
 EMP 7934,MILLER,CLERK,7782,1982-01-23 00:00:00.0,1300,,10
 DEPT 10,ACCOUNTING,NEW YORK
--- End TEST ---



DEPT BEAN クラス
最後に、DEPTクラスです。
このクラスは、SeedoフレームワークにあるBeanクラスの自動生成ツールによって作成されたものです。
MySQLやOracleにアクセスしてディクショナリ情報を参照してテーブルやビューのBeanコードを生成します。



package scott.bean

import scala.xml._
import java.math.BigDecimal
import java.sql.{Blob,Date,Timestamp}
import javax.persistence.{Column,Entity,Id,Table,Lob,UniqueConstraint,SequenceGenerator}

import seedo.Util

@Entity
@Table(name="DEPT")
class DEPT{
	// Type = TABLE
	// Index = 
	// Commect = -
	def PRIMARY_KEY :Array[String] = Array("DEPTNO")
	def timestamp_COLUMN :Array[String] = Array()
	def COLUMN :Array[String]= Array(
	"DEPTNO"		// NUMBER(2)
	,"DNAME"		// VARCHAR2(14)
	,"LOC"		// VARCHAR2(13)
	)

	def clear :Unit= {
		DEPTNO = null
		DNAME = null
		LOC = null
		for (i <- 0 to modifiedProperty.length - 1) {
			modifiedProperty(i) = false
		}
	}
	override def clone : DEPT = {
		var base = new DEPT
		if (DEPTNO != null) {
			base.DEPTNO = new BigDecimal(DEPTNO.toString)
		}
		if (DNAME != null) {
			base.DNAME = new String(DNAME)
		}
		if (LOC != null) {
			base.LOC = new String(LOC)
		}
		for (i <- 0 to modifiedProperty.length - 1) {
			base.modifiedProperty(i) = modifiedProperty(i)
		}
		base
	}
	def isModified  : Boolean = {
		modifiedProperty.foreach (i => {
			if (i) {
				return true
			}
		})
		return false
	}
	def isPrimaryKey(columnName:String) : Boolean = {
		if (columnName.equals("DEPTNO")) {return true}
		false
	}
	def isColumn(columnName:String) : Int= {
		return 0
	}
	/**
	 * Getter
	 * @return the DEPTNO
	 */
	def getDEPTNO:BigDecimal = DEPTNO
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO( Deptno:BigDecimal) : Unit = {
		this.DEPTNO = Deptno
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Int) :Unit = {
		this.DEPTNO = new BigDecimal(Deptno)
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Long) : Unit = {
		this.DEPTNO = new BigDecimal(Deptno)
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Double) : Unit = {
		this.DEPTNO = new BigDecimal(String.valueOf(Deptno))
		modifiedProperty(0) = true
	}
	/**
	 * Getter
	 * @return the DNAME
	 */
	def getDNAME:String = DNAME
	/**
	 * Setter
	 * @param Dname the DNAME to set
	 */
	def setDNAME( Dname:String) : Unit = {
		this.DNAME = Dname
		modifiedProperty(1) = true
	}
	/**
	 * Getter
	 * @return the LOC
	 */
	def getLOC:String = LOC
	/**
	 * Setter
	 * @param Loc the LOC to set
	 */
	def setLOC( Loc:String) : Unit = {
		this.LOC = Loc
		modifiedProperty(2) = true
	}
	override def toString :String= {
		var buf = new StringBuffer
		if(modifiedProperty(0)){
			buf.append(if(DEPTNO!=null){DEPTNO}else{""})
		}
		if(modifiedProperty(1)){
			if(buf.length>0)buf.append(",")
			buf.append(if(DNAME!=null){DNAME}else{""})
		}
		if(modifiedProperty(2)){
			if(buf.length>0)buf.append(",")
			buf.append(if(LOC!=null){LOC}else{""})
		}
		buf.toString
	}
	def toXml :Node = {
<DEPT><DEPTNO>{DEPTNO}</DEPTNO><DNAME>{if(DNAME != null){seedo.Util.rpxml(DNAME)}else{""}}</DNAME><LOC>{if(LOC != null){seedo.Util.rpxml(LOC)}else{""}}</LOC></DEPT>	}
	def toXmlModified :Node = {
<DEPT>{if(modifiedProperty(0)){<DEPTNO>{DEPTNO}</DEPTNO>}}{if(modifiedProperty(1)){<DNAME>{if(DNAME != null){seedo.Util.rpxml(DNAME)}else{""}}</DNAME>}}{if(modifiedProperty(2)){<LOC>{if(LOC != null){seedo.Util.rpxml(LOC)}else{""}}</LOC>}}</DEPT>
	}
	// generate json format
	def toJson :String= {
		var buf = new StringBuffer
		buf.append("{")
		if(modifiedProperty(0)){
			buf.append("\"DEPTNO\":"+(if(DEPTNO!=null){DEPTNO}else{"null"}))
		}
		if(modifiedProperty(1)){
			if(buf.length>0)buf.append(",")
			buf.append("\"DNAME\":\""+(if(DNAME!=null){DNAME}else{"null"})+"\"")
		}
		if(modifiedProperty(2)){
			if(buf.length>0)buf.append(",")
			buf.append("\"LOC\":\""+(if(LOC!=null){LOC}else{"null"})+"\"")
		}
		buf.append("}")
		buf.toString
	}
	override def hashCode :Int = {
		return (DEPTNO).hashCode
	}
	@Id
	@Column(name="DEPTNO",precision=2,nullable=false,unique=true,columnDefinition="NUMBER(2)")
	@SequenceGenerator(name="PK_DEPT", sequenceName="PK_DEPT")
	var DEPTNO:BigDecimal = null
	@Column(name="DNAME",length=14,nullable=true,unique=false,columnDefinition="VARCHAR2(14)")
	var DNAME:String = null
	@Column(name="LOC",length=13,nullable=true,unique=false,columnDefinition="VARCHAR2(13)")
	var LOC:String = null
	var modifiedProperty:Array[Boolean] = Array(false,false,false,false)
}

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

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

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

Powered by WordPress