Google
 
Web andore.com
Powered by SmartDoc

11. JDBCを用いたデータアクセス(Ver 1.2.7)

11.1 イントロ

Springで提供されているこのJDBC抽象化フレームワークは4つの異なるパッケージ、coredatasourceobjectsupportによって構成されている。

org.springframework.jdbc.coreパッケージには、JdbcTemplateクラスと様々なコールバック用インタフェース、それに様々な関係するクラスが含まれている。

org.springframework.jdbc.datasourceパッケージには、DataSourceに簡単にアクセスするためのユーティリティクラスや、単純なDataSourceの実装がいろいろ含まれており、この実装は変更していないJDBCコードをJ2EEコンテナ外部でテスト、実行するために用いられる。ユーティリティクラスには、JNDIからのコネクションの取得と、必要であればコネクション切断のための静的なメソッドが用意されている。このメソッドでは、例えばDataSourceTransactionManagerで使うためのスレッドバウンドコネクションがサポートされている。

次に、org.springframework.jdbc.objectパッケージには、RDBMSの問い合わせ、更新、ストアドプロシージャをスレッドセーフで再利用可能なオブジェクトとして表現したクラスが含まれている。もちろん問い合わせから返されたオブジェクトはデータベースから"切り離される"が、この方法はJDOによりモデル化される。このより上位レベルのJDBC抽象化はorg.springframework.jdbc.coreパッケージによる低レベル抽象化に依存している。

最後に、org.springframework.jdbc.supportパッケージにはSQLExceptionの変換機能やいくつかのユーティリティクラスがある。

JDBC処理中にスローされた例外はorg.springframework.daoパッケージに定義されている例外に変換される。これはつまり、SpringのJDBC抽象化レイヤを使っているコードでは、JDBCやRDBMSに依存したエラーハンドリングを実装する必要がない、ということだ。変換された例外は全て未検査であり、他の例外が呼び出し側へ伝播できる場合でも回復が可能な例外をキャッチするかどうかの選択は委ねられている。

11.2 JDBC Coreのクラスを使って基本的なJDBC処理やエラー処理を制御する

11.2.1 JdbcTemplate

これは、JDBCのコアパッケージの中でも中心的なクラスだ。これは、リソースの生成、解放をハンドリングしてくれるのでJDBCが簡単に使えるようになる。これによりコネクションを常に閉じるのを忘れるようなよくあるエラーが回避できる。このクラスでは、ステートメントの生成、実行のようなコアJDBCのワークフローを実行し、アプリケーションコードからSQLの受け渡しと結果の抽出を分離する。このクラスでは、SQLの問い合わせ、ステートメントやストアドプロシージャコールの更新、ResultSetに設定されたイテレーションを模擬し、返されたパラメータ値の抽出を行う。さらに、JDBC例外をキャッチし、org.springframework.daoパッケージで定義された、汎用的で、より有益な例外ヒエラルキーに変換する。

このクラスを使うコードでは、明確に定義された契約を持たせるコールバックインタフェースの実装のみが必要となる。PreparedStatementCreatorコールバックインタフェースは、このクラスで渡されるコネクションを渡され、あらかじめ用意されたステートメントを生成し、SQLおよび必要となるパラメータを提供する。同じことはコールバックステートメントを生成するCallableStatementCreatorインタフェースにも当てはまる。RowCallbackHandlerインタフェースはResultSetの各行から値の抽出を行う。

このクラスはDataSourceのリファレンスで直接インスタンス化することでサービスを実装したり、アプリケーションコンテキスト内であらかじめ用意され、ビーンリファレンスとしてサービスに渡される、といった使い方ができる。(注:DataSourceはサービスに直接渡される1つ目のケースでも、テンプレートがあらかじめ用意される2つ目のケースでも、常にアプリケーションコンテキスト内のビーンとして構築されるべきだ)。このクラスはコールバックインタフェースや、SQLExceptionTranslatorインタフェースによりパラメータ化されているので、サブクラスを用意する必要はない。このクラスで発生したSQL関連の問題については全てログに記録される。

11.2.2 DataSource

