使用HSqlDB的SQL/JRT功能

使用HSqlDB的過程中,因爲業務需求要執行動態SQL時,才突然發現HSqlDB不具有類似於SQL Server等數據庫的Exec的功能。於是一番探索之後就有了本文。
SQL-92提出了支持動態SQL的標準(12 Support for dynamic execution of SQL language)。各種主流的關係型數據庫也提供了執行方法,比如SQL Server的Exec和sp_executesql。

SQL-99提出了SQL/JRT的概念,這其實也可執行動態SQL。簡單來說,SQL/JRT就是提供了在SQL中訪問Java的static method的能力,它也被稱爲Java Procedure。從維基說明來看,Oracle、DB2和HSqlDb都實現了SQL/JRT。HSqlDb的文檔的第八章中也詳細介紹了Java Language Routines (SQL/JRT),下面以HSqlDb爲例,說明SQL/JRT的基本用法。

第一個例子:
SQL/JRT定義:
CREATE PROCEDURE get_customer(IN id INT, OUT firstname VARCHAR(50), OUT lastname VARCHAR(50)) 
   READS SQL DATA
   LANGUAGE JAVA
   EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.Test01.getCustomerProcedure'

可以看到,存儲過程中實際上需要調用Test01#getCustomerProcedure方法,該方法定義如下:
public static void getCustomerProcedure(int id, String[] firstn, String[] lastn)
   throws java.sql.SQLException {
   firstn[0] = somevalue;  // parameter out value is assigned
   lastn[0] = somevalue;   // parameter out value is assigned
}

存儲過程調用代碼:

CallableStatement c = conn.prepareCall("call get_customer(?,?,?)"); 
c.setInt(1,5);
c.execute();
String firstname = c.getString(2);
String lastname = c.getString(3);
大家不難看出,兩者參數具有對應關係,但是比較特殊的是:OUT參數在Java method中被對應爲數組,並且返回時必須放在數組的第一個元素。 第二個例子:
SQL/JRT定義:
CREATE PROCEDURE new_customer(firstname VARCHAR(50), lastname VARCHAR(50))
   MODIFIES SQL DATA 
   LANGUAGE JAVA
   DYNAMIC RESULT SETS 1
   EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.Test01.newCustomerProcedure'
可以看到,此存儲過程中實際上需要調用Test01#newCustomerProcedure方法,該方法定義如下:
public static void newCustomerProcedure(String firstn, String lastn,ResultSet[] result) throws java.sql.SQLException {
   result[0] = someresultset;  // dynamic result set is assigned
}

存儲過程調用代碼:

CallableStatement c = conn.prepareCall("call new_customer(?,?)"); 
c.setString(1,"firstName");
c.setString(2,"lastName");
c.execute();
if (c.getMoreResults())
{
	ResultSet rs = c.getResultSet();
}
相比上例,該例沒有OUT參數,但是卻有一個需要返回的記錄集(DYNAMIC RESULT SETS 1),Java method處理方式與上例類似,將需要返回的結果賦給了參數數組的第一個元素。 SQL/JRT中調用的Java method必須注意以下事項:
1. 定義必須爲static,而且返回值必須爲void;
2. 存儲過程與Java method必須參數對應。對於OUT參數,method中參數必須聲明爲數組,並且返回值必須作爲數組第一個元素;
3. Java method如需進行JDBC操作,可使用外部Connection對象,也可使用SQL/JRT提供的jdbc:default:connection。
第三點大家可能不是很明白,還是看看下面的例子吧。
SQL/JRT定義:
CREATE PROCEDURE proc1(IN P1 INT, OUT P3 INT)
 SPECIFIC P2 LANGUAGE JAVA DETERMINISTIC MODIFIES SQL DATA 
 EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.TestStoredProcedure.procTest2'");
Java method定義:
public static void procTest2(int p1, Integer[] p3) throws java.sql.SQLException {
     Connection conn = DriverManager.getConnection("jdbc:default:connection");
     java.sql.Statement stmt = conn.createStatement();
     stmt.execute("INSERT INTO MYTABLE VALUES(" + p1 + ",'test1')");


     java.sql.ResultSet rs = stmt.executeQuery("select * from MYTABLE");
     java.sql.ResultSetMetaData meta = rs.getMetaData();


     int cols  = meta.getColumnCount();
     p3[0] = Integer.valueOf(cols);


     rs.close();
     stmt.close();
 }

//or

public static void procTest2(Connection conn, int p1, Integer[] p3) throws java.sql.SQLException

兩種定義方式效果是等價的。第一種不要調用conn.close()去試圖關閉SQL/JRT維護的Connection。第二種SQL/JRT會自動忽略Java method的第一個Connecton參數。
下面列出兩種數據類型的對應關係:
SQL/JRT類型		Java類型
SMALLINT		short or Short
INT			int or Integer
BIGINT			long or Long
NUMERIC  or DECIMAL	BigDecimal
FLOAT  or DOUBLE	double or Double
CHAR or VARCHAR		String
DATE			java.sql.Date
TIME			java.sql.Time
TIMESTAMP		java.sql.Timestamp
BINARY			Byte[]
BOOLEAN			boolean or Boolean
ARRAY of any type	java.sql.Array
TABLE			java.sql.ResultSet

看到這裏,大家應該明白怎麼在HSqlDB中執行動態SQL了吧......
發佈了66 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章