MyBatis 3 - 从映射器获取 SQL 字符串

2022-09-03 02:39:39

我只想使用MyBatis3来生成SQL字符串(使用xml映射),但是我获得的SQL无效。

示例,我获取 sql 字符串:

SELECT * FROM USER WHERE NAME = john

在此 sql 不存在字符串值 john' char 或四周

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>

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没有被' char包围,所以它不是一个有效的SQL(我的目的只是使用myBatis生成有效的SQL字符串)。我希望:

SELECT * FROM USERS
WHERE 
name LIKE 'john' AND num = 12345

谢谢


答案 1

您应该使用 #{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”),因此可能无法执行,因此对查询的两种可能的更正如下所示:

<!--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>

对于解决方案二,您应该使用以下java代码执行mybatis查询:

List<Map<?, ?>> resultSet = sessionFactory.openSession().selectList("sql1.select1", pars);

为什么 getBoundSql 返回带有 ? 的查询的详细信息:

参数绑定是在驱动程序级别完成的,因此您不会获得这样的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 命令创建为字符串,并假设我是尝试访问您的数据的 malicius 用户。我可以写

john' or ''='

因此,这将生成以下 sql 命令:

SELECT * FROM USERS
WHERE 
name LIKE 'john' or ''='' AND num = 12345

参数绑定的其他优点

参数绑定的第二个好处是它允许预准备语句。假设您需要使用不同的参数执行相同的 sql 1000 次。

如果生成绑定了参数的 sql 字符串,

SELECT * FROM USERS WHERE name LIKE 'john' AND num = 12345;
SELECT * FROM USERS WHERE name LIKE 'foo' AND num = 67890;

数据库服务器需要逐个解析每个 sql 命令,然后执行它们。

使用参数化的 sql 查询,

SELECT * FROM USERS WHERE name LIKE ? AND num = ?

SQL 驱动程序缓存查询,因此分析仅执行一次,然后将不同的参数绑定到同一 SQL 命令

更新:在MyBatis之外使用BoundSql

您仍然可以将参数化的 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();

答案 2
            SELECT(" * ");
            FROM(" student ");
            WHERE(" ten LIKE '%' #{ten} '%' ");

您可以使用它,并且必须在'%'和#{}之间放一个空格(_)


推荐