JDBC---PreparedStatement操作

版權(quán)聲明:本文為小斑馬偉原創(chuàng)文章,轉(zhuǎn)載請注明出處!


上篇播客介紹了SQL注入問題。本播客將介紹PreparedStatement來解決該問題。PreparedStatement:

  • 1 可以通過調(diào)用 Connection 對象的 preparedStatement() 方法獲取 PreparedStatement 對象。

  • 2 PreparedStatement 接口是 Statement 的子接口,它表示一條預(yù)編譯過的 SQL 語句。

  • 3 PreparedStatement 對象所代表的 SQL 語句中的參數(shù)用問號(?)來表示,調(diào)用 PreparedStatement 對象的 setXXX() 方法來設(shè)置這些參數(shù). setXXX() 方法有兩個參數(shù),第一個參數(shù)是要設(shè)置的 SQL 語句中的參數(shù)的索引(從 1 開始),第二個是設(shè)置的 SQL 語句中的參數(shù)的值。

     /**
     * 使用 PreparedStatement 將有效的解決 SQL 注入問題.
     */
    @Test
     public void testSQLInjection2() {
      String username = "a' OR PASSWORD = ";
      String password = " OR '1'='1";
    
      String sql = "SELECT * FROM users WHERE username = ? "
              + "AND password = ?";
    
      Connection connection = null;
      PreparedStatement preparedStatement = null;
      ResultSet resultSet = null;
    
      try {
          connection = JDBCTools.getConnection();
          preparedStatement = connection.prepareStatement(sql);
    
          preparedStatement.setString(1, username);
          preparedStatement.setString(2, password);
    
          resultSet = preparedStatement.executeQuery();
    
          if (resultSet.next()) {
              System.out.println("登錄成功!");
          } else {
              System.out.println("用戶名和密碼不匹配或用戶名不存在. ");
          }
    
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          JDBCTools.releaseDB(resultSet, preparedStatement, connection);
      }
    }
    

PreparedStatement vs Statement
代碼的可讀性和可維護(hù)性. (防止SQL語句寫錯)
PreparedStatement 能最大可能提高性能:
DBServer會對預(yù)編譯語句提供性能優(yōu)化。因為預(yù)編譯語句有可能被重復(fù)調(diào)用,所以語句在被DBServer的編譯器編譯后的執(zhí)行代碼被緩存下來,那么下次調(diào)用時只要是相同的預(yù)編譯語句就不需要編譯,只要將參數(shù)直接傳入編譯過的語句執(zhí)行代碼中就會得到執(zhí)行。

在statement語句中,即使是相同操作但因為數(shù)據(jù)內(nèi)容不一樣,所以整個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數(shù)據(jù)庫會對普通語句編譯后的執(zhí)行代碼緩存.這樣每執(zhí)行一次都要對傳入的語句編譯一次.
(語法檢查,語義檢查,翻譯成二進(jìn)制命令,緩存)
PreparedStatement 可以防止 SQL 注入
PreparedStatement更新(插入 刪除 更新)操作

@Test
public void testPreparedStatement() {
    Connection connection = null;
    PreparedStatement preparedStatement = null;

    try {
        connection = JDBCTools.getConnection();
        String sql = "INSERT INTO customers (name, email, birth) "
                + "VALUES(?,?,?)";

        preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1, "ATGUIGU");
        preparedStatement.setString(2, "simpleit@163.com");
        preparedStatement.setDate(3,
                new Date(new java.util.Date().getTime()));

        preparedStatement.executeUpdate();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCTools.releaseDB(null, preparedStatement, connection);
    }
}

PreparedStatement查詢操作

/**
 * 具體查詢學(xué)生信息的. 返回一個 Student 對象. 若不存在, 則返回 null
 * 
 * @param searchType
 *            : 1 或 2.
 * @return
 */
private Student searchStudent(int searchType) {

    String sql = "SELECT flowid, type, idcard, examcard,"
            + "studentname, location, grade " + "FROM examstudent "
            + "WHERE ";

    Scanner scanner = new Scanner(System.in);

    // 1. 根據(jù)輸入的 searchType, 提示用戶輸入信息:
    // 1.1 若 searchType 為 1, 提示: 請輸入身份證號. 若為 2 提示: 請輸入準(zhǔn)考證號
    // 2. 根據(jù) searchType 確定 SQL
    if (searchType == 1) {
        System.out.print("請輸入準(zhǔn)考證號:");
        String examCard = scanner.next();
        sql = sql + "examcard = '" + examCard + "'";
    } else {
        System.out.print("請輸入身份證號:");
        String examCard = scanner.next();
        sql = sql + "idcard = '" + examCard + "'";
    }

    // 3. 執(zhí)行查詢
    Student student = getStudent(sql);

    // 4. 若存在查詢結(jié)果, 把查詢結(jié)果封裝為一個 Student 對象

    return student;
}

