实例分析MySQL JDBC驱动

本文深入探讨MySQL JDBC驱动的使用方法,详细分析了PreparedStatement的工作原理及其内部实现机制,揭示了PreparedStatement在MySQL中的真正作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://developer.51cto.com/art/200907/137823.htm

MySQL是一个中小型关系型数据库管理系统,目前使用的也比较广泛。为了对开发中间DAO层的问题能有更深的理解,在遇到问题的时候能够有更多的思路,于是研究了一下MySQL JDBC驱动的使用,并且在这过程中也发现了一直以来关于PreparedStatement常识理解上的错误,与大家分享。

 

 

下面是个最简单的使用jdbc取得数据的应用。在例子之后我将分成4步,分别是①取得连接,②创建PreparedStatement,③设置参数,④执行查询,来分步分析这个过程。除了设置参数那一步之外,其他的我都画了时序图,如果不想看文字的话,可以对着时序图 。文中的第4步是组装MySQL协议并发送数据包的关键,而且在这部分的(b)环节,我对于PreparedStatement的应用有详细的代码注释分析,建议大家关注一下。


  

  1. Java代码   
  1. Java代码   
public   class  DBHelper {     
  1. public
  class  DBHelper {           public   static  Connection getConnection() {     
  1.     
public   static  Connection getConnection() {              Connection conn =  null ;     
  1.         Connection conn = 
null ;               try  {     
  1.         
try  {                  Class.forName( "com.mysql.jdbc.Driver" );     
  1.             Class.forName(
"com.mysql.jdbc.Driver" );                  conn = DriverManager.getConnection( "jdbc:mysql://localhost/ad?useUnicode=true&characterEncoding=GBK&jdbcCompliantTruncation=false" ,     
  1.             conn = DriverManager.getConnection(
"jdbc:mysql://localhost/ad?useUnicode=true&characterEncoding=GBK&jdbcCompliantTruncation=false" ,                           "root" "root" );     
  1.                     
"root" "root" );              }  catch  (Exception e) {     
  1.         } 
catch  (Exception e) {                  e.printStackTrace();     
  1.             e.printStackTrace();     
        }     
  1.         }     
         return  conn;     
  1.         
return  conn;          }     
  1.     }     
}     
  1. }     
    
  1.     
         /*dao中的方法*/     
  1.         
/*dao中的方法*/           public  List getAllAdvs() {     
  1.     
public  List getAllAdvs() {                   
  1.              
        Connection conn =  null ;     
  1.         Connection conn = 
null ;              ResultSet rs =  null ;     
  1.         ResultSet rs = 
null ;              PreparedStatement stmt =  null ;     
  1.         PreparedStatement stmt = 
null ;              String sql =  "select * from adv where id = ?" ;     
  1.         String sql = 
"select * from adv where id = ?" ;              List advs =  new  ArrayList();     
  1.         List advs = 
new  ArrayList();          
  1.     
        conn = DBHelper.getConnection();     
  1.         conn = DBHelper.getConnection();     
         if  (conn !=  null ) {     
  1.         
if  (conn !=  null ) {                   try  {     
  1.             
try  {                      stmt = conn.prepareStatement(sql);     
  1.                 stmt = conn.prepareStatement(sql);     
                                stmt.setInt( 1 new  Integer( 1 ));     
  1.                                 stmt.setInt(
1 new  Integer( 1 ));                      rs = stmt.executeQuery();     
  1.                 rs = stmt.executeQuery();     
    
  1.     
                 if  (rs !=  null ) {     
  1.                 
if  (rs !=  null ) {                           while  (rs.next()) {     
  1.                     
while  (rs.next()) {                              Adv adv =  new  Adv();     
  1.                         Adv adv = 
new  Adv();                              adv.setId(rs.getLong( 1 ));     
  1.                         adv.setId(rs.getLong(
1 ));                              adv.setName(rs.getString( 2 ));     
  1.                         adv.setName(rs.getString(
2 ));                              adv.setDesc(rs.getString( 3 ));     
  1.                         adv.setDesc(rs.getString(
3 ));                              adv.setPicUrl(rs.getString( 4 ));     
  1.                         adv.setPicUrl(rs.getString(
4 ));          
  1.     
                        advs.add(adv);     
  1.                         advs.add(adv);     
                    }     
  1.                     }     
                }     
  1.                 }     
            }  catch  (SQLException e) {     
  1.             } 
catch  (SQLException e) {                      e.printStackTrace();     
  1.                 e.printStackTrace();     
            }  finally  {     
  1.             } 
finally  {                       try  {     
  1.                 
try  {                          stmt.close();     
  1.                     stmt.close();     
                    conn.close();     
  1.                     conn.close();     
                }  catch  (SQLException e) {     
  1.                 } 
catch  (SQLException e) {                          e.printStackTrace();     
  1.                     e.printStackTrace();     
                }     
  1.                 }     
            }     
  1.             }     
        }     
  1.         }     
         return  advs;     
  1.         
return  advs;          }    
  1.     }    
public   class  DBHelper {  
  1. public
  class  DBHelper {     public   static  Connection getConnection() {  
  1.  
public   static  Connection getConnection() {     Connection conn =  null ;  
  1.   Connection conn = 
null ;      try  {  
  1.   
try  {      Class.forName( "com.mysql.jdbc.Driver" );  
  1.    Class.forName(
"com.mysql.jdbc.Driver" );      conn = DriverManager.getConnection( "jdbc:mysql://localhost/ad?useUnicode=true&characterEncoding=GBK&jdbcCompliantTruncation=false" ,  
  1.    conn = DriverManager.getConnection(
"jdbc:mysql://localhost/ad?useUnicode=true&characterEncoding=GBK&jdbcCompliantTruncation=false" ,         "root" "root" );  
  1.      
"root" "root" );     }  catch  (Exception e) {  
  1.   } 
catch  (Exception e) {      e.printStackTrace();  
  1.    e.printStackTrace();  
  }  
  1.   }  
   return  conn;  
  1.   
return  conn;    }  
  1.  }  
}  
  1. }  
 
  1.  
         /*dao中的方法*/  
  1.         
/*dao中的方法*/     public  List getAllAdvs() {  
  1.  
public  List getAllAdvs() {       
  1.     
  Connection conn =  null ;  
  1.   Connection conn = 
null ;     ResultSet rs =  null ;  
  1.   ResultSet rs = 
null ;     PreparedStatement stmt =  null ;  
  1.   PreparedStatement stmt = 
null ;     String sql =  "select * from adv where id = ?" ;  
  1.   String sql = 
"select * from adv where id = ?" ;     List advs =  new  ArrayList();  
  1.   List advs = 
new  ArrayList();    
  1.  
  conn = DBHelper.getConnection();  
  1.   conn = DBHelper.getConnection();  
   if  (conn !=  null ) {  
  1.   
if  (conn !=  null ) {       try  {  
  1.    
try  {       stmt = conn.prepareStatement(sql);  
  1.     stmt = conn.prepareStatement(sql);  
                                stmt.setInt( 1 new  Integer( 1 ));  
  1.                                 stmt.setInt(
1 new  Integer( 1 ));       rs = stmt.executeQuery();  
  1.     rs = stmt.executeQuery();  
 
  1.  
     if  (rs !=  null ) {  
  1.     
if  (rs !=  null ) {         while  (rs.next()) {  
  1.      
while  (rs.next()) {         Adv adv =  new  Adv();  
  1.       Adv adv = 
new  Adv();         adv.setId(rs.getLong( 1 ));  
  1.       adv.setId(rs.getLong(
1 ));         adv.setName(rs.getString( 2 ));  
  1.       adv.setName(rs.getString(
2 ));         adv.setDesc(rs.getString( 3 ));  
  1.       adv.setDesc(rs.getString(
3 ));         adv.setPicUrl(rs.getString( 4 ));  
  1.       adv.setPicUrl(rs.getString(
4 ));    
  1.  
      advs.add(adv);  
  1.       advs.add(adv);  
     }  
  1.      }  
    }  
  1.     }  
   }  catch  (SQLException e) {  
  1.    } 
catch  (SQLException e) {       e.printStackTrace();  
  1.     e.printStackTrace();  
   }  finally  {  
  1.    } 
finally  {        try  {  
  1.     
try  {        stmt.close();  
  1.      stmt.close();  
     conn.close();  
  1.      conn.close();  
    }  catch  (SQLException e) {  
  1.     } 
catch  (SQLException e) {        e.printStackTrace();  
  1.      e.printStackTrace();  
    }  
  1.     }  
   }  
  1.    }  
  }  
  1.   }  
   return  advs;  
  1.   
return  advs;    } 
  1.  } 
  1. Java代码   
  1. Java代码   
