日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

JDBC连接mysql、创建表、操作数据、PreparedStatement防注入、sql语句返回值类型知识汇总

發(fā)布時(shí)間:2025/1/21 数据库 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDBC连接mysql、创建表、操作数据、PreparedStatement防注入、sql语句返回值类型知识汇总 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

JDBC連接過程:

import java.sql.*;/*** Description:* Created by CWG on 2020/10/29 21:05*/ public class ConnectionTest {public static void main(String[] args){String user = "root";String password = "cheng";String url = "jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC";String driver = "com.mysql.cj.jdbc.Driver";Connection conn = null;Statement stm = null;try{Class.forName(driver); //1.將mysql驅(qū)動(dòng)注冊(cè)到DriverManager中去conn = DriverManager.getConnection(url,user,password); //2. 建立連接stm = conn.createStatement(); // 3. 創(chuàng)建一個(gè)Statement 實(shí)例用于發(fā)送sql語句stm.execute("sql語句") //4.執(zhí)行sql語句} catch (Exception e) {e.printStackTrace();}} }

大概流程是這樣:

  • 首先第一步裝載驅(qū)動(dòng),個(gè)人理解就是:從DriverManager名字來看,該對(duì)象是來做sql語句跟數(shù)據(jù)庫數(shù)據(jù)交互的一個(gè)中間者,其功能實(shí)現(xiàn)還是靠com.mysql.cj.jdbc.Driver類完成。
    從源碼看出,其繼承NonRegisteringDriver類并實(shí)現(xiàn)java.sql.Driver接口,可以自查源碼發(fā)現(xiàn)com.mysql.cj.jdbc.Driver類功能都是繼承自NonRegisteringDriver類。JDBC規(guī)范要求Driver類(com.mysql.cj.jdbc)在使用前必須向DriverManger注冊(cè)自己。注冊(cè)過程在Driver類的靜態(tài)代碼塊中實(shí)現(xiàn)。也就是說只要類被加載,就完成了向驅(qū)動(dòng)管理器的注冊(cè)。
  • public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}} }
  • 接著就是建立連接以及通過conn.createStatement()創(chuàng)建一個(gè)Statement實(shí)例,用來傳遞sql語句。
  • 然后執(zhí)行sql語句,步驟3-4有幾點(diǎn)需要注意:
    (1)步驟3中,除了conn.createStatement() 創(chuàng)建Statement實(shí)例,還可以通過conn.prepareStatement 來創(chuàng)建PreparedStatement實(shí)例。
    (2)步驟4中,stm.execute 是一個(gè)通用執(zhí)行sql語句方法,除此之外還有executeQuery與executeUpdate方法。
    具體區(qū)別后邊講。

  • JDBC創(chuàng)建表:

    stm.execute("create table users(" +"id int AUTO_INCREMENT," +"name VARCHAR(20), " +"age int," +"sex char(2)," +"PRIMARY KEY (id));");

    根據(jù)上邊連接好的數(shù)據(jù)庫,執(zhí)行以上語句來創(chuàng)建一個(gè)users用戶表,其語句格式跟mysql完全一樣,只是寫成了字符串。另外注意AUTO_INCREMENT自增約束只能用于主鍵。


    JDBC操作數(shù)據(jù):

    • 插入數(shù)據(jù)
    stm.execute("INSERT into users(name,age,sex) VALUES ('小王',23,'男')");stm.execute("INSERT into users(name,age,sex) VALUES ('小豬',16,'女')");stm.execute("INSERT into users(name,age,sex) VALUES ('小李',18,'男')");stm.execute("INSERT into users(name,age,sex) VALUES ('小菜',23,'女')");//批量操作方式 String sql0 = "INSERT into users(name,age,sex) VALUES ('小王',23,'男')"; String sql1 = "INSERT into users(name,age,sex) VALUES ('小豬',16,'女')"; String sql2 = "INSERT into users(name,age,sex) VALUES ('小李',18,'男')"; String sql3 = "INSERT into users(name,age,sex) VALUES ('小菜',23,'女')"; stm.addBatch(sql0); stm.addBatch(sql1); stm.addBatch(sql2); stm.addBatch(sql3);stm.executeBatch();
    • 查詢數(shù)據(jù)
    stm.execute("SELECT * from users where sex = '男'");

    問題來了:插入數(shù)據(jù)沒問題,然而查詢數(shù)據(jù)雖然也正常執(zhí)行,但是卻沒返回要查詢的數(shù)據(jù)。另外這里的sql語句只能是靜態(tài)的,若想在sql語句中傳入?yún)?shù),如何處理?這里就是上邊提到的那兩點(diǎn)。下邊具體說明。


    Statement和 PreparedStatement:

    從功能上來講,PreparedStatement可以通過參數(shù)傳遞方式來執(zhí)行sql語句,參數(shù)位置用占位符? 表示。而Statement沒有這樣的機(jī)制,只能寫實(shí)際的字符串sql語句。

    stm = conn.createStatement(); String name = "小白"; String age = "17"; String sex = "男"; String sql = "insert into users(name,age,sex) values('"+ name + "',"+ age +",'"+ sex +"')"; stm.execute(sql);

    Statement方式傳遞參數(shù),只能通過字符串拼串來實(shí)現(xiàn),整體還是一個(gè)字符串語句。另外注意拼串時(shí),屬性name、sex這些字符串屬性的單引號(hào)不可省略。

    PreparedStatement ps = null; try{Class.forName(driver);conn = DriverManager.getConnection(url,user,password);String sql = "insert into users(name,age,sex) values(?,?,?)";ps = conn.prepareStatement(sql);ps.setString(1,"小灰");ps.setInt(2,21);ps.setString(3,"女");ps.execute();} catch (Exception e) {e.printStackTrace();}

    到這里表中數(shù)據(jù)(id不連續(xù)主要自己刪了一行,這里不要在意)

    PreparedStatement方式實(shí)現(xiàn)sql語句執(zhí)行,sql語句中先通過?來占位,然后通過setXX方法來拼串,方法中第一個(gè)參數(shù)代表第幾個(gè)參數(shù)。

    • 兩種方式區(qū)別:
  • 效率問題:Statement每次執(zhí)行sql語句,相關(guān)數(shù)據(jù)庫都要執(zhí)行sql語句的編譯;PreparedStatement是預(yù)編譯的(插入的參數(shù)不再編譯), 采用Cache機(jī)制(預(yù)編譯語句,放在Cache中,下次執(zhí)行相同SQL語句時(shí),則可以直接從Cache中取出來,對(duì)于批量處理可以大大提高效率。
  • PrepareStatement中執(zhí)行的SQL語句中是可以帶參數(shù)的,也就是說可以替換變量,盡量采用使用?號(hào)的方式傳遞參數(shù),增加代碼的可讀性又可以預(yù)編譯加速;而Statement則不可以。
  • 防止SQL注入:在SQL中包含特殊字符或SQL的關(guān)鍵字時(shí),Statement將出現(xiàn)不可預(yù)料的結(jié)果,可用PreparedStatement來解決,因?yàn)椴迦氲膮?shù)不會(huì)再進(jìn)行編譯,比較安全。
    以上3點(diǎn)摘自:https://www.cnblogs.com/gbb123/p/7053772.html

  • SQL注入問題:
    比如有這樣一個(gè)情況:

    String table = user; delete user;-- ; // 表名為 user; delete user;-- String name = "admin"; 有如下select語句 String sql = "select * from " + table + "where name = '" + name +"'";

    Statement進(jìn)行sql語句解析為:select * from user; delete user; -- where name = 'admin'; ,–后面的語句被注釋,而原本查詢用戶的語句變成了查詢所有用戶信息+刪除用戶表的語句,如果存在user用戶表,則信息全被刪了。
    而PreparedStatement會(huì)將傳入的參數(shù)全當(dāng)作字符串來處理,不會(huì)對(duì)傳入?yún)?shù)進(jìn)行編譯,因此可以避免該問題。雖然這里用一個(gè)惡意表名來舉例,場(chǎng)景感不是很強(qiáng),但是換一種場(chǎng)景,在系統(tǒng)登錄界面,通過用戶名來搞惡意注入,那問題就大了。


    executeQuery、executeUpdate 和 execute:

    前邊有一個(gè)問題就是返回值問題,execute雖然是全能sql語句操作,可以執(zhí)行任何sql語句,但其返回值類型卻是boolean型,select語句執(zhí)行時(shí),如何得到查詢結(jié)果?

    實(shí)際上,Statement和PreparedStatement接口都提供了三種執(zhí)行 SQL 語句的方法:executeQuery、executeUpdate 和 execute。

    • executeQuery 用于產(chǎn)生單個(gè)結(jié)果集(ResultSet,不是單行數(shù)據(jù))的語句。
    • executeUpdate 用于執(zhí)行 INSERT、UPDATE 或 DELETE 語句,SQL DDL(數(shù)據(jù)定義語言)語句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 語句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一個(gè)整數(shù)(int),指示受影響的行數(shù)(即更新計(jì)數(shù))。對(duì)于 CREATE TABLE 或 DROP TABLE 等不操作行的語句,executeUpdate 的返回值總為零。
    • execute 可用于執(zhí)行任何SQL語句,返回一個(gè)boolean值,表明執(zhí)行該SQL語句是否返回了ResultSet。如果執(zhí)行后第一個(gè)結(jié)果是ResultSet,則返回true,否則返回false。但它執(zhí)行SQL語句時(shí)比較麻煩,通常我們沒有必要使用execute方法來執(zhí)行SQL語句,而是使用executeQuery或executeUpdate更適合,但如果在不清楚SQL語句的類型時(shí)則只能使用execute方法來執(zhí)行該SQL語句了。

    以上三者區(qū)別摘自:https://www.cnblogs.com/yunqingtuo/p/10512677.html


    • execute實(shí)例:
    stm = conn.createStatement(); boolean res0 = stm.execute("INSERT into users(name,age,sex) VALUES ('小C',25,'女')"); boolean res1 = stm.execute("SELECT * from users where sex = '男'"); System.out.println(res0 + "====" + res1);

    返回false====true ,看出execute會(huì)在select語句中返回true,表明產(chǎn)生ResultSet結(jié)果集,而insert語句返回false

    獲取execute的結(jié)果集:

    stm = conn.createStatement(); boolean res1 = stm.execute("SELECT name,age from users where sex = '男'"); ResultSet resultSet = stm.getResultSet(); //注意通過Statement實(shí)例stm獲取結(jié)果集// ResultSetMetaData meta = resultSet.getMetaData(); //獲取每一條元數(shù)據(jù)信息 // int columnCount = meta.getColumnCount(); //統(tǒng)計(jì)一條元數(shù)據(jù)有幾個(gè)字段while (resultSet.next()){System.out.println("name:" + resultSet.getString("name") + ", age:" + resultSet.getInt("age")); }


    • executeQuery實(shí)例:
    ResultSet resultSet = stm.executeQuery("SELECT name,age from users where sex = '男'"); while (resultSet.next()){System.out.println("name:" + resultSet.getString("name") + ", age:" + resultSet.getInt("age")); }

    executeQuery返回值就是ResultSet,不必像execute通過stm.getResultSet()來獲取結(jié)果集。


    • executeUpdate 實(shí)例:
    int res = stm.executeUpdate("delete from users where sex = '男'"); System.out.println("語句executeUpdate執(zhí)行后受影響的行數(shù)為:" + res);

    結(jié)果:語句executeUpdate執(zhí)行后受影響的行數(shù)為:3,另外executeUpdate不可以執(zhí)行select操作


    與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的JDBC连接mysql、创建表、操作数据、PreparedStatement防注入、sql语句返回值类型知识汇总的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。