/**
 * 根據(jù)傳入的 SQL 返回 Student 對象
 * 
 * @param sql
 * @return
 */
public Student getStudent(String sql, Object... args) {
    Student stu = null;

    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;

    try {
        connection = JDBCTools.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i + 1, args[i]);
        }
        resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            stu = new Student();
            stu.setFlowId(resultSet.getInt(1));
            stu.setType(resultSet.getInt(2));
            stu.setIdCard(resultSet.getString(3));
            // ...
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCTools.releaseDB(resultSet, preparedStatement, connection);
    }

    return stu;
}

/**
 * 從控制臺讀入一個整數(shù), 確定要查詢的類型
 * 
 * @return: 1. 用身份證查詢. 2. 用準(zhǔn)考證號查詢 其他的無效. 并提示請用戶重新輸入.
 */
private int getSearchTypeFromConsole() {

    System.out.print("請輸入查詢類型: 1. 用身份證查詢. 2. 用準(zhǔn)考證號查詢 ");

    Scanner scanner = new Scanner(System.in);
    int type = scanner.nextInt();

    if (type != 1 && type != 2) {
        System.out.println("輸入有誤請重新輸入!");
        throw new RuntimeException();
    }

    return type;
}

Student類:

 public class Student {

// 流水號
private int flowId;
// 考試的類型
private int type;
// 身份證號
private String idCard;
// 準(zhǔn)考證號
private String examCard;
// 學(xué)生名
private String studentName;
// 學(xué)生地址
private String location;
// 考試分?jǐn)?shù).
private int grade;

public int getFlowId() {
    return flowId;
}

public void setFlowId(int flowId) {
    this.flowId = flowId;
}

public int getType() {
    return type;
}

public void setType(int type) {
    this.type = type;
}

public String getIdCard() {
    return idCard;
}

public void setIdCard(String idCard) {
    this.idCard = idCard;
}

public String getExamCard() {
    return examCard;
}

public void setExamCard(String examCard) {
    this.examCard = examCard;
}

public String getStudentName() {
    return studentName;
}

public void setStudentName(String studentName) {
    this.studentName = studentName;
}

public String getLocation() {
    return location;
}

public void setLocation(String location) {
    this.location = location;
}

public int getGrade() {
    return grade;
}

public void setGrade(int grade) {
    this.grade = grade;
}

public Student(int flowId, int type, String idCard, String examCard,
        String studentName, String location, int grade) {
    super();
    this.flowId = flowId;
    this.type = type;
    this.idCard = idCard;
    this.examCard = examCard;
    this.studentName = studentName;
    this.location = location;
    this.grade = grade;
}

public Student() {
    // TODO Auto-generated constructor stub
}

@Override
public String toString() {
    return "Student [flowId=" + flowId + ", type=" + type + ", idCard="
            + idCard + ", examCard=" + examCard + ", studentName="
            + studentName + ", location=" + location + ", grade=" + grade
            + "]";
}

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內(nèi)容

  • JDBC基礎(chǔ)知識 一、采用JDBC訪問數(shù)據(jù)庫的基本步驟: A.載入JDBC驅(qū)動程序 B.定義連接URL ...
    java日記閱讀 3,877評論 0 20
  • JDBC簡介 SUN公司為了簡化、統(tǒng)一對數(shù)據(jù)庫的操作,定義了一套Java操作數(shù)據(jù)庫的規(guī)范,稱之為JDBC。JDBC...
    奮斗的老王閱讀 1,531評論 0 51
  • 本文內(nèi)容 1.什么是JDBC以及為什么要使用JDBC 2.JDBC核心API的講解 3.使用JDBC核心API進(jìn)行...
    Vincilovfang閱讀 1,229評論 0 11
  • java類和數(shù)據(jù)庫表的關(guān)系 類對應(yīng)表,字段對應(yīng)屬性,一行數(shù)據(jù)對應(yīng)一個對象,所以類的屬性名要和表的字段名相同才能進(jìn)行...
    孫浩j閱讀 562評論 0 1
  • 今天第一次產(chǎn)檢,身體不好,寶寶加油,媽媽以后不再生氣了
    濤濤愛培培閱讀 165評論 0 0