データベースから取得したデータで動作させるためには、データベースへのコネクションを取得する必要がある。Springで行っている方法は、DataSourceを経由するというものだ。DataSourceはJDBC仕様の一部で、汎用化されたコネクションファクトリとしてみなすことができる。このように見なすことにより、コンテナやフレームワークでコネクションプーリングやトランザクション管理に関する問題をアプリケーションコードから隠蔽することが可能になる。開発者としては、データベースにどのように接続するかといった詳細については知る必要がなく、データソースをセットアップする管理者の責任だ。コードを開発し、テストしている間は、その両方の役割を担う必要があるのがほとんどだと思うが、どのように生成したデータのソースが設定されているかは必ずしも知る必要はないのだ。

SpringのJDBC層を使う場合、データソースをJNDIから取得するか、Springの配布パッケージで提供されている実装を使って自分で設定するかを選択することができる。後者はウェブコンテナの外部でユニットテストを実施するのに便利だ。本章では、DriverManagerDataSourceの実装を用いるが、後者の方法をカバーする実装は他にもいくつか用意されている。このDriverManagerDataSourceはJDBCコネクションを取得する際に用いている方法とおそらく同じ方法で動作する。DriverManagerがドライバクラスをロードできるようにするために、JDBCドライバの完全なクラス名指定する。そして、ドライバ間で変わるURLを用意する必要がある。ここで使用する正しい値については、使うドライバのドキュメントを参照する必要がある。最後に、データベースに接続するためのユーザ名とパスワードを用意する。DriverManagerDataSourceの設定するための例は下記のようになる。

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( "org.hsqldb.jdbcDriver");
dataSource.setUrl( "jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername( "sa");
dataSource.setPassword( ""); 

11.2.3 SQLExceptionTranslator

SQLExceptionTranslatorは、SQLExceptionとデータアクセスストラテジを問わないorg.springframework.dao.DataAccessExceptionとの間で変換が可能なクラスにより実装されるインタフェースだ。

実装をよりいっそう正確に、(例えば、JDBC用SQLStateコードを用いて)汎用的にも、(例えばOracleのエラーコードを用いて)プロプライエタリにもすることができる。

SQLErrorCodeSQLExceptionTranslatorはデフォルトで使われる。SQLExceptionTranslatorを実装したものだ。この実装は特定のベンダコードを使う。SQLStateの実装よりもより正確だが、ベンダに特化している。このエラーコードの変換はSQLErrorCodesという名前のJavaBean型クラスで保持されたコードに基づいたものだ。このクラスは、"sql-error-codes.xml"という名前の設定ファイルのコンテンツに基づいてSQLErrorCodesを生成するファクトリであると名前が示唆するSQLErrorCodesFactoryによって生成され、配置される。このファイルはベンダコードで占められ、DatabaseMetaDataから得られたDatabaseProductNameに基づいてカレントのデータベース用コードが使われる。

このSQLErrorCodeSQLExceptionTranslatorは下記のマッチングルールを適用する。

SQLErrorCodeSQLExceptionTranslatorは下記のような方法を使って拡張することが可能だ。

public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
    protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
        if (sqlex.getErrorCode() == -12345)
            return new DeadlockLoserDataAccessException(task, sqlex);
        return null;
    }
}

この例で指定されているエラーコード"-12345"は変換され、他のエラーもデフォルトのトランスレータの実装で変換するように残される。このカスタムトランスレータを使うためには、setExceptionTranslatorメソッドを使ってJdbcTemplateに渡し、このトランスレータを必要とするデータアクセス処理全てにこのJdbcTemplateを使う必要がある。下記は、このカスタムトランスレータをどのように使うかを示した例である。

// create a JdbcTemplate and set data source
JdbcTemplate jt = new JdbcTemplate(); 
jt.setDataSource(dataSource); 
// create a custom translator and set the datasource for the default translation lookup
MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); 
tr.setDataSource(dataSource); 
jt.setExceptionTranslator(tr); 
// use the JdbcTemplate for this SqlUpdate
SqlUpdate su = new SqlUpdate(); 
su.setJdbcTemplate(jt); 
su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); 
su.compile(); 
su.update();