public   class  Driver  extends  NonRegisteringDriver  implements  java.sql.Driver {     
  1. public
  class  Driver  extends  NonRegisteringDriver  implements  java.sql.Driver {           static  {     
  1.     
static  {               try  {     
  1.         
try  {                  java.sql.DriverManager.registerDriver( new  Driver());     
  1.             java.sql.DriverManager.registerDriver(
new  Driver());              }  catch  (SQLException E) {     
  1.         } 
catch  (SQLException E) {                   throw   new  RuntimeException( "Can't register driver!" );     
  1.             
throw   new  RuntimeException( "Can't register driver!" );              }     
  1.         }     
    }     
  1.     }     
     public  Driver()  throws  SQLException {     
  1.     
public  Driver()  throws  SQLException {          }     
  1.     }     
}    
  1. }    
public   class  Driver  extends  NonRegisteringDriver  implements  java.sql.Driver {  
  1. public
  class  Driver  extends  NonRegisteringDriver  implements  java.sql.Driver {     static  {  
  1.  
static  {      try  {  
  1.   
try  {      java.sql.DriverManager.registerDriver( new  Driver());  
  1.    java.sql.DriverManager.registerDriver(
new  Driver());     }  catch  (SQLException E) {  
  1.   } 
catch  (SQLException E) {       throw   new  RuntimeException( "Can't register driver!" );  
  1.    
throw   new  RuntimeException( "Can't register driver!" );     }  
  1.   }  
 }  
  1.  }  
  public  Driver()  throws  SQLException {  
  1.  
public  Driver()  throws  SQLException {    }  
  1.  }  
  1. Java代码   
  1. Java代码   
    public   static   synchronized   void  registerDriver(java.sql.Driver driver)     
  1.    
public   static   synchronized   void  registerDriver(java.sql.Driver driver)      throws  SQLException {     
  1. throws
 SQLException {      if  (!initialized) {     
  1. if
 (!initialized) {          initialize();     
  1.     initialize();     
}            
  1. }            
DriverInfo di =  new  DriverInfo();      
  1. DriverInfo di = 
new  DriverInfo();          /*把driver的信息封装一下,组成一个DriverInfo对象*/     
  1.   
/*把driver的信息封装一下,组成一个DriverInfo对象*/      di.driver = driver;     
  1. di.driver = driver;     
di.driverClass = driver.getClass();     
  1. di.driverClass = driver.getClass();     
di.driverClassName = di.driverClass.getName();       
  1. di.driverClassName = di.driverClass.getName();       
writeDrivers.addElement(di);      
  1. writeDrivers.addElement(di);      
println( "registerDriver: "  + di);      
  1. println(
"registerDriver: "  + di);       readDrivers = (java.util.Vector) writeDrivers.clone();      
  1. readDrivers = (java.util.Vector) writeDrivers.clone();      
   }    
  1.    }    
     public   static   synchronized   void  registerDriver(java.sql.Driver driver)  
  1.     
public   static   synchronized   void  registerDriver(java.sql.Driver driver)     throws  SQLException {  
  1.  
throws  SQLException {     if  (!initialized) {  
  1.  
if  (!initialized) {        initialize();  
  1.      initialize();  
 }        
  1.  }        
 DriverInfo di =  new  DriverInfo();  
  1.  DriverInfo di = 
new  DriverInfo();       /*把driver的信息封装一下,组成一个DriverInfo对象*/  
  1.    
/*把driver的信息封装一下,组成一个DriverInfo对象*/    di.driver = driver;  
  1.  di.driver = driver;  
 di.driverClass = driver.getClass();  
  1.  di.driverClass = driver.getClass();  
 di.driverClassName = di.driverClass.getName();  
  1.  di.driverClassName = di.driverClass.getName();  
 
  1.  
 writeDrivers.addElement(di);   
  1.  writeDrivers.addElement(di);   
 println( "registerDriver: "  + di);  
  1.  println(
"registerDriver: "  + di);    readDrivers = (java.util.Vector) writeDrivers.clone();  
  1.  readDrivers = (java.util.Vector) writeDrivers.clone();  
 
  1.  
    } 
  1.     } 
至于驱动的集合writeDrivers和readDrivers,很有趣的是,无论是registerDriver还是deregisterDriver,都是先对writeDrivers中的数据进行添加或者删除,然后再把writeDrivers中的驱动都拷贝到readDrivers中,但每次取出driver却从来不从writeDrivers中取,都是通过readDrivers来获得。我认为可以这样理解,writeDrivers只负责注册driver与注销driver,而readDrivers只负责提供可用的driver,只有当writeDrivers中准备好了驱动,这些驱动才是可以使用的,所以才能被copy至readDrivers中以备使用。这样一来,对内的注册注销与对外的提供使用就分开来了。
  1. Java代码   
  1. Java代码   
    private   static  Connection getConnection(     
  1.    
private   static  Connection getConnection(      String url, java.util.Properties info, ClassLoader callerCL)  throws  SQLException {     
  1. String url, java.util.Properties info, ClassLoader callerCL) 
throws  SQLException {      java.util.Vector drivers =  null ;     
  1. java.util.Vector drivers = 
null ;      ...     
  1. ...     
if  (!initialized) {     
  1. if
 (!initialized) {          initialize();     
  1.     initialize();     
}     
  1. }     
    /*取得连接使用的driver从readDrivers中取*/     
  1.    
/*取得连接使用的driver从readDrivers中取*/      synchronized  (DriverManager. class ){      
  1. synchronized
 (DriverManager. class ){           drivers = readDrivers;       
  1.     drivers = readDrivers;       
       }     
  1.        }     
    
  1.     
SQLException reason =  null ;     
  1. SQLException reason = 
null ;      for  ( int  i =  0 ; i < drivers.size(); i++) {     
  1. for
 ( int  i =  0 ; i < drivers.size(); i++) {          DriverInfo di = (DriverInfo)drivers.elementAt(i);     
  1.     DriverInfo di = (DriverInfo)drivers.elementAt(i);     
          
  1.           
     if  ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {     
  1.     
if  ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {           continue ;     
  1.     
continue ;          }     
  1.     }     
     try  {     
  1.     
try  {              /*找到可供使用的驱动,连接数据库server*/     
  1.        
/*找到可供使用的驱动,连接数据库server*/          Connection result = di.driver.connect(url, info);     
  1.     Connection result = di.driver.connect(url, info);     
     if  (result !=  null ) {     
  1.     
if  (result !=  null ) {               return  (result);     
  1.         
return  (result);          }     
  1.     }     
    }  catch  (SQLException ex) {     
  1.     } 
catch  (SQLException ex) {           if  (reason ==  null ) {     
  1.     
if  (reason ==  null ) {              reason = ex;     
  1.         reason = ex;     
    }     
  1.     }     
    }     
  1.     }     
}     
  1. }     
        
  1.         
if  (reason !=  null )    {     
  1. if
 (reason !=  null )    {          println( "getConnection failed: "  + reason);     
  1.     println(
"getConnection failed: "  + reason);           throw  reason;     
  1.     
throw  reason;      }         
  1. }         
throw   new  SQLException( "No suitable driver found for " + url,  "08001" );     
  1. throw
  new  SQLException( "No suitable driver found for " + url,  "08001" );         }    
  1.    }    
     private   static  Connection getConnection(  
  1.     
private   static  Connection getConnection(    String url, java.util.Properties info, ClassLoader callerCL)  throws  SQLException {  
  1.  String url, java.util.Properties info, ClassLoader callerCL) 
throws  SQLException {    java.util.Vector drivers =  null ;  
  1.  java.util.Vector drivers = 
null ;    ...  
  1.  ...  
  if  (!initialized) {  
  1.  
if  (!initialized) {        initialize();  
  1.      initialize();  
 }  
  1.  }  
     /*取得连接使用的driver从readDrivers中取*/  
  1.     
/*取得连接使用的driver从readDrivers中取*/     synchronized  (DriverManager. class ){   
  1.  
synchronized  (DriverManager. class ){         drivers = readDrivers;    
  1.      drivers = readDrivers;    
        }  
  1.         }  
 
  1.  
 SQLException reason =  null ;  
  1.  SQLException reason = 
null ;     for  ( int  i =  0 ; i < drivers.size(); i++) {  
  1.  
for  ( int  i =  0 ; i < drivers.size(); i++) {        DriverInfo di = (DriverInfo)drivers.elementAt(i);  
  1.      DriverInfo di = (DriverInfo)drivers.elementAt(i);  
        
  1.         
      if  ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {  
  1.      
if  ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {      continue ;  
  1.   
continue ;        }  
  1.      }  
      try  {  
  1.      
try  {            /*找到可供使用的驱动,连接数据库server*/  
  1.         
/*找到可供使用的驱动,连接数据库server*/     Connection result = di.driver.connect(url, info);  
  1.   Connection result = di.driver.connect(url, info);  
   if  (result !=  null ) {  
  1.   
if  (result !=  null ) {          return  (result);  
  1.       
return  (result);     }  
  1.   }  
     }  catch  (SQLException ex) {  
  1.      } 
catch  (SQLException ex) {      if  (reason ==  null ) {  
  1.   
if  (reason ==  null ) {         reason = ex;  
  1.       reason = ex;  
  }  
  1.   }  
     }  
  1.      }  
 }  
  1.  }  
      
  1.       
  if  (reason !=  null )    {  
  1.  
if  (reason !=  null )    {        println( "getConnection failed: "  + reason);  
  1.      println(
"getConnection failed: "  + reason);         throw  reason;  
  1.      
throw  reason;    }      
  1.  }      
  throw   new  SQLException( "No suitable driver found for " + url,  "08001" );  
  1.  
throw   new  SQLException( "No suitable driver found for " + url,  "08001" );       } 
  1.     } 
  1. Java代码   
  1. Java代码   
public  java.sql.Connection connect(String url, Properties info)     
  1. public
 java.sql.Connection connect(String url, Properties info)               throws  SQLException {     
  1.         
throws  SQLException {          Properties props =  null ;     
  1.     Properties props = 
null ;           if  ((props = parseURL(url, info)) ==  null ) {     
  1.     
if  ((props = parseURL(url, info)) ==  null ) {               return   null ;     
  1.         
return   null ;          }     
  1.     }     
    
  1.     
     try  {     
  1.     
try  {              Connection newConn =  new  com.mysql.jdbc.Connection(host(props),     
  1.         Connection newConn = 
new  com.mysql.jdbc.Connection(host(props),                      port(props), props, database(props), url);     
  1.                 port(props), props, database(props), url);     
    
  1.     
         return  newConn;     
  1.         
return  newConn;          }  catch  (SQLException sqlEx) {     
  1.     } 
catch  (SQLException sqlEx) {               throw  sqlEx;     
  1.         
throw  sqlEx;          }  catch  (Exception ex) {     
  1.     } 
catch  (Exception ex) {               throw  SQLError.createSQLException(...);     
  1.         
throw  SQLError.createSQLException(...);          }     
  1.     }     
}    
  1. }    
  public  java.sql.Connection connect(String url, Properties info)  
  1.  
public  java.sql.Connection connect(String url, Properties info)       throws  SQLException {  
  1.    
throws  SQLException {     Properties props =  null ;  
  1.   Properties props = 
null ;      if  ((props = parseURL(url, info)) ==  null ) {  
  1.   
if  ((props = parseURL(url, info)) ==  null ) {       return   null ;  
  1.    
return   null ;     }  
  1.   }  
 
  1.  
   try  {  
  1.   
try  {      Connection newConn =  new  com.mysql.jdbc.Connection(host(props),  
  1.    Connection newConn = 
new  com.mysql.jdbc.Connection(host(props),        port(props), props, database(props), url);  
  1.      port(props), props, database(props), url);  
 
  1.  
    return  newConn;  
  1.    
return  newConn;     }  catch  (SQLException sqlEx) {  
  1.   } 
catch  (SQLException sqlEx) {       throw  sqlEx;  
  1.    
throw  sqlEx;     }  catch  (Exception ex) {  
  1.   } 
catch  (Exception ex) {       throw  SQLError.createSQLException(...);  
  1.    
throw  SQLError.createSQLException(...);     }  
  1.   }  
 } 
  1.  } 
2、PreparedStatement stmt = conn.prepareStatement(sql);使用得到的connection创建一个Statement。Statement有许多种,我们常用的就是PreparedStatement,用于执行预编译好的SQL语句,CallableStatement用于调用数据库的存储过程。它们的继承关系如下图所示。 Java代码  public java.sql.PreparedStatement prepareStatement(String sql)    throws SQLException {    return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,    java.sql.ResultSet.CONCUR_READ_ONLY);    }   
public java.sql.PreparedStatement prepareStatement(String sql,   
int resultSetType, int resultSetConcurrency) throws SQLException;  
public java.sql.PreparedStatement prepareStatement(String sql)
throws SQLException {
return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_READ_ONLY);
} public java.sql.PreparedStatement prepareStatement(String sql,    int resultSetType, int resultSetConcurrency) throws SQLException;   public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); } int resultSetType, int resultSetConcurrency) throws SQLException; ResultSet中的参数常量主要有以下几种: 
  1. TYPE_FORWARD_ONLY: ResultSet的游标只能向前移动。   
  1. TYPE_FORWARD_ONLY: ResultSet的游标只能向前移动。   
