私は ORM フレームワーク Mybatis を使用しており、mybatis でいくつかの共通機能を使用しています。現在、プロジェクト開発では、特定のテーブルのフィールドに対する各ユーザーのクエリを制限し、たとえば、特定のテーブルの特定のフィールドをユーザーがクエリできないようにする必要があるビジネスがあります。この場合、テーブル名とフィールド名を動的に渡す SQL を構築する必要があります。同じ問題に遭遇した友人に役立つことを願って、解決策をまとめます。
動的 SQL は、mybatis の強力な機能の 1 つです。mybatis は SQL ステートメントをプリコンパイルする前に、SQL を動的に解析して BoundSql オブジェクトに変換します。まず、mybatis での #{} と ${} の使用法を理解しましょう:
動的 SQL 解析プロセスでは、#{} と ${} の効果は異なります:
#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。
たとえば、次の SQLステートメント
select * from user where name = #{name};
次のように解析されます:
select * from user where name = ?;
#{} がパラメータのプレースホルダーとして解析されることがわかりますか? 。
${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换
たとえば、次の SQL ステートメント:
select * from user where name = ${name};
パラメータ「sprite」を渡すと、SQL は次のように解析されます:
select * from user where name = "sprite";
プリコンパイル前の SQL ステートメントには変数名が含まれていないことがわかります。
${ } の変数の置換ステージは動的 SQL 解析ステージにあり、# { } の変数の置換ステージは DBMS にあります。
#{} と ${} の違いは次のように簡単に要約できます:
#{} は受信パラメータを文字列として扱い、受信パラメータに二重引用符を追加します
${} はインポートされた
#{} は SQL インジェクションを大幅に防ぐことができます。 ${} はプリコンパイル前に変数に置き換えられます。 SQLインジェクション。次の SQL
select * from ${tableName} where name = ${name}
受信パラメータ tableName が user; delete user; -- の場合、SQL が動的に解析された後、プリコンパイル前の SQL は次のようになります:
select * from user; delete user; -- where name = ?;
--次のステートメントはコメントとして機能しません。友達も私もびっくりしました! ! !元のクエリ ステートメントに、テーブル データを削除する SQL ステートメントがこっそり含まれていたことがわかりましたか? ! !大事なことを3回も言うと、どれだけリスクが高いか想像できると思います。
${} は、通常、データベースのテーブル名、フィールド名などを転送するために使用されます
#{} が使用できる場所では、${} を使用しないようにしてください
それでは、本題に入ります。上記の分析では、テーブル名とフィールド名についてはいくつかのアイデアを動的に呼び出す方法をすでにご存知かと思います。例は次のとおりです。
<select id="getUser" resultType="java.util.Map" parameterType="java.lang.String" statementType="STATEMENT"> select ${columns} from ${tableName} where COMPANY_REMARK = ${company} </select>
テーブル名とフィールド名の動的呼び出しを実現するには、プリコンパイルは使用できず、statementType="STATEMENT"" を追加する必要があります。
statementType:STATEMENT(非预编译),PREPARED(预编译)或CALLABLE中的任意一个,这就告诉 MyBatis 分别使用Statement,PreparedStatement或者CallableStatement。默认:PREPARED。这里显然不能使用预编译,要改成非预编译。
次に、SQL の変数値は ${ # {xxx} ではなく、xxx} です。
${} は、SQL を生成するために受け取ったパラメータを直接表示するため、たとえば、${xxx} で渡されるパラメータが文字列データである場合、パラメータの前に引用符を追加する必要があります。次のように渡されます:
String name = "sprite"; name = "'" + name + "'";