このカスタムトランスレータはデータソースに渡され sql-error-codes.xmlでエラーコードをルックアップするのにこのデフォルト変換を使いたいので、カスタムトランスレータはデータソースに渡される。

11.2.4 実行手順

SQLステートメントを実行するために、必要とされるとても小さなコードがある。ここで必要になるのは、DataSourceJdbcTemplateだけだ。一度使えば、JdbcTemplateで用意されている多くの便利なメソッドを使えるようになる。ここで新しいテーブルを生成するための必要最小限であるが、完全に機能するクラスのために取り込む必要のあるものを示した短いサンプルをお見せしよう。

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAStatement {
    private JdbcTemplate jt;
    private DataSource dataSource;

    public void doExecute() {
        jt = new JdbcTemplate(dataSource);
        jt.execute("create table mytable (id integer, name varchar(100))"); 
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

11.2.5 クエリを実行する

実行方法に加えて、問い合わせの方法もたくさんある。そのうちのいくつかは、値をひとつだけ返すような問い合わせに使うように意図されたものだ。カウントを取得したり、1つの行から特定の値を検索したいことがあると思う。その場合は、queryForIntqueryForLongqueryForObjectを使うといい。最後のものは、返されたJDBC型から引数として渡されたJavaクラスに変換するものである。型変換が無効の場合は、InvalidDataAccessApiUsageExceptionが投げられる。ここに、1つの問い合わせメソッド、1つはint型用、1つはString用の例をお見せする。

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class RunAQuery {
    private JdbcTemplate jt;
    private DataSource dataSource;
  
    public int getCount() {
        jt = new JdbcTemplate(dataSource);
        int count = jt.queryForInt("select count(*) from mytable");
        return count;
    }

    public String getName() {
        jt = new JdbcTemplate(dataSource);
	String name = (String) jt.queryForObject("select name from mytable", String.class);

        return name;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

値を1つしか結果を返さないクエリメソッドに加えて、クエリが返した各行をエントリにもつリストを返すメソッドがいくつか用意されている。最も汎用的なものは、各エントリがMapになっていてそのマップの各エントリが各行のカラム値を表すようなリストを返すqueryForListだ。上述した例に、全ての行のリストを検索するようなメソッドを追加するとすると、下記のようになる。

    public List getList() {
        jt = new JdbcTemplate(dataSource);
        List rows = jt.queryForList("select * from mytable");
        return rows;
    }

これで返されるリストは [{name=Bob, id=1}, {name=Mary, id=2}] のようになっている。

11.2.6 データベースを更新する

他にも利用可能な更新メソッドが多数用意されている。あるプライマリキー用カラムを更新するような例をお見せしよう。この例では、行パラメータ用プレースホルダをもつSQLステートメントを使っている。ほとんどのクエリや更新メソッドにはこの機能が具備されている。このパラメータ値はオブジェクトの配列として渡される。

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAnUpdate {
    private JdbcTemplate jt;
    private DataSource dataSource;

    public void setName(int id, String name) {
        jt = new JdbcTemplate(dataSource);
        jt.update("update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)});
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

11.3 データベースへの接続方法を制御する

11.3.1 DataSourceUtils

JNDIからコネクションを取得したり、必要に応じてコネクションをクローズするためのスタティックメソッドを提供するHelperクラスでは、DataSourceTransactionManagerで使うためのスレッドバウンドコネクションをサポートしている。

注意: getDataSourceFromJndiメソッドはApplicationContext各々でビーンファクトリやアプリケーションコンテキストを使わないアプリケーションでターゲットにされる。自前のビーンやファクトリにあるJdbcTemplateインスタンスもあらかじめ設定しておくのが望ましい。JndiObjectFactoryBeanDataSourceをJNDIからフェッチし、DataSourceビーンリファレンスを他のビーンに渡すのに用いることができる。別のDataSourceへの切り替えについては、どのように設定しているかだけの問題である。FactoryBeanの定義を他の非JNDIなDataSourceに切り替えることもできる。

11.3.2 SmartDataSource

リレーショナルデータベースへの接続を提供することができるクラスで実装されるべきインタフェースである。javax.sql.DataSourceインタフェースを拡張し、操作した後でコネクションが閉じているかどうかを問い合わせることができる。これはコネクションを使いまわしたい場合には、とても役に立つことがある。

11.3.3 AbstractDataSource

興味対象ではない糊の役割をする、SpringのDataSourceの実装用の抽象ベースクラスである。自前のDataSourceの実装を書いている場合はこのクラスを拡張するとよい。

11.3.4 SingleConnectionDataSource

使用後に接続を閉じないシングルコネクションをラップするSmartDataSourceの実装である。当然、マルチスレッドでは動かない。

もし、クライアントコードが、永続化ツールを使う場合のようにコネクションがプールされているという想定でクローズを呼び出す場合、suppressClosetrueをセットする。これは物理的なコネクションの代わりに、クローズ抑制プロキシを返す。この返り値はネイティブのOracleコネクション等にキャストすることはできない。

これは、主にテスト用クラスだ。例えば、簡単なJNDI環境と接続し、アプリケーションサーバの外でコードを簡単にテストができるようになる。DriverManagerDataSourceとは反対に、いつも同じコネクションを使いまわし物理的なコネクションを必要以上に生成するのを避けている。

11.3.5 DriverManagerDataSource

ビーンプロパティからプレーンで古いJDBCドライバを設定するSmartDataSourceの実装である。毎回新しいコネクションを返す。

これは、望ましいApplicationContext内のDataSourceビーンや、あるいは単純なJNDI環境とあわせてJ2EEコンテナの外部でのテストやスタンドアロン環境でのテストに役に立つ。プーリングを想定したConnection.close()のコールは単にコネクションを閉じるので、任意のDataSourceを意識した永続化のコードはちゃんと動くはずだ。しかしながら、commons-dbcpのようなJavaBeanスタイルのコネクションプールを使うのは、たとえテスト環境といえどもとても簡単なので、DriverManagerDataSource上でそのようなコネクションプールを使うのが、多くの場合は望ましい。

TransactionAwareDataSourceProxy

これは、Spring管理下のアェアネスを追加するためにターゲットのDataSourceをラップする、プロキシだ。この点で、J2EEサーバで提供されているトランザクション処理されたJNDI DataSourceに似ている。

標準JDBCのDataSourceインタフェースの実装に必ず呼ばれ、渡される既存のコードが存在する場合を除いて、このクラスを必要とし、このクラスを使うのが望ましいことはほとんどないはずだ。この場合、このコードを使えるようにするのは可能ではあるが、Spring管理のトランザクションに入る。通常は、リソース管理にJdbcTemplateDataSourceUtilsのようなもっとレイヤの高い抽象化を使った自前の新しいコードを書くのが望ましい。

さらなる詳細は、TransactionAwareDataSourceProxyのJavadocを参照してほしい。

11.3.7 DataSourceTransactionManager

単一のJDBCデータソース用PlatformTransactionManagerの実装。特定のデータソースからJDBCコネクションをスレッドにバインドし、潜在的にデータソース毎に1本のスレッドコネクションを割り当てる効果がある。

アプリケーションコードでは、J2EE標準のDataSource.getConnectionの代わりにDataSourceUtils.getConnection(DataSource)からJDBCコネクションを検索する必要がある。これは、検査済みSQLExceptionの代わりにorg.springframework.daoの未検査例外を投げるのでとにかく推奨される。JdbcTemplateのような全てのフレームワーククラスはこの戦略を暗黙的に利用する。このトランザクションマネージャが一緒に用いられない場合は、ルックアップストラテジは共通のもののように正確に動作し、どんな場合でも使用することができる。

独自の分離レベルや適切なJDBCステートメント問い合わせタイムアウトとして適用されるタイムアウトをサポートする。後者をサポートするために、アプリケーションコードは生成されたステートメント各々にJdbcTemplateを使うかDataSourceUtils.applyTransactionTimeoutメソッドを呼ばないといけない。

この実装は、単一のリソースの場合にはJtaTransactionManagerの代わりに用いられる。というのも、JTAをサポートするコンテナを必要としないからだ。もし、要求されたコネクションルックアップパターンに固執するのであれば、両者での切り替えは単に設定次第である。JTAでは独自の分離レベルをサポートしていない点に注意してほしい。

11.4 JDBC操作をJavaオブジェクトとしてモデリングする

このorg.springframework.jdbc.objectパッケージには、よりオブジェクト指向的な方法でデータベースにアクセスできるようにするためのクラス群が含まれている。クエリを実行したり、ビジネスオブジェクトが格納され、そのビジネスオブジェクトのプロパティにマッピングされたリレーショナルカラムデータが付与されたリストとして結果を受け取ることができる。さらにストアドプロシージャを実行したり、アップデートの実行、ステートメントの挿入、削除も行える。

11.4.1 SqlQuery

SQLクエリを表現するための、再利用可能でスレッドセーフなオブジェクト。サブクラスでは、ResultSetをイテレートする間、結果を格納しておけるようなオブジェクトを提供するためにnewResultReader()メソッドを実装しなければならない。このクラスを拡張したMappingSqlQueryで、行をJavaオブジェクトにマッピングするためのもっと便利な実装が提供されているので、このクラスが直接利用されることはほとんどない。SqlQueryを拡張した他の実装としては、MappingSqlQueryWithParametersUpdatableSqlQueryがある。

11.4.2 MappingSqlQuery

MappingSqlQueryは再利用可能な問い合わせではあるが、具象サブクラスでは、JDBCのResultSetの各行をオブジェクトに変換するためのabstractmapRow(ResultSet, int)メソッドを実装しなければならない。

SqlQueryの全ての実装の中で、これが最もよく利用され、また一番簡単なやり方でもある。

下記は、customerテーブルから取り出したデータをCustomerというJavaオブジェクトにマッピングする独自のクエリの例を示したコード片である。

private class CustomerMappingQuery extends MappingSqlQuery {

    public CustomerMappingQuery(DataSource ds) {
        super(ds, "SELECT id, name FROM customer WHERE id = ?");
        super.declareParameter(new SqlParameter("id", Types.INTEGER));
        compile();
    }

    public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
        Customer cust = new Customer();
        cust.setId((Integer) rs.getObject("id"));
        cust.setName(rs.getString("name"));
        return cust;
    } 
  }

我々はこの唯一のパラメータとしてDataSourceをとるこのcustomerのクエリにコンストラクタを提供する。このコンストラクタでは、DataSourceやこのクエリの行を検索するために実行されるべきSQLを渡してスーパクラスのコンストラクタを呼び出す。このSQLには、実行時に渡されるパラメータのためのプレースホルダが含まれているのでPreparedStatementを生成するために利用される。各パラメータはSqlParameterで渡されるdeclareParameterメソッドを使って宣言されなければならない。このSqlParameterは名前java.sql.Typesで定義されたとJDBC型をもつ。全てのパラメータが定義された後、ステートメントがその後実行される準備が整うのでコンパイルメソッドを呼び出す。

では、この独自クエリがインスタンス化され実行されるところのコードを見てみよう。

    public Customer getCustomer(Integer id) {
        CustomerMappingQuery custQry = new CustomerMappingQuery(dataSource); 
        Object[] parms = new Object[1];
        parms[0] = id;
        List customers = custQry.execute(parms);
        if (customers.size() > 0)
            return (Customer) customers.get(0);
        else
            return null;
    }

この例のメソッドは、唯一のパラメータとして渡されるidをもつcustomerを検索する。CustomerMappingQueryクラスのインスタンスを生成した後、渡された全てのパラメータを含んだオブジェクトの配列を生成する。この場合、パラメータは1つしかなく、Integerとして渡される。これで、このパラメータ配列を使ってクエリを実行する準備が整い、クエリにより返される各行に対応するCustomerオブジェクトを含んだリストを取得する。この場合は、もしマッチするとすれば、1つのエントリのみだろう。

11.4.3 SqlUpdate

RdbmsOperationというサブクラスはSQL Updateを表す。クエリのようにupdateオブジェクトは再利用可能だ。全てのRdbmsOperationオブジェクトのように、updateはパラメータを持つことができ、これは、SQLで定義される。

このクラスではqueryオブジェクトのexecute()と類似した多くのupdate()メソッドが用意されている。

このクラスは具象である。サブクラスを作ることはできる(例えば、独自のupdateメソッドを追加するため)が、SQLの設定やパラメータを宣言することで、パラメタライズ化するのは簡単だ。

import java.sql.Types;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;

public class UpdateCreditRating  extends SqlUpdate {
    public UpdateCreditRating(DataSource ds) {
        setDataSource(ds);
        setSql("update customer set credit_rating = ? where id = ?");
        declareParameter(new SqlParameter(Types.NUMERIC));
        declareParameter(new SqlParameter(Types.NUMERIC));
        compile();
    }

    /**
     * @param id for the Customer to be updated
     * @param rating the new value for credit rating
     * @return number of rows updated
     */
    public int run(int id, int rating) {
        Object[] params =
            new Object[] {
                new Integer(rating),
                new Integer(id)};
        return update(params);
    }
}

11.4.4 StoredProcedure

RDBMSのストアドプロシージャをオブジェクト抽象化したスーパクラスである。このクラスはアブストラクトでexecuteメソッドはprotectedであり、より限定された型用のサブクラスから以外から使われるのを防いでいる。

継承されたSQLのプロパティはRDBMSのストアドプロシージャの名前だ。JDBC3.0では名前付パラメータが取り入れられたが、このクラスで提供されている他の機能もJDBC3.0が必要となる。

オラクルのsysdate()関数を呼び出すプログラムの例をお見せしよう。このストアドプロシージャ機能を使うには、StoredProcedureを拡張したクラスを作らなければならない。これには入力パラメータはなく、SqlOutParameterクラスを使ったdateとして宣言された出力パラメータが1つある。execute()メソッドは各エントリにkeyというパラメータ名を使った出力パラメータが宣言されたマップを返す。

import java.sql.Types;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.datasource.*;
import org.springframework.jdbc.object.StoredProcedure;

public class TestStoredProcedure {

    public static void main(String[] args)  {
        TestStoredProcedure t = new TestStoredProcedure();
        t.test();
        System.out.println("Done!");
    }
    
    void test() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("oracle.jdbc.OracleDriver");
        ds.setUrl("jdbc:oracle:thin:@localhost:1521:mydb");
        ds.setUsername("scott");
        ds.setPassword("tiger");

        MyStoredProcedure sproc = new MyStoredProcedure(ds);
        Map res = sproc.execute();
        printMap(res);
    }

    private class MyStoredProcedure extends StoredProcedure {
        public static final String SQL = "sysdate";

        public MyStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setFunction(true);
            setSql(SQL);
            declareParameter(new SqlOutParameter("date", Types.DATE));
            compile();
        }

        public Map execute() {
            Map out = execute(new HashMap());
            return out;
        }

    }

    private static void printMap(Map r) {
        Iterator i = r.entrySet().iterator();
        while (i.hasNext()) {
            System.out.println((String) i.next().toString());  
        }
    }
}

11.4.5 SqlFunction

結果を1行だけ返すクエリ用SQLの"関数"ラッパである。デフォルトではintを返す振る舞いになっているが、特殊な型のパラメータを返すメソッドを使ってオーバライドすることができる。これは、JdbcTemplateQueryForXxxメソッドを使うのと似たものである。SqlFunctionの利点は、JdbcTemplateの生成はバックグランドで実行されるので、自分で生成する必要がない、ということだ。

このクラスは、"select user()"や、"select sysdate from dual"のようなクエリを使って結果を1つだけ返すSQL関数を呼び出すのに使うためのものだ。より複雑なストアド関数の呼び出しやストアドプロシージャやストアド関数を起動するためのCallableStatementを使うためののものではない。このような処理を行うには、StoredProcedureSqlCallを使うこと。

これは具象クラスで、通常はサブクラスを作る必要はない。このパッケージを使ったコードは、この型のオブジェクトを生成したり、SQLやパラメータを宣言でき、従って関数を実行するために適切なrunメソッドを繰り返し起動することができる。テーブルの行数を問い合わせる例をお見せしよう。

    public int countRows() {
        SqlFunction sf = new SqlFunction(dataSource, "select count(*) from mytable");
        sf.compile();
        return sf.run();
    }