TYPE_SCROLL_INSENSITIVE:ResultSet的游标可以滚动,但对于resultSet下的数据改变不敏感。   
  1. TYPE_SCROLL_INSENSITIVE:ResultSet的游标可以滚动,但对于resultSet下的数据改变不敏感。   
TYPE_SCROLL_SENSITIVE:ResultSet的游标可以滚动,但对于resultSet下的数据改变是敏感的。   
  1. TYPE_SCROLL_SENSITIVE:ResultSet的游标可以滚动,但对于resultSet下的数据改变是敏感的。   
CONCUR_READ_ONLY:不可以更新的ResultSet的并发模式。   
  1. CONCUR_READ_ONLY:不可以更新的ResultSet的并发模式。   
CONCUR_UPDATABLE:可以更新的ResultSet的并发模式。   
  1. CONCUR_UPDATABLE:可以更新的ResultSet的并发模式。   
FETCH_FORWARD:按正向(即从第一个到最后一个)处理结果集中的行。   
  1. FETCH_FORWARD:按正向(即从第一个到最后一个)处理结果集中的行。   
FETCH_REVERSE:按反向(即从最后一个到第一个)处理结果集中的行处理。   
  1. FETCH_REVERSE:按反向(即从最后一个到第一个)处理结果集中的行处理。   
