PreparedStatement
日本語 | 準備済み命令文 |
英語 | prepared statement |
ふりがな | ぷりぺあーどすてーとめんと |
フリガナ | プリペアードステートメント |
プリペアードステートメントのSQLを実行するためのインターフェイス。
J2SEに含まれるインターフェイスのひとつ。パッケージも含めたインターフェイス名はjava.sql.PreparedStatement。
プリペアードステートメントのように、「?」のパラメーターを持つSQLを実行するためのインターフェイス。
インターフェイスのため、newでインスタンスを作ることはできない。代わりにConnectionインターフェイスのprepareStatement()メソッドでインスタンスを作成しその参照を取得する。
?への値のセットは、PreparedStatementインターフェイスのsetInt()等のメソッドを使用する。
第1引数には?のインデックスナンバーを指定する。ただし、インデックスナンバーは1から始まる。配列等と異なるため注意。たとえば「INSERT INTO TABLE_TEST VALUES( ?, ? )」というSQLの場合、最初のハテナのインデックスナンバーは1、後のハテナのインデックスナンバーは2となる。
第2引数にはセットする値を指定する。この時、値は単にSQLが置き換えられるわけではなく、適切な形式でセットされる。たとえば文字列をセットするsetString()メソッドの場合、「?」は「'セットする値'」と、文字列リテラルを作るための「'」(アポストロフィ)で囲んでセットされる。また、この「セットする値」に「'」が含まれていた場合には「\'」に置き換えられ「'」が無効化される。この無効化を「サニタイジング」と呼ぶ。
SQL文の実行は、executeQuery()メソッドかexecuteUpdate()メソッドで行う。SELECT文のように変化を伴わないSQLの場合にはexecuteQuery()メソッドを、INSERT文のように変化を伴うSQLの場合にはexecuteUpdate()メソッドを使用する。
使用後は必ずclose()メソッドを呼び出し終了処理を行う。このメソッドの呼び出しは、呼び出し忘れを避けるため必ずfinally内で行う。
決して、PreparedStatementインターフェイスの参照型変数に、close()メソッドを呼ぶ前に新しい参照をセットしてはならない。このような理由によりclose()メソッドを呼び出さないStatementインターフェイスのインスタンスが残ると、メモリ等の資源が解放されず、後に致命的な問題となり異常終了する可能性もある。
SQLが、何らかの形で「外部から入力された値をセットする必要がある」場合には、必ずSQLは「セットする値を?でパラメーター化する」ようにし、PreparedStatementインターフェイスを使用して実行する。
これを行わないとサニタイジングされず直接使用されてしまい、SQLインジェクションが起こされる可能性があるためである。
J2SEに含まれるインターフェイスのひとつ。パッケージも含めたインターフェイス名はjava.sql.PreparedStatement。
プリペアードステートメントのように、「?」のパラメーターを持つSQLを実行するためのインターフェイス。
インターフェイスのため、newでインスタンスを作ることはできない。代わりにConnectionインターフェイスのprepareStatement()メソッドでインスタンスを作成しその参照を取得する。
?への値のセットは、PreparedStatementインターフェイスのsetInt()等のメソッドを使用する。
第1引数には?のインデックスナンバーを指定する。ただし、インデックスナンバーは1から始まる。配列等と異なるため注意。たとえば「INSERT INTO TABLE_TEST VALUES( ?, ? )」というSQLの場合、最初のハテナのインデックスナンバーは1、後のハテナのインデックスナンバーは2となる。
第2引数にはセットする値を指定する。この時、値は単にSQLが置き換えられるわけではなく、適切な形式でセットされる。たとえば文字列をセットするsetString()メソッドの場合、「?」は「'セットする値'」と、文字列リテラルを作るための「'」(アポストロフィ)で囲んでセットされる。また、この「セットする値」に「'」が含まれていた場合には「\'」に置き換えられ「'」が無効化される。この無効化を「サニタイジング」と呼ぶ。
SQL文の実行は、executeQuery()メソッドかexecuteUpdate()メソッドで行う。SELECT文のように変化を伴わないSQLの場合にはexecuteQuery()メソッドを、INSERT文のように変化を伴うSQLの場合にはexecuteUpdate()メソッドを使用する。
使用後は必ずclose()メソッドを呼び出し終了処理を行う。このメソッドの呼び出しは、呼び出し忘れを避けるため必ずfinally内で行う。
決して、PreparedStatementインターフェイスの参照型変数に、close()メソッドを呼ぶ前に新しい参照をセットしてはならない。このような理由によりclose()メソッドを呼び出さないStatementインターフェイスのインスタンスが残ると、メモリ等の資源が解放されず、後に致命的な問題となり異常終了する可能性もある。
SQLが、何らかの形で「外部から入力された値をセットする必要がある」場合には、必ずSQLは「セットする値を?でパラメーター化する」ようにし、PreparedStatementインターフェイスを使用して実行する。
これを行わないとサニタイジングされず直接使用されてしまい、SQLインジェクションが起こされる可能性があるためである。
// Sample.java
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Sample
{
public static void main( String[] args )
{
Connection conn = null;
PreparedStatement psCreate = null;
PreparedStatement psInsert = null;
PreparedStatement psSelect = null;
ResultSet rs1 = null;
ResultSet rs2 = null;
try
{
// この使用例の使用方法についてはSQLの項目を参照してください。
// JDBCドライバをロードします。
Class.forName( "org.hsqldb.jdbcDriver" );
// コネクションを取得します。
String url = "jdbc:hsqldb:mem:aname";
String user = "sa";
String password = "";
conn = DriverManager.getConnection( url, user, password );
// テーブルを作ります。
psCreate = null;
final String SQL_CREATE = "CREATE TABLE TABLE_TEST( NAME VARCHAR, VALUE INT );";
psCreate = conn.prepareStatement( SQL_CREATE );
psCreate.execute();
// 行を追加します。
// セットする値は直接記述せず、?にしておきます。
// この?になっているSQLがプリペアードステートメントです。
final String SQL_INSERT = "INSERT INTO TABLE_TEST VALUES( ?, ? );";
// プリペアードステートメントは、Connectionインターフェイスの
// prepareStatement()メソッドでPreparedStatementインターフェイスの
// インスタンスを作って、これを使って操作します。
psInsert = conn.prepareStatement( SQL_INSERT );
for( int iF1 = 0; iF1 < 3; ++iF1 )
{
// ?に値をセットします。
// 列の指定は、インデックスナンバーで行います。
// 1から始まるので注意。
psInsert.setString( 1, "ネーム" + iF1 );
psInsert.setInt( 2, iF1 );
// execute()メソッドでSQLを実行して、行を追加します。
psInsert.execute();
}
// 検索します。その際、WHEREで絞り込みを行います。
// 絞り込みに使用する値はセットせず、?にしておきます。
final String SQL_SELECT = "SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = ?;";
// INSERTの時と同じく、?に値をセットする場合には
// PreparedStatementインターフェイスを使用します。
psSelect = conn.prepareStatement( SQL_SELECT );
// ?に検索条件をセットします。
// これもインデックスナンバーは1から始まるので注意してください。
psSelect.setString( 1, "ネーム1" );
// 検索します。
rs1 = psSelect.executeQuery();
while( rs1.next() )
{
String name = rs1.getString( "NAME" );
int value = rs1.getInt( "VALUE" );
System.out.println( name + ", " + value );
}
// ネーム1, 1
// setInt()等のメソッドを使用した結果のSQLは、単なる「置き換え」ではありません。
// たとえば、こんな感じのパラメーターをセットしてみます。
psSelect.setString( 1, "' OR 'DUMMY' = 'DUMMY" );
// もし単なる置き換えであれば、
// 「SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = '' OR 'DUMMY' = 'DUMMY'」
// というSQLが作られ、検索条件が変わります。ですが
rs2 = psSelect.executeQuery();
while( rs2.next() )
{
String name = rs2.getString( "NAME" );
int value = rs2.getInt( "VALUE" );
System.out.println( name + ", " + value );
}
// (0件です)
// と、実際にはそうならず、'がサニタイジングされてそのまま検索条件になります。
// この機能のおかげでSQLインジェクションを防ぐことができます。
}
catch( SQLException e )
{
// SQLの実行で問題があった場合に投げられます。
e.printStackTrace();
}
catch( ClassNotFoundException e )
{
// JDBCドライバが存在しなかった場合に投げられます。
e.printStackTrace();
}
finally
{
if( rs1 != null )
{
try
{
rs1.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( rs2 != null )
{
try
{
rs2.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psSelect != null )
{
try
{
psSelect.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psInsert != null )
{
try
{
psInsert.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psCreate != null )
{
try
{
psCreate.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( conn != null )
{
try
{
conn.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
}
}
}
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Sample
{
public static void main( String[] args )
{
Connection conn = null;
PreparedStatement psCreate = null;
PreparedStatement psInsert = null;
PreparedStatement psSelect = null;
ResultSet rs1 = null;
ResultSet rs2 = null;
try
{
// この使用例の使用方法についてはSQLの項目を参照してください。
// JDBCドライバをロードします。
Class.forName( "org.hsqldb.jdbcDriver" );
// コネクションを取得します。
String url = "jdbc:hsqldb:mem:aname";
String user = "sa";
String password = "";
conn = DriverManager.getConnection( url, user, password );
// テーブルを作ります。
psCreate = null;
final String SQL_CREATE = "CREATE TABLE TABLE_TEST( NAME VARCHAR, VALUE INT );";
psCreate = conn.prepareStatement( SQL_CREATE );
psCreate.execute();
// 行を追加します。
// セットする値は直接記述せず、?にしておきます。
// この?になっているSQLがプリペアードステートメントです。
final String SQL_INSERT = "INSERT INTO TABLE_TEST VALUES( ?, ? );";
// プリペアードステートメントは、Connectionインターフェイスの
// prepareStatement()メソッドでPreparedStatementインターフェイスの
// インスタンスを作って、これを使って操作します。
psInsert = conn.prepareStatement( SQL_INSERT );
for( int iF1 = 0; iF1 < 3; ++iF1 )
{
// ?に値をセットします。
// 列の指定は、インデックスナンバーで行います。
// 1から始まるので注意。
psInsert.setString( 1, "ネーム" + iF1 );
psInsert.setInt( 2, iF1 );
// execute()メソッドでSQLを実行して、行を追加します。
psInsert.execute();
}
// 検索します。その際、WHEREで絞り込みを行います。
// 絞り込みに使用する値はセットせず、?にしておきます。
final String SQL_SELECT = "SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = ?;";
// INSERTの時と同じく、?に値をセットする場合には
// PreparedStatementインターフェイスを使用します。
psSelect = conn.prepareStatement( SQL_SELECT );
// ?に検索条件をセットします。
// これもインデックスナンバーは1から始まるので注意してください。
psSelect.setString( 1, "ネーム1" );
// 検索します。
rs1 = psSelect.executeQuery();
while( rs1.next() )
{
String name = rs1.getString( "NAME" );
int value = rs1.getInt( "VALUE" );
System.out.println( name + ", " + value );
}
// ネーム1, 1
// setInt()等のメソッドを使用した結果のSQLは、単なる「置き換え」ではありません。
// たとえば、こんな感じのパラメーターをセットしてみます。
psSelect.setString( 1, "' OR 'DUMMY' = 'DUMMY" );
// もし単なる置き換えであれば、
// 「SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = '' OR 'DUMMY' = 'DUMMY'」
// というSQLが作られ、検索条件が変わります。ですが
rs2 = psSelect.executeQuery();
while( rs2.next() )
{
String name = rs2.getString( "NAME" );
int value = rs2.getInt( "VALUE" );
System.out.println( name + ", " + value );
}
// (0件です)
// と、実際にはそうならず、'がサニタイジングされてそのまま検索条件になります。
// この機能のおかげでSQLインジェクションを防ぐことができます。
}
catch( SQLException e )
{
// SQLの実行で問題があった場合に投げられます。
e.printStackTrace();
}
catch( ClassNotFoundException e )
{
// JDBCドライバが存在しなかった場合に投げられます。
e.printStackTrace();
}
finally
{
if( rs1 != null )
{
try
{
rs1.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( rs2 != null )
{
try
{
rs2.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psSelect != null )
{
try
{
psSelect.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psInsert != null )
{
try
{
psInsert.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( psCreate != null )
{
try
{
psCreate.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
if( conn != null )
{
try
{
conn.close();
}
catch( SQLException e )
{
// 各close()メソッドからもSQLException例外が
// 投げられるので、拾っておきます。
e.printStackTrace();
}
}
}
}
}
// Sample.java import java.sql.DriverManager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class Sample { public static void main( String[] args ) { Connection conn = null; PreparedStatement psCreate = null; PreparedStatement psInsert = null; PreparedStatement psSelect = null; ResultSet rs1 = null; ResultSet rs2 = null; try { // この使用例の使用方法についてはSQLの項目を参照してください。 // JDBCドライバをロードします。 Class.forName( "org.hsqldb.jdbcDriver" ); // コネクションを取得します。 String url = "jdbc:hsqldb:mem:aname"; String user = "sa"; String password = ""; conn = DriverManager.getConnection( url, user, password ); // テーブルを作ります。 psCreate = null; final String SQL_CREATE = "CREATE TABLE TABLE_TEST( NAME VARCHAR, VALUE INT );"; psCreate = conn.prepareStatement( SQL_CREATE ); psCreate.execute(); // 行を追加します。 // セットする値は直接記述せず、?にしておきます。 // この?になっているSQLがプリペアードステートメントです。 final String SQL_INSERT = "INSERT INTO TABLE_TEST VALUES( ?, ? );"; // プリペアードステートメントは、Connectionインターフェイスの // prepareStatement()メソッドでPreparedStatementインターフェイスの // インスタンスを作って、これを使って操作します。 psInsert = conn.prepareStatement( SQL_INSERT ); for( int iF1 = 0; iF1 < 3; ++iF1 ) { // ?に値をセットします。 // 列の指定は、インデックスナンバーで行います。 // 1から始まるので注意。 psInsert.setString( 1, "ネーム" + iF1 ); psInsert.setInt( 2, iF1 ); // execute()メソッドでSQLを実行して、行を追加します。 psInsert.execute(); } // 検索します。その際、WHEREで絞り込みを行います。 // 絞り込みに使用する値はセットせず、?にしておきます。 final String SQL_SELECT = "SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = ?;"; // INSERTの時と同じく、?に値をセットする場合には // PreparedStatementインターフェイスを使用します。 psSelect = conn.prepareStatement( SQL_SELECT ); // ?に検索条件をセットします。 // これもインデックスナンバーは1から始まるので注意してください。 psSelect.setString( 1, "ネーム1" ); // 検索します。 rs1 = psSelect.executeQuery(); while( rs1.next() ) { String name = rs1.getString( "NAME" ); int value = rs1.getInt( "VALUE" ); System.out.println( name + ", " + value ); } // ネーム1, 1 // setInt()等のメソッドを使用した結果のSQLは、単なる「置き換え」ではありません。 // たとえば、こんな感じのパラメーターをセットしてみます。 psSelect.setString( 1, "' OR 'DUMMY' = 'DUMMY" ); // もし単なる置き換えであれば、 // 「SELECT NAME, VALUE FROM TABLE_TEST WHERE NAME = '' OR 'DUMMY' = 'DUMMY'」 // というSQLが作られ、検索条件が変わります。ですが rs2 = psSelect.executeQuery(); while( rs2.next() ) { String name = rs2.getString( "NAME" ); int value = rs2.getInt( "VALUE" ); System.out.println( name + ", " + value ); } // (0件です) // と、実際にはそうならず、'がサニタイジングされてそのまま検索条件になります。 // この機能のおかげでSQLインジェクションを防ぐことができます。 } catch( SQLException e ) { // SQLの実行で問題があった場合に投げられます。 e.printStackTrace(); } catch( ClassNotFoundException e ) { // JDBCドライバが存在しなかった場合に投げられます。 e.printStackTrace(); } finally { if( rs1 != null ) { try { rs1.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } if( rs2 != null ) { try { rs2.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } if( psSelect != null ) { try { psSelect.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } if( psInsert != null ) { try { psInsert.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } if( psCreate != null ) { try { psCreate.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } if( conn != null ) { try { conn.close(); } catch( SQLException e ) { // 各close()メソッドからもSQLException例外が // 投げられるので、拾っておきます。 e.printStackTrace(); } } } } }