MyBatis3を使用して(xmlマッピングを使用して)SQL文字列を生成したいのですが、取得したSQLが無効です。
例、sql文字列を取得します。
SELECT * FROM USER WHERE NAME = john
このsqlには存在しません'文字列値を囲む文字john
in mybatis.xml:
...
<mappers>
<mapper resource="sql1.xml"/>
</mappers>
...
sql1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="sql1">
<select id="select1" parameterType="map" resultType="String" >
SELECT * FROM USERS
WHERE
name LIKE ${name} AND num = ${number}
</select>
</mapper>
in MyBatisSql.Java:
SqlSessionFactory sessionFactory = ConnectionFactory.getSqlSessionFactory();
Configuration configuration = sessionFactory.getConfiguration();
Map pars = new HashMap<String, Object>();
pars.put("name", "john");
pars.put("number", 1345);
MappedStatement ms = configuration.getMappedStatement("sql1.select1);
BoundSql boundSql = ms.getBoundSql(params);
String sql = boundSql.getSql();
System.out.println(sql);
結果は
SELECT * FROM USERS
WHERE
name LIKE john AND num = 12345
このSQLでは、文字列johnは'文字で囲まれていないため、有効なSQLではありません(myBatisを使用して有効なSQL文字列を生成することだけが目的です)。欲しいのですが:
SELECT * FROM USERS
WHERE
name LIKE 'john' AND num = 12345
ありがとう
$ {name}の代わりに#{name}を使用する必要があります。
以下のサンプルは有効なSQLを生成します
<mapper namespace="sql1">
<select id="select1" parameterType="map" resultType="String" >
SELECT * FROM USERS
WHERE
name LIKE #{name} AND num = #{number}
</select>
</mapper>
$文字を使用する場合、MyBatisは文字列パラメータを直接コピーして貼り付けます。一方、#文字を使用すると、パラメータバインディングが使用されます。
次に、selectMap、selectList、またはselectOneを使用してSQLを実行する必要があります。
List<String> resultSet = sessionFactory.openSession().selectList("sql1.select1", pars);
この呼び出しは、パラメーターをステートメントに自動的にバインドして実行します。
警告:
<select id="select1" parameterType="map" resultType="String" >
SELECT * FROM USERS
WHERE
name LIKE #{name} AND num = #{number}
</select>
myBatisは複数の列(SELECT *)を単一の文字列(resultType = "String")にマップできないため、実行に失敗する可能性があります。クエリに対する2つの可能な修正を以下に示します。
<!--Solution One-->
<select id="select1" parameterType="map" resultType="String" >
SELECT name FROM USERS
WHERE
name LIKE #{name} AND num = #{number}
</select>
<!--Solution Two-->
<select id="select1" parameterType="map" resultType="Java.util.LinkedHashMap" >
SELECT * FROM USERS
WHERE
name LIKE #{name} AND num = #{number}
</select>
ソリューション2の場合、Java以下のコードを使用してmybatisクエリを実行する必要があります。
List<Map<?, ?>> resultSet = sessionFactory.openSession().selectList("sql1.select1", pars);
パラメータのバインドはドライバレベルで行われるため、このようなSQL文字列は取得されません
SELECT * FROM USERS
WHERE
name LIKE 'john' AND num = 12345
代わりに、パラメーターバインディングの準備ができているSQLクエリテンプレートを取得します。
SELECT * FROM USERS
WHERE
name LIKE ? AND num = ?
SQL文字列にパラメーターを追加すると、SQLインジェクションが許可されます。安全な方法は、SQLドライバーで提供されるパラメーターバインディングメソッドを使用することです。MyBatisは常にパラメーターバインディングを使用します。
Sqlコマンドを文字列として手動で作成したとします。また、私がデータにアクセスしようとしている悪意のあるユーザーであるとします。私は書くことができます
john' or ''='
したがって、以下のsqlコマンドが生成されます。
SELECT * FROM USERS
WHERE
name LIKE 'john' or ''='' AND num = 12345
パラメータバインディングの2番目の利点は、準備されたステートメントを使用できることです。同じSQLを異なるパラメーターで1000回実行する必要があるとします。
パラメータがバインドされたSQL文字列を生成する場合、
SELECT * FROM USERS WHERE name LIKE 'john' AND num = 12345;
SELECT * FROM USERS WHERE name LIKE 'foo' AND num = 67890;
データベースサーバーは、各sqlコマンドを1つずつ解析して実行する必要があります。
パラメータ化されたSQLクエリでは、
SELECT * FROM USERS WHERE name LIKE ? AND num = ?
SQLドライバーはクエリをキャッシュするため、解析は1回だけ行われ、その後、異なるパラメーターを同じSQLコマンドにバインドします。
パラメータ化されたsql(boundSql)を別のライブラリまたはJavaのJava.sql.Connectionで引き続き使用できます。以下に例を示します。
Connection myConnection;
PreparedStatement preparedStatement = myConnection.prepareStatement(boundSql);
preparedStatement.setString(1, "john"); //First parameter starts with 1 not 0!
preparedStatement.setInt(2, 12345);
ResultSet results = preparedStatement.executeQuery();
SELECT(" * ");
FROM(" student ");
WHERE(" ten LIKE '%' #{ten} '%' ");
使用でき、 '%'と#{}の間にスペース(_)を挿入する必要があります