jdbc mysql 缓存_JDBC操作MySQL(1)—PreparedStatement

JDBC作为JAVA访问数据库的一套规范与标准,统一了数据库操作的API,大大简化了程序开发工作。不过由于历史原因,MySQL对JDBC默认的实现与规范定义或者说其它数据库如Oracle并不一致,为了更完整记录这些差异,计划抽空写个系列,整理下这些可能会误解的常用功能。第一篇首先聊聊PreparedStatement。

MySQL JDBC PreparedStatement

Prepare SQL的产生原因与实现原理

数据库SQL执行过程包括以下阶段: 词法分析->语法分析->语义分析->执行计划优化->执行。【词法分析->语法分析】这两个阶段称之为硬解析。词法分析识别SQL中每个词,语法分析解析SQL语句是否符合(SQL92、99、方言等)语法,并得到一棵语法树。

其实基于SQL的架构设计,基本都有这样一个处理过程,TDDL、ShardingJDBC、MyCAT都如此,当然这些产品都相比于数据库,支持的关键词、语法都只是其子集。另外SQL解析器可基于Yacc、Lex、Antlr、Javacc等构建,当然如果对解析性能要更好要求,则需要进行一个纯手工编写的解析器,例如阿里的Druid中的SQL解析器,应用可基于Vistor模式进行使用。

Prepare SQL也叫预编译SQL、Prepared Statements或者Parameterized Statements,就是将这类SQL中的值用占位符?替代,可以视为将SQL语句模板化或者说参数化。预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程。

Prepare的出现就是为了优化硬解析的问题,Prepare在服务器端的执行过程如下:

【Prepare】 接收客户端带?的SQL, 硬解析得到语法树(stmt->Lex), 缓存在线程所在的PS cache中。此cache是一个HASH MAP. Key为stmt->id. 然后返回客户端stmt->id等信息。

【Execute】接收客户端stmt->id和参数等信息(客户端不需要再发SQL过来)。服务器根据stmt->id在PS cache中查找得到硬解析后的stmt, 并设置参数,就可以继续后面的优化和执行。

Prepare在execute阶段可以节省硬解析的时间。因此prepare适用于频繁执行的SQL。

7ef5499d5f60

Prepare的另一个作用是防止SQL注入,这个是纯客户端JDBC通过转义实现的。这也是一般更推荐使用PreparedStatement而不是Statement的主要理由。防SQL注入的具体实现可以参见MySQL驱动中com.mysql.jdbc.PreparedStatement.setString代码。

MySQL驱动中PrepareStament的实现逻辑

看完Prepare的功能原理后,我们看下JDBC操作MySQL时的PreparaStatement,

在com.mysql.jdbc.ConnectionImpl类中

public java.sql.PreparedStatement prepareStatement(String sql,

int resultSetType, int resultSetConcurrency) throws SQLException {

synchronized (getConnectionMutex()) {

checkClosed();

//

// FIXME: Create warnings if can't create results of the given

// type or concurrency

//

PreparedStatement pStmt = null;

boolean canServerPrepare = true;

String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;

if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {

canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);

}

if (this.useServerPreparedStmts && canServerPrepare) {

if (this.getCachePreparedStatements()) {

synchronized (this.serverSideStatementCache) {

pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);

if (pStmt != null) {

((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 =

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值