大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

Java中連結MySQL啟用預編譯的先決條件


在Java編程中,應用代碼絕大多數使用了使用了PreparedStatement,無論你是直接使用JDBC還是使用框架。

在Java編程中,絕大多數使用了使用了PreparedStatement的應用代碼沒有啟用預編譯,無論你是直接使用JDBC還是使用框架。

在我所能見到的項目中,幾乎沒有見過啟用MySQL預編譯功能的。網上更有文章說MySQL不支持預編譯,實在是害人不淺。

這篇文章分以下幾個方面:

一、MySQL是支持預編譯的

打開MySQL日志功能,啟動MySQL,然后 tail -f mysql.log.path(默認:/var/log/mysql/mysql.log).

  

create table axman_test (ID int(4) auto_increment primary key, name varchar(20),age int(4));
  insert into axman_test (name,age) values ('axman',1000);
  prepare myPreparedStmt from 'select * from axman_test where name = ? _cke_saved_name = ?';
  set @name='axman' _cke_saved_name='axman';
  execute myPreparedStmt using @name;


控制臺可以正確地輸出:

mysql> execute myPreparedStmt using @name;

  +----+-------+------+

  | ID | name | age |

  +----+-------+------+

  | 1 | axman | 1000 |

  +----+-------+------+

  1 row in set (0.00 sec)

而log文件中也忠實地記錄如下:

  111028 9:25:06 51 Query prepare myPreparedStmt from 'select * from axman_test where name = ? _cke_saved_name = ?'

  51 Prepare select * from axman_test where name = ?

  51 Query set @name='axman' _cke_saved_name='axman'

  111028 9:25:08 51 Query execute myPreparedStmt using @name

  51 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

二、通過JDBC本身是可以預編譯的,這個不用多說。相當于我們把控制臺輸入的命令直接通過JDBC語句來執行:

  Class.forName("org.gjt.mm.mysql.Driver");

  String url = "jdbc:mysql://localhost:3306/mysql";

  Connection conn = null;

  try {

  conn = DriverManager.getConnection(url, "root", "12345678");

  Statement stmt = conn.createStatement();

  /*以下忽略返回值處理*/

  stmt.executeUpdate("prepare mystmt from 'select * from axman_test where name = ? _cke_saved_name = ?'");

  stmt.execute("set @name='axman' _cke_saved_name='axman'");

  stmt.executeQuery("execute mystmt using @name");

  stmt.close();

  } finally {

  if (conn != null) {

  conn.close();

  }

  }

看日志輸出:

  111028 9:30:19 52 Connect root@localhost on mysql

  52 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SHOW VARIABLES WHERE Variable_name ='language' _cke_saved_name ='language' OR Variable_name = 'net_write_timeout' _cke_saved_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' _cke_saved_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' _cke_saved_name = 'wait_timeout' OR Variable_name = 'character_set_client' _cke_saved_name = 'character_set_client' OR Variable_name = 'character_set_connection' _cke_saved_name = 'character_set_connection' OR Variable_name = 'character_set' _cke_saved_name = 'character_set' OR Variable_name = 'character_set_server' _cke_saved_name = 'character_set_server' OR Variable_name = 'tx_isolation' _cke_saved_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' _cke_saved_name = 'transaction_isolation' OR Variable_name = 'character_set_results' _cke_saved_name = 'character_set_results' OR Variable_name = 'timezone' _cke_saved_name = 'timezone' OR Variable_name = 'time_zone' _cke_saved_name = 'time_zone' OR Variable_name = 'system_time_zone' _cke_saved_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' _cke_saved_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' _cke_saved_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' _cke_saved_name = 'net_buffer_length' OR Variable_name = 'sql_mode' _cke_saved_name = 'sql_mode' OR Variable_name = 'query_cache_type' _cke_saved_name = 'query_cache_type' OR Variable_name = 'query_cache_size' _cke_saved_name = 'query_cache_size' OR Variable_name = 'init_connect' _cke_saved_name = 'init_connect'

  52 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SELECT @@session.auto_increment_increment

  52 Query SHOW COLLATION

  52 Query SET NAMES latin1

  52 Query SET character_set_results = NULL

  52 Query SET autocommit=1

  52 Query SET sql_mode='STRICT_TRANS_TABLES'

  52 Query prepare mystmt from 'select * from axman_test where name = ? _cke_saved_name = ?'

  52 Prepare select * from axman_test where name = ?

  52 Query set @name='axman' _cke_saved_name='axman'

  52 Query execute mystmt using @name

  52 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

  52 Quit

