Scala Iterator for JDBC

Posted 2011年8月2日 by

JDBCドライバをScalaのIteratorで皮を被せた実装です。

これは、Seedoフレームワークの中心的な部分です。
Javaで実装したSeedoフレームワークでは、Iterator部分が別Classとなる実装でしたが、Scalaではすっきりと1つのClassの中に記述できてしまいます。

内容はJDBCドライバの関数を生で記述したことがある人であればすぐに理解できるものです。
最近は、JDBCドライバの関数で直接コードを書く機会がないと思いますが、お使いのフレームワークの中身とこれとは似たようなものでしょう。

prepareStatement関数を使ったgetObjectによってレコードカラムのデータを取得するコーディングです。

	def executeQueryIterator = new Iterator[Array[Any]] {
		if(sqlMarker != rdbms._SELECT_){
			throw new Exception("SQL sentence should be SELECT phrase")
		}
		var pstmt:PreparedStatement = con.prepareStatement(getSql)
		var parameterIndex = 1
		if (parameters != null) {
			for (i <- 0 to parameters.length - 1) {
				pstmt.setObject(parameterIndex, parameters(i))
				parameterIndex += 1
			}
		}
		var rs:ResultSet = pstmt.executeQuery
		var orsmd:ResultSetMetaData = rs.getMetaData
		var numColumns = orsmd.getColumnCount
		columnNameList = new Array[String](numColumns) // make array for column name
		for (i <- 1 to numColumns) {		// get column name
			columnNameList(i-1) = orsmd.getColumnName (i)
		}
	
		override def hasNext : Boolean = {
			try {
				val r = rs.next	// read next record
				if(!r)
					close
				return r
			} catch {
				case e:SQLException => {
					e.printStackTrace
				}
			}
			return false
		}
		override def next : Array[Any] = {
			val rec = new Array[Any](numColumns) // make array for one record
			var i = 0
			for (j <- 1 to numColumns) {
				val tp = orsmd.getColumnType (j)
				try {
					rec(i) = rs.getObject(j)
				} catch {
					case e:SQLException => {
						errorMessage = "Warning "+e.getMessage
						System.err.println(errorMessage)
						close
					}
				}
				i += 1
			}
			rec
		}
		def close :Unit = {
			try {
				if (pstmt != null) {
					pstmt.close
					pstmt = null
				}
				if (rs != null){
					rs.close
					rs = null
				}
			} catch {
				case e:SQLException => {
					e.printStackTrace
				}
			}
		}
		clear
	}



override def hasNext では、rs.next を呼び出しています。

rs.hasNext や rs.isLast や rs.last が使えるといいのですが、
Oracleデータベースでは、prepareStatement関数が、TYPE_SCROLL_INSENSITIVEでないResultSetを作るため例外を発してしまいます。PostgreSQLのJDBCドライバでは問題はないようです。


次のscalatestによるテストコードは、Seedoフレームワークのテスト用のものです。

import org.scalatest.junit.{JUnitSuite,ShouldMatchersForJUnit}
import org.junit.{Test,Before,After,BeforeClass,AfterClass}
import seedo._
import seedo.database._
import scott.bean._

class testDB extends JUnitSuite with ShouldMatchersForJUnit{
	var drivrName = "oracle.jdbc.driver.OracleDriver"
	var dsn = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL"
	var user ="scott"
	var passwd ="tiger"
	var db:Db = _

	@Before def initialize() {
	  println("Start")
	  db = new Db(drivrName,dsn,user,passwd)
	}
	@Test def verifyTable() { // Uses JUnit-style assertions & Uses ScalaTest assertions
	  db.select("*").from("EMP").where("empno >= ?",7700).orderBy("ename")
	  println(db.toString)
	  println(db.getSql)
	  println(" --- Bean SELECTした結果をBEANに格納してArrayで返す")
	  db.executeQuery(classOf[EMP]).foreach({c =>
	 	  println(c.toString)
	  })
	  println(" --- ColumnNameList")
	  db.getColumnNameList.foreach(c =>{
	 	  print(" " + c)
	  })
	  println
	  db.select("EMPNO,ENAME,HIREDATE").from("EMP").where("empno >= ?",7700).orderBy("EMPNO")
	  println(db.toString)
	  println(db.getSql)
	  println(" --- Bean SELECTした結果をBEANに格納して返すIteratorによる実装。foreachにて出力はXML")
	  db.executeQueryIterator(classOf[EMP]).foreach({c =>
	 	  println(c.toXml)
	  })
	  println(" --- ColumnNameList")
	  db.getColumnNameList.foreach(c =>{
	 	  print(" " + c)
	  })
	  println
	  db.select("EMPNO,ENAME,HIREDATE").from("EMP").where("empno >= ?",7700).orderBy("EMPNO")
	  println(db.toString)
	  println(db.getSql)
	  println(" --- Bean SELECTした結果をBEANに格納して返すIteratorによる実装。while句でhasNext、next関数にて出力はJson")
	  val it = db.executeQueryIterator(classOf[EMP])
	  while(it.hasNext){
		  println(it.next.toJson)
	  }
	  println(" --- ColumnNameList")
	  db.getColumnNameList.foreach(c =>{
	 	  print(" " + c)
	  })
	  println
	  db.select("EMPNO,ENAME,HIREDATE").from("EMP").where("empno >= ?",7700).orderBy("EMPNO")
	  println(db.toString)
	  println(db.getSql)
	  println(" --- Array SELECTした結果をArray[Any]に格納して返すIteratorによる実装。while句でhasNext、next関数にて出力はtoString")
	  val list2 = db.executeQueryIterator
	  while(list2.hasNext){
		  list2.next.foreach(v =>print(v.toString + " "))
		  println
	  }
	  println(" --- ColumnNameList")
	  db.getColumnNameList.foreach(c =>{
	 	  print(" " + c)
	  })
	  println

	  println(" --- End Sql")
	}
	@After def close() {
	  if(db != null)
		  db.close
	  println("End")
	}
}