FETCH_UNKNOWN:结果集中的行的处理顺序未知。   
  1. FETCH_UNKNOWN:结果集中的行的处理顺序未知。   
CLOSE_CURSORS_AT_COMMIT:调用Connection.commit方法时应该关闭 ResultSet 对   
  1. CLOSE_CURSORS_AT_COMMIT:调用Connection.commit方法时应该关闭 ResultSet 对   
HOLD_CURSORS_OVER_COMMIT:调用Connection.commit方法时不应关闭ResultSet对象。 
  1. HOLD_CURSORS_OVER_COMMIT:调用Connection.commit方法时不应关闭ResultSet对象。 
3、stmt.setInt(1, new Integer(1)); 4、ResultSet rs = stmt.executeQuery(); 一切准备就绪,开始执行查询罗!
  1. Java代码   
  1. Java代码   
     protected  Buffer fillSendPacket( byte [][] batchedParameterStrings,     
  1.     
protected  Buffer fillSendPacket( byte [][] batchedParameterStrings,                  InputStream[] batchedParameterStreams,  boolean [] batchedIsStream,     
  1.             InputStream[] batchedParameterStreams, 
boolean [] batchedIsStream,                   int [] batchedStreamLengths)  throws  SQLException {     
  1.             
int [] batchedStreamLengths)  throws  SQLException {               // 从connection的IO中得到发送数据包,首先清空其中的数据     
  1.         
// 从connection的IO中得到发送数据包,首先清空其中的数据              Buffer sendPacket =  this .connection.getIO().getSharedSendPacket();     
  1.         Buffer sendPacket = 
this .connection.getIO().getSharedSendPacket();              sendPacket.clear();     
  1.         sendPacket.clear();     
             
  1.              
         /* 数据包的第一位为一个操作标识符(MysqlDefs.QUERY),表示驱动向服务器发送的连接的操作信号,包括有QUERY, PING, RELOAD, SHUTDOWN, PROCESS_INFO, QUIT, SLEEP等等,这个操作信号并不是针对sql语句操作而言的CRUD操作,从提供的几种参数来看,这个操作是针对服务器的一个操作。一般而言,使用到的都是MysqlDefs.QUERY,表示发送的是要执行sql语句的操作。    
  1.         
/* 数据包的第一位为一个操作标识符(MysqlDefs.QUERY),表示驱动向服务器发送的连接的操作信号,包括有QUERY, PING, RELOAD, SHUTDOWN, PROCESS_INFO, QUIT, SLEEP等等,这个操作信号并不是针对sql语句操作而言的CRUD操作,从提供的几种参数来看,这个操作是针对服务器的一个操作。一般而言,使用到的都是MysqlDefs.QUERY,表示发送的是要执行sql语句的操作。     */    
  1. */
             sendPacket.writeByte(( byte ) MysqlDefs.QUERY);     
  1.         sendPacket.writeByte((
byte ) MysqlDefs.QUERY);          
  1.     
         boolean  useStreamLengths =  this .connection     
  1.         
boolean  useStreamLengths =  this .connection                      .getUseStreamLengthsInPrepStmts();     
  1.                 .getUseStreamLengthsInPrepStmts();     
    
  1.     
         int  ensurePacketSize =  0 ;     
  1.         
int  ensurePacketSize =  0 ;               for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {     
  1.         
for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {                   if  (batchedIsStream[i] && useStreamLengths) {     
  1.             
if  (batchedIsStream[i] && useStreamLengths) {                      ensurePacketSize += batchedStreamLengths[i];     
  1.                 ensurePacketSize += batchedStreamLengths[i];     
            }     
  1.             }     
        }     
  1.         }     
    
  1.     
         /* 判断这个sendPacket的byte buffer够不够大,不够大的话,按照1.25倍来扩充buffer    
  1.         
/* 判断这个sendPacket的byte buffer够不够大,不够大的话,按照1.25倍来扩充buffer     */    
  1. */
              if  (ensurePacketSize !=  0 ) {     
  1.         
if  (ensurePacketSize !=  0 ) {                  sendPacket.ensureCapacity(ensurePacketSize);     
  1.             sendPacket.ensureCapacity(ensurePacketSize);     
        }     
  1.         }     
    
  1.     
         /* 遍历所有的参数。在prepareStatement阶段的new ParseInfo()中,驱动曾经对sql语句进行过分割,如果含有以问号标识的参数占位符的话,就记录下这个占位符的位置,依据这个位置把sql分割成多段,放在了一个名为staticSql的字符串中。这里就开始把sql语句进行拼装,把staticSql和parameterValues进行组合,放在操作符的后面。    
  1.         
/* 遍历所有的参数。在prepareStatement阶段的new ParseInfo()中,驱动曾经对sql语句进行过分割,如果含有以问号标识的参数占位符的话,就记录下这个占位符的位置,依据这个位置把sql分割成多段,放在了一个名为staticSql的字符串中。这里就开始把sql语句进行拼装,把staticSql和parameterValues进行组合,放在操作符的后面。     */    
  1. */
              for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {     
  1.         
for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {          
  1.     
         /* batchedParameterStrings就是parameterValues,batchedParameterStreams就是parameterStreams,如果两者都为null,说明在参数的设置过程中出了错,立即抛出错误。    
  1.         
/* batchedParameterStrings就是parameterValues,batchedParameterStreams就是parameterStreams,如果两者都为null,说明在参数的设置过程中出了错,立即抛出错误。     */    
  1. */
                  if  ((batchedParameterStrings[i] ==  null )     
  1.             
if  ((batchedParameterStrings[i] ==  null )                          && (batchedParameterStreams[i] ==  null )) {     
  1.                     && (batchedParameterStreams[i] == 
null )) {                       throw  SQLError.createSQLException(Messages     
  1.                 
throw  SQLError.createSQLException(Messages                              .getString( "PreparedStatement.40" //$NON-NLS-1$     
  1.                         .getString(
"PreparedStatement.40" //$NON-NLS-1$                              + (i +  1 ), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);     
  1.                         + (i + 
1 ), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);                  }     
  1.             }     
    
  1.     
         /*在sendPacket中加入staticSql数组中的元素,就是分割出来的没有”?”的sql语句,并把字符串转换成byte。    
  1.         
/*在sendPacket中加入staticSql数组中的元素,就是分割出来的没有”?”的sql语句,并把字符串转换成byte。     */    
  1. */
                 sendPacket.writeBytesNoNull( this .staticSqlStrings[i]);     
  1.             sendPacket.writeBytesNoNull(
this .staticSqlStrings[i]);          
  1.     
         /* batchedIsStream就是isStream,如果参数是通过CallableStatement传递进来的话,batchedIsStream[i]==true,就用batchedParameterStreams中的值填充到问号占的参数位置中去。    
  1.         
/* batchedIsStream就是isStream,如果参数是通过CallableStatement传递进来的话,batchedIsStream[i]==true,就用batchedParameterStreams中的值填充到问号占的参数位置中去。     */    
  1. */
                  if  (batchedIsStream[i]) {     
  1.             
if  (batchedIsStream[i]) {                      streamToBytes(sendPacket, batchedParameterStreams[i],  true ,     
  1.                 streamToBytes(sendPacket, batchedParameterStreams[i], 
true ,                              batchedStreamLengths[i], useStreamLengths);     
  1.                         batchedStreamLengths[i], useStreamLengths);     
            }  else  {     
  1.             } 
else  {                       
  1.                  
         /*否则的话,就用batchedParameterStrings,也就是parameterValues来填充参数位置。在循环中,这个操作是跟在staticSql后面的,因此就把第i个参数加到了第i个staticSql段中。参考前面的staticSql的例子,发现当循环结束的时候,原始sql语句最后一个”?”之前的sql语句就拼成了正确的语句了。    
  1.         
/*否则的话,就用batchedParameterStrings,也就是parameterValues来填充参数位置。在循环中,这个操作是跟在staticSql后面的,因此就把第i个参数加到了第i个staticSql段中。参考前面的staticSql的例子,发现当循环结束的时候,原始sql语句最后一个”?”之前的sql语句就拼成了正确的语句了。     */    
  1. */
         sendPacket.writeBytesNoNull(batchedParameterStrings[i]);     
  1.     sendPacket.writeBytesNoNull(batchedParameterStrings[i]);     
            }     
  1.             }     
        }     
  1.         }     
    
  1.     
         /*由于在原始的包含问号的sql语句中,在最后一个”?”后面可能还有order by等语句,因此staticSql数组中的元素个数一定比参数的个数多1,所以这里把staticSqlString中的最后一段sql语句放入sendPacket中。    
  1.         
/*由于在原始的包含问号的sql语句中,在最后一个”?”后面可能还有order by等语句,因此staticSql数组中的元素个数一定比参数的个数多1,所以这里把staticSqlString中的最后一段sql语句放入sendPacket中。     */    
  1. */
             sendPacket     
  1.         sendPacket     
                .writeBytesNoNull( this .staticSqlStrings[batchedParameterStrings.length]);     
  1.                 .writeBytesNoNull(
this .staticSqlStrings[batchedParameterStrings.length]);          
  1.     
         return  sendPacket;     
  1.         
return  sendPacket;          }    
  1.     }    
  protected  Buffer fillSendPacket( byte [][] batchedParameterStrings,  
  1.  
protected  Buffer fillSendPacket( byte [][] batchedParameterStrings,      InputStream[] batchedParameterStreams,  boolean [] batchedIsStream,  
  1.    InputStream[] batchedParameterStreams, 
boolean [] batchedIsStream,       int [] batchedStreamLengths)  throws  SQLException {  
  1.    
int [] batchedStreamLengths)  throws  SQLException {            // 从connection的IO中得到发送数据包,首先清空其中的数据  
  1.         
// 从connection的IO中得到发送数据包,首先清空其中的数据     Buffer sendPacket =  this .connection.getIO().getSharedSendPacket();  
  1.   Buffer sendPacket = 
this .connection.getIO().getSharedSendPacket();     sendPacket.clear();  
  1.   sendPacket.clear();  
          
  1.           
         /* 数据包的第一位为一个操作标识符(MysqlDefs.QUERY),表示驱动向服务器发送的连接的操作信号,包括有QUERY, PING, RELOAD, SHUTDOWN, PROCESS_INFO, QUIT, SLEEP等等,这个操作信号并不是针对sql语句操作而言的CRUD操作,从提供的几种参数来看,这个操作是针对服务器的一个操作。一般而言,使用到的都是MysqlDefs.QUERY,表示发送的是要执行sql语句的操作。  
  1.         
/* 数据包的第一位为一个操作标识符(MysqlDefs.QUERY),表示驱动向服务器发送的连接的操作信号,包括有QUERY, PING, RELOAD, SHUTDOWN, PROCESS_INFO, QUIT, SLEEP等等,这个操作信号并不是针对sql语句操作而言的CRUD操作,从提供的几种参数来看,这个操作是针对服务器的一个操作。一般而言,使用到的都是MysqlDefs.QUERY,表示发送的是要执行sql语句的操作。   */ 
  1. */
    sendPacket.writeByte(( byte ) MysqlDefs.QUERY);  
  1.   sendPacket.writeByte((
byte ) MysqlDefs.QUERY);    
  1.  
   boolean  useStreamLengths =  this .connection  
  1.   
boolean  useStreamLengths =  this .connection       .getUseStreamLengthsInPrepStmts();  
  1.     .getUseStreamLengthsInPrepStmts();  
 
  1.  
   int  ensurePacketSize =  0 ;  
  1.   
int  ensurePacketSize =  0 ;      for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {  
  1.   
for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {       if  (batchedIsStream[i] && useStreamLengths) {  
  1.    
if  (batchedIsStream[i] && useStreamLengths) {       ensurePacketSize += batchedStreamLengths[i];  
  1.     ensurePacketSize += batchedStreamLengths[i];  
   }  
  1.    }  
  }  
  1.   }  
 
  1.  
         /* 判断这个sendPacket的byte buffer够不够大,不够大的话,按照1.25倍来扩充buffer  
  1.         
/* 判断这个sendPacket的byte buffer够不够大,不够大的话,按照1.25倍来扩充buffer   */ 
  1. */
     if  (ensurePacketSize !=  0 ) {  
  1.   
if  (ensurePacketSize !=  0 ) {      sendPacket.ensureCapacity(ensurePacketSize);  
  1.    sendPacket.ensureCapacity(ensurePacketSize);  
  }  
  1.   }  
 
  1.  
         /* 遍历所有的参数。在prepareStatement阶段的new ParseInfo()中,驱动曾经对sql语句进行过分割,如果含有以问号标识的参数占位符的话,就记录下这个占位符的位置,依据这个位置把sql分割成多段,放在了一个名为staticSql的字符串中。这里就开始把sql语句进行拼装,把staticSql和parameterValues进行组合,放在操作符的后面。  
  1.         
/* 遍历所有的参数。在prepareStatement阶段的new ParseInfo()中,驱动曾经对sql语句进行过分割,如果含有以问号标识的参数占位符的话,就记录下这个占位符的位置,依据这个位置把sql分割成多段,放在了一个名为staticSql的字符串中。这里就开始把sql语句进行拼装,把staticSql和parameterValues进行组合,放在操作符的后面。   */ 
  1. */
     for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {  
  1.   
for  ( int  i =  0 ; i < batchedParameterStrings.length; i++) {    
  1.  
         /* batchedParameterStrings就是parameterValues,batchedParameterStreams就是parameterStreams,如果两者都为null,说明在参数的设置过程中出了错,立即抛出错误。  
  1.         
/* batchedParameterStrings就是parameterValues,batchedParameterStreams就是parameterStreams,如果两者都为null,说明在参数的设置过程中出了错,立即抛出错误。   */ 
  1. */
      if  ((batchedParameterStrings[i] ==  null )  
  1.    
if  ((batchedParameterStrings[i] ==  null )        && (batchedParameterStreams[i] ==  null )) {  
  1.      && (batchedParameterStreams[i] == 
null )) {        throw  SQLError.createSQLException(Messages  
  1.     
throw  SQLError.createSQLException(Messages         .getString( "PreparedStatement.40" //$NON-NLS-1$  
  1.       .getString(
"PreparedStatement.40" //$NON-NLS-1$         + (i +  1 ), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);  
  1.       + (i + 
1 ), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);      }  
  1.    }  
 
  1.  
         /*在sendPacket中加入staticSql数组中的元素,就是分割出来的没有”?”的sql语句,并把字符串转换成byte。  
  1.         
/*在sendPacket中加入staticSql数组中的元素,就是分割出来的没有”?”的sql语句,并把字符串转换成byte。   */ 
  1. */
     sendPacket.writeBytesNoNull( this .staticSqlStrings[i]);  
  1.    sendPacket.writeBytesNoNull(
this .staticSqlStrings[i]);    
  1.  
         /* batchedIsStream就是isStream,如果参数是通过CallableStatement传递进来的话,batchedIsStream[i]==true,就用batchedParameterStreams中的值填充到问号占的参数位置中去。  
  1.         
/* batchedIsStream就是isStream,如果参数是通过CallableStatement传递进来的话,batchedIsStream[i]==true,就用batchedParameterStreams中的值填充到问号占的参数位置中去。   */ 
  1. */
      if  (batchedIsStream[i]) {  
  1.    
if  (batchedIsStream[i]) {       streamToBytes(sendPacket, batchedParameterStreams[i],  true ,  
  1.     streamToBytes(sendPacket, batchedParameterStreams[i], 
true ,         batchedStreamLengths[i], useStreamLengths);  
  1.       batchedStreamLengths[i], useStreamLengths);  
   }  else  {  
  1.    } 
else  {        
  1.      
         /*否则的话,就用batchedParameterStrings,也就是parameterValues来填充参数位置。在循环中,这个操作是跟在staticSql后面的,因此就把第i个参数加到了第i个staticSql段中。参考前面的staticSql的例子,发现当循环结束的时候,原始sql语句最后一个”?”之前的sql语句就拼成了正确的语句了。  
  1.         
/*否则的话,就用batchedParameterStrings,也就是parameterValues来填充参数位置。在循环中,这个操作是跟在staticSql后面的,因此就把第i个参数加到了第i个staticSql段中。参考前面的staticSql的例子,发现当循环结束的时候,原始sql语句最后一个”?”之前的sql语句就拼成了正确的语句了。   */ 
  1. */
   sendPacket.writeBytesNoNull(batchedParameterStrings[i]);  
  1.  sendPacket.writeBytesNoNull(batchedParameterStrings[i]);  
   }  
  1.    }  
  }  
  1.   }  
 
  1.  
         /*由于在原始的包含问号的sql语句中,在最后一个”?”后面可能还有order by等语句,因此staticSql数组中的元素个数一定比参数的个数多1,所以这里把staticSqlString中的最后一段sql语句放入sendPacket中。  
  1.         
/*由于在原始的包含问号的sql语句中,在最后一个”?”后面可能还有order by等语句,因此staticSql数组中的元素个数一定比参数的个数多1,所以这里把staticSqlString中的最后一段sql语句放入sendPacket中。   */ 
  1. */
    sendPacket  
  1.   sendPacket  
    .writeBytesNoNull( this .staticSqlStrings[batchedParameterStrings.length]);  
  1.     .writeBytesNoNull(
this .staticSqlStrings[batchedParameterStrings.length]);    
  1.  
   return  sendPacket;  
  1.   
return  sendPacket;    } 
  1.  } 
但是并不是说PreparedStatement除了给我们带来高效率就没有其他作用了,它还有非常好的其他作用:  i. 极大的提高了sql语句的安全性,可以防止sql注入  ii. 代码结构清晰,易于理解,便于维护。 i. 设置当前数据库连接,并调用connection的execSQL来执行查询.然后继续把要发送的查询包,就是之间组装完毕的sendPacket传递进入MysqlIO的sqlQueryDirect()。  ii. 接下来就要往server端发送我们的查询指令啦(sendCommand),说到发送数据,不禁要问,如果这个待发送的数据包超级大,难道每次都是一次性的发送吗?当然不是,如果数据包超过规定的最大值的话,就会把它分割一下,分成几个不超过最大值的数据包来发送。  所以可以肯定,在分割的过程中,除了最后一个数据包,其他数据包的大小都是一样的。那就这样的数据包直接切割了进行发送的话,假如现在被分成了三个数据包,发送给mysql server,服务器怎么知道那个包是第一个呢,它读数据该从什么地方开始读呢,这都是问题,所以,我们要给每个数据包的前面加上一点属性标志,这个标志一共占了4个byte。从代码①处开始就是头标识位的设置。第一位表示数据包的开始位置,就是数据存放的起始位置,一般都设置为0,就是从第一个位置开始。第二和第三个字节标识了这个数据包的大小,注意的是,这个大小是出去标识的4个字节的大小,对于非最后一个数据包来说,这个大小都是一样的,就是splitSize,也就是maxThreeBytes,它的值是255 * 255 * 255。  最后一个字节中存放的就是数据包的编号了,从0开始递增。  在标识位设置完毕之后,就可以把255 * 255 * 255大小的数据从我们准备好的待发送数据包中copy出来了,注意,前4位已经是标识位了,所以应该从第五个位置开始copy数据。  在数据包都装配完毕之后,就可以往socket的outputSteam中发送数据了。接下来的事情,就是由mysql服务器接收数据并解析,执行查询了。
  1. Java代码   
  1. Java代码   
while  (len >=  this .maxThreeBytes) {     
  1. while
 (len >=  this .maxThreeBytes) {           this .packetSequence++;     
  1.     
this .packetSequence++;           /*设置包的开始位置*/     
  1.     
/*设置包的开始位置*/         headerPacket.setPosition( 0 );      
  1.    headerPacket.setPosition(
0 );            /*设置这个数据包的大小,splitSize=255 * 255 * 255*/     
  1.     
/*设置这个数据包的大小,splitSize=255 * 255 * 255*/          headerPacket.writeLongInt(splitSize);      
  1.     headerPacket.writeLongInt(splitSize);      
     /*设置数据包的序号*/     
  1.     
/*设置数据包的序号*/          headerPacket.writeByte( this .packetSequence);      
  1.     headerPacket.writeByte(
this .packetSequence);            /*origPacketBytes就是sendPacket,所以这里就是把sendPacket中大小为255 * 255 * 255的数据放入headPacket中,headerPacketBytes是headPacket的byte buffer*/     
  1.     
/*origPacketBytes就是sendPacket,所以这里就是把sendPacket中大小为255 * 255 * 255的数据放入headPacket中,headerPacketBytes是headPacket的byte buffer*/          System.arraycopy(origPacketBytes, originalPacketPos,     
  1.     System.arraycopy(origPacketBytes, originalPacketPos,     
        headerPacketBytes,  4 , splitSize);     
  1.         headerPacketBytes, 
4 , splitSize);          
  1.     
     int  packetLen = splitSize + HEADER_LENGTH;     
  1.     
int  packetLen = splitSize + HEADER_LENGTH;           if  (! this .useCompression) {     
  1.     
if  (! this .useCompression) {               this .mysqlOutput.write(headerPacketBytes,  0 ,     
  1.         
this .mysqlOutput.write(headerPacketBytes,  0 ,                  splitSize + HEADER_LENGTH);     
  1.             splitSize + HEADER_LENGTH);     
         this .mysqlOutput.flush();     
  1.         
this .mysqlOutput.flush();          }  else  {     
  1.     } 
else  {              Buffer packetToSend;     
  1.         Buffer packetToSend;     
    
  1.     
        headerPacket.setPosition( 0 );     
  1.         headerPacket.setPosition(
0 );              packetToSend = compressPacket(headerPacket, HEADER_LENGTH,     
  1.         packetToSend = compressPacket(headerPacket, HEADER_LENGTH,     
                splitSize, HEADER_LENGTH);     
  1.                 splitSize, HEADER_LENGTH);     
        packetLen = packetToSend.getPosition();     
  1.         packetLen = packetToSend.getPosition();     
         /*往IO的output stream中写数据*/     
  1.         
/*往IO的output stream中写数据*/               this .mysqlOutput.write(packetToSend.getByteBuffer(),  0 ,     
  1.         
this .mysqlOutput.write(packetToSend.getByteBuffer(),  0 ,                  packetLen);     
  1.             packetLen);     
         this .mysqlOutput.flush();     
  1.         
this .mysqlOutput.flush();          }     
  1.     }     
    
  1.     
    originalPacketPos += splitSize;     
  1.     originalPacketPos += splitSize;     
    len -= splitSize;     
  1.     len -= splitSize;     
}    
  1. }    
             while  (len >=  this .maxThreeBytes) {  
  1.             
while  (len >=  this .maxThreeBytes) {                    this .packetSequence++;  
  1.                 
this .packetSequence++;                    /*设置包的开始位置*/  
  1.                 
/*设置包的开始位置*/       ①          headerPacket.setPosition( 0 );   
  1.     ①          headerPacket.setPosition(
0 );                     /*设置这个数据包的大小,splitSize=255 * 255 * 255*/  
  1.                 
/*设置这个数据包的大小,splitSize=255 * 255 * 255*/                   headerPacket.writeLongInt(splitSize);   
  1.                 headerPacket.writeLongInt(splitSize);   
                 /*设置数据包的序号*/  
  1.                 
/*设置数据包的序号*/                   headerPacket.writeByte( this .packetSequence);   
  1.                 headerPacket.writeByte(
this .packetSequence);                     /*origPacketBytes就是sendPacket,所以这里就是把sendPacket中大小为255 * 255 * 255的数据放入headPacket中,headerPacketBytes是headPacket的byte buffer*/  
  1.                 
/*origPacketBytes就是sendPacket,所以这里就是把sendPacket中大小为255 * 255 * 255的数据放入headPacket中,headerPacketBytes是headPacket的byte buffer*/                   System.arraycopy(origPacketBytes, originalPacketPos,  
  1.                 System.arraycopy(origPacketBytes, originalPacketPos,  
                    headerPacketBytes,  4 , splitSize);  
  1.                     headerPacketBytes, 
4 , splitSize);    
  1.  
                 int  packetLen = splitSize + HEADER_LENGTH;  
  1.                 
int  packetLen = splitSize + HEADER_LENGTH;                    if  (! this .useCompression) {  
  1.                 
if  (! this .useCompression) {                        this .mysqlOutput.write(headerPacketBytes,  0 ,  
  1.                     
this .mysqlOutput.write(headerPacketBytes,  0 ,                           splitSize + HEADER_LENGTH);  
  1.                         splitSize + HEADER_LENGTH);  
                     this .mysqlOutput.flush();  
  1.                     
this .mysqlOutput.flush();                   }  else  {  
  1.                 } 
else  {                       Buffer packetToSend;  
  1.                     Buffer packetToSend;  
 
  1.  
                    headerPacket.setPosition( 0 );  
  1.                     headerPacket.setPosition(
0 );                       packetToSend = compressPacket(headerPacket, HEADER_LENGTH,  
  1.                     packetToSend = compressPacket(headerPacket, HEADER_LENGTH,  
                            splitSize, HEADER_LENGTH);  
  1.                             splitSize, HEADER_LENGTH);  
                    packetLen = packetToSend.getPosition();  
  1.                     packetLen = packetToSend.getPosition();  
                     /*往IO的output stream中写数据*/  
  1.                     
/*往IO的output stream中写数据*/                        this .mysqlOutput.write(packetToSend.getByteBuffer(),  0 ,  
  1.                     
this .mysqlOutput.write(packetToSend.getByteBuffer(),  0 ,                           packetLen);  
  1.                         packetLen);  
                     this .mysqlOutput.flush();  
  1.                     
this .mysqlOutput.flush();                   }  
  1.                 }  
 
  1.  
                originalPacketPos += splitSize;  
  1.                 originalPacketPos += splitSize;  
                len -= splitSize;  
  1.                 len -= splitSize;  
            } 
  1.             } 

1、首先我们看到要的到一个数据库连接,得到数据库连接这部分放在DBHelper类中的getConnection方法中实现。Class.forName("com.mysql.jdbc.Driver");用来加载mysql的jdbc驱动。

Mysql的Driver类实现了java.sql.Driver接口,任何数据库提供商的驱动类都必须实现这个接口。在DriverManager类中使用的都是接口Driver类型的驱动,也就是说驱动的使用不依赖于具体的实现,这无疑给我们的使用带来很大的方便。如果需要换用其他的数据库的话,只需要把Class.forName()中的参数换掉就可以了,可以说是非常方便的。

在com.mysql.jdbc.Driver类中,除了构造方法,就是一个static的方法体,它调用了DriverManager的registerDriver()方法,这个方法会加载所有系统提供的驱动,并把它们都假如到具体的驱动类中,当然现在就是mysql的Driver。在这里我们第一次看到了DriverManager类,这个类中提供了jdbc连接的主要操作,创建连接就是在这里完成的,可以说这是一个管理驱动的工具类。

注册驱动首先就是初始化,然后把驱动的信息封装一下放进一个叫做DriverInfo的驱动信息类中,最后放入一个驱动的集合中。初始化工作主要是完成所有驱动的加载。 

第二步就要根据url和用户名,密码来获得数据库的连接了。url一般都是这样的格式:jdbc:protocol://host_name:port/db_name?parameter_name=param_value。开头部分的protocal是对应于不同的数据库提供商的协议,例如mysql的就是mysql。

DriverManager中有重载了四个getConnection(),因为我们有用户名和密码,就把用户和密码存放在Properties中,最后进入终极getConnection(),如下:

Initialize()简直无所不在,DriverManager中只要使用driver之前,就要检查一下有没有初始化,非常小心。然后开始遍历所有驱动,直到找到一个可用的驱动,用这个驱动来取得一个数据库连接,最后返回这个连接。当然,这是正常的情况,从上面我们可以看到,程序中对异常的处理很仔细。如果连接失败,会记录抛出的第一个异常信息,如果没有找到合适的驱动,就抛出一个08001的错误。

现在重点就是假如一切正常,就应该从driver.connect()返回一个数据库连接,所以我们来看看如何通过url提供的数据库。

很简洁的写法,就是新建了一个mysql的connection,host, port, database给它传进入,让它去连接就对了,props里面是些什么东西呢,就是把url拆解一下,什么host,什么数据库名,然后url后面的一股脑的参数,再把用户跟密码也都放进入,反正就是所有的连接数据都放进入了。

在com.mysql.jdbc.Connection的构造方法里面,会先做一些连接的初始化操作,例如创建PreparedStatement的cache,创建日志等等。然后就进入createNewIO()来建立连接了。

新建了一个mysql的connection

从时序图中可以看到,createNewIO()就是新建了一个com.mysql.jdbc.MysqlIO,利用com.mysql.jdbc.StandardSocketFactory来创建一个socket。然后就由这个mySqlIO来与MySql服务器进行握手(doHandshake()),这个doHandshake主要用来初始化与Mysql server的连接,负责登陆服务器和处理连接错误。在其中会分析所连接的mysql server的版本,根据不同的版本以及是否使用SSL加密数据都有不同的处理方式,并把要传输给数据库server的数据都放在一个叫做packet的buffer中,调用send()方法往outputStream中写入要发送的数据。


继承关系

一旦有了一个statement,就可以通过执行statement.executeQuery()并通过ResultSet对象读出查询结果(如果查询有返回结果的话)。

创建statement的方法一般都有重载,我们看下面的prepareStatement:


public java.sql.PreparedStatement prepareStatement(String sql,

 

如果没有指定resultSetType和resultSetConcurrency的话,会给它们默认设置一个值。 

prepareStatement的创建如下图所示:

prepareStatement的创建

在new ParseInfo中,会对这个sql语句进行分析,例如看看这个sql是什么语句;有没有limit条件语句,还有一个重要的工作,如果使用的是PreparedStatement来准备sql语句的话,会在这里把sql语句进行分解。我们知道PreparedStatement对象在实例化创建时就被设置了一个sql语句,使用PreparedStatement对象执行的sql语句在首次发送到数据库时,sql语句就会被编译,这样当多次执行同一个sql语句时,mysql就不用每次都去编译sql语句了。

这个sql语句如果包含参数的话,可以用问号(”?”)来为参数进行占位,而不需要立即为参数赋值,而在语句执行之前,必须通过适当的set***()来为问号处的参数赋值。New ParseInfo()中,包含了参数的sql语句就会被分解为多段,放在staticSql中,以便需要设置参数时定位参数的位置。假如sql语句为“select * from adv where id = ? and name = ?”的话,那么staticSql中的元素就是3个,staticSql[3]={ ”select * from adv where id = ”, ” and name = ” , ””}。注意数组中最后一个元素,在这个例子中是””,因为我的例子里面最后一个就是”?”,如果sql语句是这样的“select * from adv where id = ? and name = ? order by id”的话,staticSql就变成是这样的{ ”select * from adv where id = ”, ” and name = ” , ” order by id”}。


设置sql语句中的参数值。

对于参数而言,PreparedStatement中一共有四个变量来储存它们,分别是

a) byte[][] parameterValues:参数转换为byte后的值。

b) InputStream[] parameterStreams:只有在调用存储过程batch(CallableStatement)的时候才会用到它,否则它的数组中的值设置为null。

c) boolean[] isStream:是否为stream的标志,如果调用的是preparedStatement,isStream数组中的值均为false,若调用的是CallableStatement,则均设置为true。

d) boolean[] isNull:标识参数是否为空,设置为false。

这四个变量的一维数组的大小都是一样的,sql语句中有几个待set的参数(几个问号),一维的元素个数就是多大。 

a) 检查preparedStatement是否已关闭,如果已关闭,抛出一个SQLError.SQL_STATE_CONNECTION_NOT_OPEN的错误。

b) fillSendPacket:创建数据包,其中包含了要发送到服务器的查询。

这个sendPacket就是mysql驱动要发送给数据库服务器的协议数据。一般来说,协议的数据格式有两种,一种是二进制流的格式,还有一种是文本的格式。文本协议就是基本上人可以直接阅读的协议,一般是用ascii字符集,也有用utf8格式的,优点是便于理解,读起来方便,扩充容易,缺点就是解析的时候比较麻烦,而且占用的空间比较大,冗余的数据比较多。二进制格式话,就需要服务器与客户端协议规定固定的数据结构,哪个位置放什么数据,如果单独看协议内容的话,很难理解数据含义,优点就是数据量小,解析的时候只要根据固定位置的值就能知道具体标识什么意义。

在这里使用的是二进制流的格式,也就是说协议中的数据格式是固定的,而且都要转换成二进制。格式为第一个byte标识操作信号,后面开始就是完整的sql语句的二进制流,请看下面的代码分析。

 

假如sql语句为“select * from adv where id = ?”的话,这个sendPacket中第一个byte的值就是3(MysqlDefs.QUERY的int值),后面接着的就是填充了参数值的完整的sql语句字符串(例如:select * from adv where id = 1)转换成的byte格式。

于是,我们看到,好像sql语句在这里就已经不是带”?”的preparedStatement,而是在驱动里面把参数替代到”?”中,再把完整的sql语句发送给mysql server来编译,那么尽管只是参数改变,但对于mysql server来说,每次都是新的sql语句,都要进行编译的。这与我们之前一直理解的PreparedStatement完全不一样。照理来说,应该把带”?”的sql语句发送给数据库server,由mysql server来编译这个带”?”的sql语句,然后用实际的参数来替代”?”,这样才是实现了sql语句只编译一次的效果。sql语句预编译的功能取决于server端,oracle就是支持sql预编译的。

所以说,从mysql驱动的PreparedStatement里面,好像我们并没有看到mysql支持预编译功能的证据。(实际测试也表明,如果server没有预编译功能的话,PreparedStatement和Statement的效率几乎一样,甚至当使用次数不多的时候,PreparedStatement比Statement还要慢一些)。 

2009-07-02增加(感谢gembler):其实,在mysql5上的版本是支持预编译sql功能的。我用的驱动是5.0.6的,在com.mysql.jdbc.Connection中有一个参数useServerPreparedStmts,表明是否使用预编译功能,所以如果把useServerPreparedStmts置为true的话,mysql驱动可以通过PreparedStatement的子类ServerPreparedStatement来实现真正的PreparedStatement的功能。在这个类的serverExecute方法里面,就负责告诉server,用现在提供的参数来动态绑定到编译好的sql语句上。所以说,ServerPreparedStatement才是真正实现了所谓prepare statement。

c) 设置当前的数据库名,并把之前的数据库名记录下来,在查询完成之后还要恢复原状。

d) 检查一下之前是否有缓存的数据,如果不久之前执行过这个查询,并且缓存了数据的话,就直接从缓存中取出。

e) 如果sql查询没有限制条件的话,为其设置默认的返回行数,若preparedStatement中已经设置了maxRows的话,就使用它。

f) executeInternal:执行查询。 


iii. 通过readAllResults方法读取查询结果。这个读取的过程与发送过程相反,如果接收到的数据包有多个的话,通过IO不断读取,并根据第packet第4个位置上的序号来组装这些packet。然后把读到的数据组装成resultSet中的rowData,这个结果就是我们要的查询结果了。

结合下面的executeQuery的时序图再理一下思路就更清楚了。

executeQuery的时序图

至此,把resultSet一步步的返回给dao,接下来的过程,就是从resultSet中取出rowData,组合成我们自己需要的对象数据了。

总结一下,经过这次对mysql驱动的探索,我发现了更多关于mysql的底层细节,对于以后分析问题解决问题有很大帮助,当然,这里面还有很多细节文中没有写。另外一个就是对于PreparedStatement有了重新的认识,有些东西往往都是想当然得出来的结论,真相还是要靠实践来发现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值