三、默認的PrearedStatement不能開啟MySQL預編譯功能:

  Class.forName("org.gjt.mm.mysql.Driver");

  String url = "jdbc:mysql://localhost:3306/mysql";

  Connection conn = null;

  try {

  conn = DriverManager.getConnection(url, "root", "12345678");

  PreparedStatement ps = conn.prepareStatement("select * from axman_test where name = ? _cke_saved_name = ?");

  ps.setString(1, "axman' or 1==1");

  ResultSet rs = ps.executeQuery();

  if (rs.next()) {

  System.out.println(rs.getString(1));

  }

  Thread.sleep(1000);

  rs.close();

  ps.clearParameters();

  ps.setString(1, "axman");

  rs = ps.executeQuery();

  if (rs.next()) {

  System.out.println(rs.getString(1));

  }

  rs.close();

  ps.close();

  } finally {

  if (conn != null) {

  conn.close();

  }

  }

廢話少說,直接看日志:

  111028 9:54:03 53 Connect root@localhost on mysql

  53 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SHOW VARIABLES WHERE Variable_name ='language' _cke_saved_name ='language' OR Variable_name = 'net_write_timeout' _cke_saved_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' _cke_saved_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' _cke_saved_name = 'wait_timeout' OR Variable_name = 'character_set_client' _cke_saved_name = 'character_set_client' OR Variable_name = 'character_set_connection' _cke_saved_name = 'character_set_connection' OR Variable_name = 'character_set' _cke_saved_name = 'character_set' OR Variable_name = 'character_set_server' _cke_saved_name = 'character_set_server' OR Variable_name = 'tx_isolation' _cke_saved_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' _cke_saved_name = 'transaction_isolation' OR Variable_name = 'character_set_results' _cke_saved_name = 'character_set_results' OR Variable_name = 'timezone' _cke_saved_name = 'timezone' OR Variable_name = 'time_zone' _cke_saved_name = 'time_zone' OR Variable_name = 'system_time_zone' _cke_saved_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' _cke_saved_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' _cke_saved_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' _cke_saved_name = 'net_buffer_length' OR Variable_name = 'sql_mode' _cke_saved_name = 'sql_mode' OR Variable_name = 'query_cache_type' _cke_saved_name = 'query_cache_type' OR Variable_name = 'query_cache_size' _cke_saved_name = 'query_cache_size' OR Variable_name = 'init_connect' _cke_saved_name = 'init_connect'

  53 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SELECT @@session.auto_increment_increment

  53 Query SHOW COLLATION

  53 Query SET NAMES latin1

  53 Query SET character_set_results = NULL

  53 Query SET autocommit=1

  53 Query SET sql_mode='STRICT_TRANS_TABLES'

  53 Query select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1'

  111028 9:54:04 53 Query select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

  53 Quit

兩條語句都是直接執行,而沒有預編譯。注意我的第一條語句select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1',下面還會說到它。

接著我們改變一下jdbc.url的選項:

  String url = "jdbc:mysql://localhost:3306/mysql?cachePrepStmts=true&prepStmtCacheSize=25&prepStmtCacheSqlLimit=256";

執行上面的代碼還是沒有開啟Mysql的預編譯。

四、只有使用了useServerPrepStmts=true才能開啟Mysql的預編譯。

上面的代碼其它不變,只修改String url = "jdbc:mysql://localhost:3306/mysql?useServerPrepStmts=true";

查看日志:

  111028 10:04:52 54 Connect root@localhost on mysql

  54 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SHOW VARIABLES WHERE Variable_name ='language' _cke_saved_name ='language' OR Variable_name = 'net_write_timeout' _cke_saved_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' _cke_saved_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' _cke_saved_name = 'wait_timeout' OR Variable_name = 'character_set_client' _cke_saved_name = 'character_set_client' OR Variable_name = 'character_set_connection' _cke_saved_name = 'character_set_connection' OR Variable_name = 'character_set' _cke_saved_name = 'character_set' OR Variable_name = 'character_set_server' _cke_saved_name = 'character_set_server' OR Variable_name = 'tx_isolation' _cke_saved_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' _cke_saved_name = 'transaction_isolation' OR Variable_name = 'character_set_results' _cke_saved_name = 'character_set_results' OR Variable_name = 'timezone' _cke_saved_name = 'timezone' OR Variable_name = 'time_zone' _cke_saved_name = 'time_zone' OR Variable_name = 'system_time_zone' _cke_saved_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' _cke_saved_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' _cke_saved_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' _cke_saved_name = 'net_buffer_length' OR Variable_name = 'sql_mode' _cke_saved_name = 'sql_mode' OR Variable_name = 'query_cache_type' _cke_saved_name = 'query_cache_type' OR Variable_name = 'query_cache_size' _cke_saved_name = 'query_cache_size' OR Variable_name = 'init_connect' _cke_saved_name = 'init_connect'

  54 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SELECT @@session.auto_increment_increment

  54 Query SHOW COLLATION

  54 Query SET NAMES latin1

  54 Query SET character_set_results = NULL

  54 Query SET autocommit=1

  54 Query SET sql_mode='STRICT_TRANS_TABLES'

  54 Prepare select * from axman_test where name = ?

  54 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1'

  111028 10:04:53 54 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

  54 Close stmt

  54 Quit