テスト結果は、以下の通り。

Start
MissingResourceException:Can't find bundle for base name env, locale 

JDBC Driver:Oracle JDBC driver Version:11.1.0.7.0-Production
Database:Oracle Version:11.2
select * from EMP where empno >= ? order by ename [7700]
select * from EMP where empno >= ? order by ename
 --- Bean SELECTした結果をBEANに格納してArrayで返す
7876,ADAMS,CLERK,7788,1987-05-23 00:00:00.0,1100,null,20
7782,CLARK,MANAGER,7839,1981-06-09 00:00:00.0,2450,null,10
7902,FORD,ANALYST,7566,1981-12-03 00:00:00.0,3000,null,20
7900,JAMES,CLERK,7698,1981-12-03 00:00:00.0,950,null,30
7839,KING,PRESIDENT,null,1981-11-17 00:00:00.0,5000,null,10
7934,MILLER,CLERK,7782,1982-01-23 00:00:00.0,1300,null,10
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,null,20
7844,TURNER,SALESMAN,7698,1981-09-08 00:00:00.0,1500,0,30
 --- ColumnNameList
 EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO [7700]
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO
 --- Bean SELECTした結果をBEANに格納して返すIteratorによる実装。foreachにて出力はXML
<EMP><EMPNO>7782</EMPNO><ENAME>CLARK</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1981-06-09 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7788</EMPNO><ENAME>SCOTT</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1987-04-19 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7839</EMPNO><ENAME>KING</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1981-11-17 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7844</EMPNO><ENAME>TURNER</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1981-09-08 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7876</EMPNO><ENAME>ADAMS</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1987-05-23 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7900</EMPNO><ENAME>JAMES</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1981-12-03 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7902</EMPNO><ENAME>FORD</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1981-12-03 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>
<EMP><EMPNO>7934</EMPNO><ENAME>MILLER</ENAME><JOB></JOB><MGR></MGR><HIREDATE>1982-01-23 00:00:00.000</HIREDATE><SAL></SAL><COMM></COMM><DEPTNO></DEPTNO></EMP>

 --- ColumnNameList
 EMPNO ENAME HIREDATE
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO [7700]
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO
 --- Bean SELECTした結果をBEANに格納して返すIteratorによる実装。while句でhasNext、next関数にて出力はJson
{"EMP" : {"EMPNO" : "7782","ENAME" : "CLARK","HIREDATE" : "1981-06-09 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7788","ENAME" : "SCOTT","HIREDATE" : "1987-04-19 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7839","ENAME" : "KING","HIREDATE" : "1981-11-17 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7844","ENAME" : "TURNER","HIREDATE" : "1981-09-08 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7876","ENAME" : "ADAMS","HIREDATE" : "1987-05-23 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7900","ENAME" : "JAMES","HIREDATE" : "1981-12-03 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7902","ENAME" : "FORD","HIREDATE" : "1981-12-03 00:00:00.0"}}
{"EMP" : {"EMPNO" : "7934","ENAME" : "MILLER","HIREDATE" : "1982-01-23 00:00:00.0"}}
 --- ColumnNameList
 EMPNO ENAME HIREDATE
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO [7700]
select EMPNO,ENAME,HIREDATE from EMP where empno >= ? order by EMPNO
 --- Array SELECTした結果をArray[Any]に格納して返すIteratorによる実装。while句でhasNext、next関数にて出力はtoString
7782 CLARK 1981-06-09 00:00:00.0
7788 SCOTT 1987-04-19 00:00:00.0
7839 KING 1981-11-17 00:00:00.0
7844 TURNER 1981-09-08 00:00:00.0
7876 ADAMS 1987-05-23 00:00:00.0
7900 JAMES 1981-12-03 00:00:00.0
7902 FORD 1981-12-03 00:00:00.0
7934 MILLER 1982-01-23 00:00:00.0
 --- ColumnNameList
 EMPNO ENAME HIREDATE
 --- End Sql
End

Post Details

  • Post Title: Scala Iterator for JDBC
  • Author: admin
  • Filed As: Scala
  • Tags:
  • You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

コメントを残す