mybatis深入理解(一)之#与$区别以及sql预编译转载
原创mybatis 中使用 sqlMap 进行 sql 在查询时,经常需要动态传递参数,例如当我们需要根据用户的名称过滤用户时,sql 如下:
select * from user where name = "ruhua";
上述 sql 在,我们希望 name 后的参数 "ruhua" 它是动态的和可变的,即E。在不同的时间按不同的名称查询用户。在……里面 sqlMap 的 xml 该文件中使用了以下内容 sql 可以实现参数的动态传递 name:
select * from user where name = #{name};
或者
select * from user where name = ${name};
对于上面的查询案例,请使用。 #{ } 和 ${ } 结果是一样的,但在某些情况下,我们只能使用两种方法中的一种。
与 $
区别
动态 SQL 是 mybatis 的强大功能之一 ORM 这是该框架的一个重要原因。mybatis 在对 sql 在预编译语句之前, sql 对于动态分析,解析为 BoundSql 对象,此处也用于动态 SQL 已处理完毕。
在动态 SQL 解析阶段, #{ } 和 ${ } 届时将有不同的表演:
#{ } 合二为一 JDBC 预编译语句(prepared statement)参数标记。
例如,sqlMap 中如下的 sql 语句
select * from user where name = #{name};
解析为:
select * from user where name = ?;
一个 #{ } 被合二为一参数占位符 ? 。
而,
${ } 只是为了一个纯洁的 string 替换,动态中 SQL 解析阶段将是变量替换。
例如,sqlMap 中如下的 sql
select * from user where name = ${name};
当我们传递参数时 "ruhua" 时,上述 sql 决议案如下:
select * from user where name = "ruhua";
在预编译之前 SQL 该语句不再包含变量。 name 了。
总而言之, ${ } 变量的替换阶段是动态的。 SQL 解析阶段,而 #{ }变量的替换在中。 DBMS 中。
用法 tips
1、能使用 #{ } 使用这个地方 #{ }
首先,这是出于性能考虑,相同的预编译 sql 可以重复使用。
其次, ${ } 在预编译之前,它已被变量替换,这是存在的。 sql 注入问题 。例如,以下内容 sql,
select * from ${tableName} where name = #{name}
假设,我们的参数 tableName 为 user; delete user; -- ,那么 SQL 动态解析阶段之后,在预编译之前 sql 将变为
select * from user; delete user; -- where name = ?;
-- 后续语句将用作注释,不起作用,因此原始查询语句秘密包含DELETE表数据 SQL!
2,表名作为变量,必须使用。 ${ }
这是因为表名是一个字符串,使用。 sql 占位符用单引号替换字符串。 ,这会导致 sql 语法错误,例如:
select * from #{tableName} where name = #{name};
在预编译之后sql 变为:
select * from ? where name = ?;
假设我们传入一个参数 tableName = "user" , name = "ruhua",则在占位符替换为变量后,sql 语句变为
select * from user where name=ruhua;
上述 sql 语句中存在语法错误,表名不能用单引号引起来。 (请注意,反引号 ``是可能的)。
sql预编译
定义
sql 预编译是指发送中的数据库驱动程序。 sql 语句和参数 DBMS 之前对 sql 语句被编译,以便 DBMS 执行 sql 不需要重新编译。
为什么要预编译
JDBC 中使用的对象 PreparedStatement 使用预编译抽象预编译语句
-
可以优化预编译阶段。 sql 的执行 。
在预编译之后 sql 在大多数情况下,它可以直接执行,DBMS 不需要重新编译,越复杂sql编译的复杂性越高,预编译阶段可以将多个操作合并为一个操作。 -
预编译的语句对象可以重复使用 。
把一个 sql 在预编译后生成 PreparedStatement 对象缓存关闭,下一次同样如此sql,您可以直接使用此缓存。 PreparedState 对象。
mybatis 默认情况下,全部。 sql 预编译的。
mysql预编译源代码解析
mysql 的预编译源代码 com.mysql.jdbc.ConnectionImpl 类,如下所示:
public synchronized java.sql.PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency) throws SQLException {
checkClosed();
//
// FIXME: Create warnings if cant create results of the given
// type or concurrency
//
PreparedStatement pStmt = null;
boolean canServerPrepare = true;
// 不同的数据库系统配对。sql语法转换
String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
// 确定是否可以进行服务器端预编译。
if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
}
// 如果服务器端预编译是可能的
if (this.useServerPreparedStmts && canServerPrepare) {
// 它是否已缓存PreparedStatement对象
if (this.getCachePreparedStatements()) {
synchronized (this.serverSideStatementCache) {
// 从缓存中获取缓存。PreparedStatement对象
pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
if (pStmt != null) {
// 当缓存中存在对象时,原始的 sqlStatement 执行参数清空等。
((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
pStmt.clearParameters();
}
if (pStmt == null) {
try {
// 如果缓存不存在,则调用服务器端。(数据库)预编译的
pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
this.database, resultSetType, resultSetConcurrency);
if (sql.length() < getPreparedStatementCacheSqlLimit()) {
((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
}
// 设置退货类型和并发类型。
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
} catch (SQLException sqlEx) {
// Punt, if necessary
if (getEmulateUnsupportedPstmts()) {
pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
if (sql.length() < getPreparedStatementCacheSqlLimit()) {
this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
}
} else {
throw sqlEx;
}
}
}
}
} else {
// 未启用缓存时,直接调用服务器端预编译的
try {
pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
this.database, resultSetType, resultSetConcurrency);
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
} catch (SQLException sqlEx) {
// Punt, if necessary
if (getEmulateUnsupportedPstmts()) {
pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
} else {
throw sqlEx;
}
}
}
} else {
// 调用服务器端预编译时不支持客户端预编译(不需要数据库) connection )
pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
}
return pStmt;
}
流程图如下:

mybatis之sql动态解析和预编译源代码
mybatis sql 动态解析
mybatis 在调用 connection 进行 sql 在预编译之前,sql语句的动态解析,动态解析主要包括以下功能:
-
占位符的处理
-
动态sql的处理
-
参数类型检查
mybatis强大的动力SQL下面是该函数的具体实现。动态解析涉及的事情太多,不能在后面讨论。
总结
本文主要对此进行了深入的探讨。 mybatis 对 #{ } 和 ${ }不同的治疗方法,并了解 sql 预编译。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123