如果useServerPrepStmts=true,ConneciontImpl在prepareStatement時會產生一個ServerPreparedStatement.在這個ServerPreparedStatement對象構造時首先會把當前SQL語句發送給MySQL進行預編譯,然后將返回的結果緩存起來,其中包含預編譯的名稱(我們可以看成是當前SQL語句編譯后的函數名),簽名(參數列表),然后執行的時候就會直接把參數傳給這個函數請求MySQL執行這個函數。否則返回的是客戶端預編譯語句,它僅做參數化工作,見第五節。

注意上面的代碼中,兩次執行使用的是同一個PreparedStatement句柄.如果使用個不同的PreparedStatement句柄,把代碼改成:

  Class.forName("org.gjt.mm.mysql.Driver");

  String url = "jdbc:mysql://localhost:3306/mysql?useServerPrepStmts=true";

  Connection conn = null;

  try {

  conn = DriverManager.getConnection(url, "root", "12345678");

  PreparedStatement ps = conn.prepareStatement("select * from axman_test where name = ? _cke_saved_name = ?");

  ps.setString(1, "axman' or 1==1");

  ResultSet rs = ps.executeQuery();

  if (rs.next()) {

  System.out.println(rs.getString(1));

  }

  Thread.sleep(1000);

  rs.close();

  ps.close();

  ps = conn.prepareStatement("select * from axman_test where name = ? _cke_saved_name = ?");

  ps.setString(1, "axman");

  rs = ps.executeQuery();

  if (rs.next()) {

  System.out.println(rs.getString(1));

  }

  rs.close();

  ps.close();

  } finally {

  if (conn != null) {

  conn.close();

  }

  }

再看日志輸出:

  Connect root@localhost on mysql

  55 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SHOW VARIABLES WHERE Variable_name ='language' _cke_saved_name ='language' OR Variable_name = 'net_write_timeout' _cke_saved_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' _cke_saved_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' _cke_saved_name = 'wait_timeout' OR Variable_name = 'character_set_client' _cke_saved_name = 'character_set_client' OR Variable_name = 'character_set_connection' _cke_saved_name = 'character_set_connection' OR Variable_name = 'character_set' _cke_saved_name = 'character_set' OR Variable_name = 'character_set_server' _cke_saved_name = 'character_set_server' OR Variable_name = 'tx_isolation' _cke_saved_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' _cke_saved_name = 'transaction_isolation' OR Variable_name = 'character_set_results' _cke_saved_name = 'character_set_results' OR Variable_name = 'timezone' _cke_saved_name = 'timezone' OR Variable_name = 'time_zone' _cke_saved_name = 'time_zone' OR Variable_name = 'system_time_zone' _cke_saved_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' _cke_saved_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' _cke_saved_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' _cke_saved_name = 'net_buffer_length' OR Variable_name = 'sql_mode' _cke_saved_name = 'sql_mode' OR Variable_name = 'query_cache_type' _cke_saved_name = 'query_cache_type' OR Variable_name = 'query_cache_size' _cke_saved_name = 'query_cache_size' OR Variable_name = 'init_connect' _cke_saved_name = 'init_connect'

  55 Query /* @MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ ) */SELECT @@session.auto_increment_increment

  55 Query SHOW COLLATION

  55 Query SET NAMES latin1

  55 Query SET character_set_results = NULL

  55 Query SET autocommit=1

  55 Query SET sql_mode='STRICT_TRANS_TABLES'

  55 Prepare select * from axman_test where name = ?

  55 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1'

  111028 10:10:24 55 Close stmt

  55 Prepare select * from axman_test where name = ?

  55 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

  55 Close stmt

  55 Quit

  55 Quit

同一個SQL語句發生了兩次預編譯。這不是我們想要的效果,要想對同一SQL語句多次執行不是每次都預編譯,就要使用cachePrepStmts=true,這個選項可以讓JVM端緩存每個SQL語句的預編譯結果,說白了就是以SQL語句為key, 將預編譯結果緩存起來,下次遇到相同的SQL語句時作為key去get一下看看有沒有這個SQL語句的預編譯結果,有就直接合出來用。我們還是以事實來說明:

上面的代碼只修改String url = "jdbc:mysql://localhost:3306/mysql?useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=25&prepStmtCacheSqlLimit=256";

這行代碼中有其它參數自己去讀文檔,我不多啰嗦,執行的結果:

  111028 10:27:23 58 Connect root@localhost on mysql

  58 Query /* mysql-connector-java-5.1.18 ( Revision: [email protected] ) */SHOW VARIABLES WHERE Variable_name ='language' _cke_saved_name ='language' OR Variable_name = 'net_write_timeout' _cke_saved_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' _cke_saved_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' _cke_saved_name = 'wait_timeout' OR Variable_name = 'character_set_client' _cke_saved_name = 'character_set_client' OR Variable_name = 'character_set_connection' _cke_saved_name = 'character_set_connection' OR Variable_name = 'character_set' _cke_saved_name = 'character_set' OR Variable_name = 'character_set_server' _cke_saved_name = 'character_set_server' OR Variable_name = 'tx_isolation' _cke_saved_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' _cke_saved_name = 'transaction_isolation' OR Variable_name = 'character_set_results' _cke_saved_name = 'character_set_results' OR Variable_name = 'timezone' _cke_saved_name = 'timezone' OR Variable_name = 'time_zone' _cke_saved_name = 'time_zone' OR Variable_name = 'system_time_zone' _cke_saved_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' _cke_saved_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' _cke_saved_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' _cke_saved_name = 'net_buffer_length' OR Variable_name = 'sql_mode' _cke_saved_name = 'sql_mode' OR Variable_name = 'query_cache_type' _cke_saved_name = 'query_cache_type' OR Variable_name = 'query_cache_size' _cke_saved_name = 'query_cache_size' OR Variable_name = 'init_connect' _cke_saved_name = 'init_connect'

  58 Query /* mysql-connector-java-5.1.18 ( Revision: [email protected] ) */SELECT @@session.auto_increment_increment

  58 Query SHOW COLLATION

  58 Query SET NAMES latin1

  58 Query SET character_set_results = NULL

  58 Query SET autocommit=1

  58 Query SET sql_mode='STRICT_TRANS_TABLES'

  58 Prepare select * from axman_test where name = ?

  58 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1'

  111028 10:27:24 58 Execute select * from axman_test where name = 'axman' _cke_saved_name = 'axman'

  58 Quit

注意僅發生一次預編譯,盡管代碼本身在第一次執行后關閉了ps.close();但因為使用了cachePrepStmts=true,底層并沒有真實關閉。

五、即使沒有開啟MySQL的預編譯,堅持使用PreparedStatement仍然非常必要。

在第三節的最后我說到"注意我的第一條語句select * from axman_test where name = 'axman' _cke_saved_name = 'axman' or 1==1',下面還會說到它。",現在我們回過頭來看,即使沒有開啟MySQL端的預編譯,我們仍然要堅持使用PreparedStatement,因為JVM端對PreparedStatement的SQL語句進行了參數化,即用占位符替換參數,以后任何內容輸入都是字符串或其它類型的值,而不會和原始的SQL語句拚接產生SQL注入,對字符串中的任何字符都會做檢查,如果可能是SQL語句使用的標識符,會進行轉義。然后發送一個合法的安全的SQL語句給數據庫執行。

上一篇:12306奇葩驗證碼的開發者會是那家的?
下一篇:Java視頻意想不到的結果

開班信息

主站蜘蛛池模板: 五月狠狠亚洲小说专区 | 四虎www成人影院 | 国产中文 | aⅴ一区二区三区 | 久久久久夜色精品波多野结衣 | 亚洲精品国产经典一区二区 | 麻豆成人精品国产免费 | 亚洲aa视频 | 99爱国产 | 日韩精品久久不卡中文字幕 | 国产三级在线精品男人的天堂 | 日日操夜夜爱 | 欧美日本俄罗斯一级毛片 | 7777成年大片免费播放器 | 久久天天操 | 久久精品道一区二区三区 | 视频大全在线观看免费 | 亚洲欧美日韩综合一区久久 | 99这里只有 | 成人在线视频免费观看 | 真实国产乱子伦精品免费 | 国产51自产区在线 | 伊人免费在线观看 | 成人国产精品一级毛片天堂 | 欧美激情中文字幕一区二区 | 老司机成人午夜精品福利视频 | 国产女人视频 | 久久香蕉国产 | 日韩特级毛片免费观看视频 | 日韩中文字幕精品 | 草久在线观看视频 | 亚洲狠狠婷婷综合久久久久网站 | 久久免费精品一区二区 | 国产在线精品一区二区三区 | 手机福利在线观看 | 久久综合九色综合国产 | 免费伊人 | 国产真实自拍 | 香蕉视频国产精品 | 欧美成人a | 国内精品久久久久 |