JAVA EE-JDBC

JAVA EE-JDBC

塑成一個雕像,把生命賦給這個雕像,這是美麗的;創造一個有智慧的人,把真理灌輸給他,這就更美麗。 —— 雨果

什麼是JDBC?

  • JDBC 就是由 java提供的一套訪問數據庫的統一api. 使用這套api , 我們在 切換庫時 十分方便. 並且切換庫不會改變代碼.學習成本也降低了.

如何開發一個JDBC程序?

1 導包 ==> 導入廠商提供的數據庫驅動. ==> mysql-connector-java-5.0.8-bin.jar
2> 註冊驅動 
3> 連接數據庫
4> 操作數據庫(執行sql)
5> 關閉資源

JDBC中的類

DriverManager 用於註冊驅動,獲得連接
Connection    代表連接 , 獲得Statement對象
Statement     運送sql語句
ResultSet     將運行結果從數據庫運回java端

一個簡單的JDBC連接實例

public class Demo {
    @Test
    //發送插入語句
    public void fun1() throws Exception{
        //1 導入驅動類庫
        //2 註冊驅動
        DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 
        //3 連接數據庫
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //4 操作數據庫
        Statement st = conn.createStatement();
        String sql =  " INSERT INTO `t_user` "+
                      " VALUES (NULL, 'tom', 18)" ;
        st.executeUpdate(sql);

        //5 關閉資源
        st.close();
        conn.close();
    }

    @Test
    //發送查詢語句
    public void fun2() throws Exception{
        //1 導入驅動類庫
        //2 註冊驅動
        DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 
        //3 連接數據庫
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //4 操作數據庫
        Statement st = conn.createStatement();
        String sql =  " select * from t_user " ;

        ResultSet rs = st.executeQuery(sql);

        //遍歷結果集中的內容並打印
        while(rs.next()){
            String name = rs.getString("name");
            int id = rs.getInt("id");
            int age = rs.getInt("age");

            System.out.println(name+"==>"+age+"==>"+id);
        }

        //5 關閉資源
        st.close();
        conn.close();
    }
}

關於DriverManager的細節問題

public class Demo {
    @Test
    public void fun1() throws Exception{
        // 註冊驅動
        //註冊方式1:不推薦 => 驅動實現類中 的靜態代碼以及調用過
        // DriverManager.registerDriver(driver);
        //註冊方式2:推薦
        Class.forName("com.mysql.jdbc.Driver");
    }
    @Test
    public void fun2() throws Exception{
        // 獲得連接
        DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/day05", "root", "1234");

        //url完整 格式:  大協議:子協議://IP地址:端口號/庫名?參數鍵=參數值
        //完整:             jdbc:mysql://127.0.0.1:3306/day05?useUnicode=true&characterEncoding=utf8
        //簡單:             jdbc:mysql:///day05?useUnicode=true&characterEncoding=utf8
    }
}
1> 註冊驅動的問題.
        DriverManager.registDriver(new Driver()); ==> 該種註冊方式,在將來的開發中 不要使用.
    使用如下方式:
        Class.forName("com.mysql.jdbc.Driver");
    2>爲什麼?
        在驅動類的代碼中,我們可以看到有一個靜態代碼塊。 靜態代碼塊中已經做了註冊驅動的事情。 所以我們只需要加載
    驅動類,就相當於調用了 registDriver 方法。
    3>使用 Class.forName有什麼好處?
        * 如果調用registDriver 方法, 那麼相當於創建了兩個Driver對象,浪費資源.
        * 使用forname的方式. 因爲驅動類的名稱是以字符串的形式填寫,那麼我們把該名稱放到配置文件中,每次從配置文件中讀取.
    那麼切換驅動類就非常方便. 也就意味着切換數據庫方便.

關於Statement的細節問題

//Statement細節
public class Demo {
    @Test
    //execute   原始,增刪改查都可以 返回值  true=> 查詢有結果集  |  false=> 查詢沒有結果集
    //executeBatch  批量執行sql
    //executeUpdate 執行增刪改
    //executeQuery 執行查詢
    public void fun1() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  " INSERT INTO `t_user` "+
                  " VALUES (NULL, 'jerry', 16)" ;
        //5 執行sql
    boolean result =    st.execute(sql);
    System.out.println(result);//false
        //6關閉資源
     st.close();
     conn.close();
    }
    @Test
    public void fun2() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  "select * from t_user" ;
        //5 執行sql
    boolean result =    st.execute(sql);
        if(result){
            ResultSet  rs = st.getResultSet();
            System.out.println(rs);
        }
        //6關閉資源
     st.close();
     conn.close();
    }
    @Test
    //executeUpdate
    public void fun3() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  " INSERT INTO `t_user` "+
                  " VALUES (NULL, 'jack', 20)" ;
        //5 執行sql
        int row =  st.executeUpdate(sql);
        if(row!=1){
            throw new RuntimeException("插入失敗!");
        }
        System.out.println(row);//1
        //6關閉資源
     st.close();
     conn.close();
    }
    @Test
    //executeQuery
    public void fun4() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  "select * from t_user" ;
        //5 執行sql
        ResultSet rs = st.executeQuery(sql);
        //遍歷rs
        System.out.println(rs);
        //6關閉資源
     st.close();
     conn.close();
    }
}
6.Statement 對象
    該對象可以理解爲一個 向數據庫運送sql語句的 "小車";
    方法:
        void addBatch(String sql)  向車上添加語句. (用於批量執行sql語句); insert update delete
        int[] executeBatch()   將車上的語句 運送給數據庫執行.  返回值存放每個語句執行後影響的行數. 因爲是多個語句,所以用數組裝.
        void clearBatch() 清除車上的語句.
        ----以上3個方法是批量執行sql相關的(下午最後一節課演示)----------------------
        boolean execute(String sql)  執行一個sql語句. 如果該語句返回結果集 返回值爲true(select). 如果該語句不返回結果集 返回false(insert update delete);
        ResultSet executeQuery(String sql)  執行一個有結果集的查詢. 會將結果集包裝到resultset對象中.(select)
        int executeUpdate(String sql)   執行一個沒有結果集的語句. 會將語句影響的行數返回.(insert update delete)

    結論: 
        執行查詢語句時使用: executeQuery方法
        執行增刪改等語句時使用: executeUpdate方法

關於ResultSet的細節

Demo 1

//ResultSet細節
//功能: 封裝結果集數據
//操作: 如何獲得(取出)結果
//結論:  
    //1. next方法,向下移動並判斷是否有內容
    //2. getXXX方法,根據列索引或列名獲得列的內容
public class Demo {
    @Test
    public void fun1() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  "select * from t_user" ;
        //5 執行sql
        ResultSet rs = st.executeQuery(sql);
        //向下移動一行,並判斷
        while(rs.next()){
            //有數據
            //取數據:getXXX 
            int id = rs.getInt(1);//獲得第一列的值
            //int id rs.getInt("id");// 獲得id列的值
            String name = rs.getString(2);//獲得第二列的值
            int age = rs.getInt(3);//獲得第三列的值
            System.out.println(id+"==>"+name+"==>"+age);


        }

        //6關閉資源
     st.close();
     conn.close();
    }
    /* 數據庫類型            java類型
        int              int
        double           double
        decimal          double
        char             String
        varchar          String
        datetime         Date
        timestamp        Timestamp/Date

     */
}

Demo 2

//ResultSet細節
// 1.結果集的滾動 => 移動結果集的指針就是滾動
// 2.結果集反向修改數據庫
public class Demo2 {
    @Test
    public void fun1() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement();
        //4 書寫sql
        String sql =  "select * from t_user" ;
        //5 執行sql
        ResultSet rs = st.executeQuery(sql);
        //倒着遍歷
            //1> 光標移動到最後一行之後
            rs.afterLast();
            //2> 遍歷=>
            while(rs.previous()){//向上移動光標,並判斷是否有數據
                int id = rs.getInt("id");// 獲得id列的值
                String name = rs.getString("name");//獲得第二列的值
                int age = rs.getInt("age");//獲得第三列的值
                System.out.println(id+"==>"+name+"==>"+age);
            }
        //6關閉資源
     st.close();
     conn.close();
    }
    /* 數據庫類型            java類型
        int              int
        double           double
        decimal          double
        char             String
        varchar          String
        datetime         Date
        timestamp        Timestamp/Date

     */
}

Demo 3

//ResultSet細節
// 2.結果集反向修改數據庫
public class Demo3 {
    @Test
    public void fun1() throws Exception{
        //1 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "1234");
        //3 創建Statement
        Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
        //4 書寫sql
        String sql =  "select * from t_user" ;
        //5 執行sql
        ResultSet rs = st.executeQuery(sql);
        //使用結果集 反向修改數據庫
        rs.next();//將光標移動到第一行
        rs.updateString("name", "湯姆");// 修改第一行name列的值爲中文湯姆
        rs.updateRow();// 確認修改
        //6關閉資源
     st.close();
     conn.close();
    }
}

參數問題:

        參數1  resultSetType - 結果集類型   
        ResultSet.TYPE_FORWARD_ONLY、 不支持結果集滾動,只能向前.
        ResultSet.TYPE_SCROLL_INSENSITIVE  支持滾動, 遲鈍,不敏感的結果集.
        ResultSet.TYPE_SCROLL_SENSITIVE    支持滾動, 敏感的結果集.
        參數2  resultSetConcurrency  - 結果是否支持修改類型
        ResultSet.CONCUR_READ_ONLY   不支持修改
        ResultSet.CONCUR_UPDATABLE   支持修改

JDBC中關於釋放資源的一些問題

9.釋放資源
    1> 從小到大釋放. resultSet < Statement < Connection
    2> 3個都需要釋放.
    3>釋放時調用close方法即可. 如果其中一個對象的關閉 出現了異常. 也要保證其他的對象關閉方法被調用.

    resultSet.close();
    Statement.close();
    Connection.close();
    以上代碼是無法保證一定都能執行的.

    try{
        resultSet.close();
    }catch(Exception e){

    }finally{
        try{
            Statement.close();
        }catch(Exception e){
        }
        finally{
            try{
                Connection.close();
            }catch(Exception e){

            }
        }
    }

自己封裝的一JDBC工具類

import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
    private static String driver;
    private static String url;
    private static String user;
    private static String password;



    static{

        try {
            //0讀取配置文件
            Properties prop  = new Properties();

            InputStream is = new FileInputStream("src/db.properties");


            prop.load(is);

            is.close();

            driver = prop.getProperty("driver");
            url = prop.getProperty("url");
            user = prop.getProperty("user");
            password = prop.getProperty("password");

            //1 註冊驅動
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //1 獲得連接
    public static Connection getConnection(){
        Connection conn = null;
        try {
            //2 獲得連接
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("創建連接失敗!");
        }

        return conn;
    }

    //2 釋放資源
        //1> 參數可能爲空
        //2> 調用close方法要拋出異常,確保即使出現異常也能繼續關閉
        //3>關閉順序,需要從小到大
    public  static void  close(Connection conn , Statement st , ResultSet rs){

        try {
            if(rs!=null){
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            try {
                if(st!=null){
                st.close(); 
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                try {
                    if(conn!=null){
                        conn.close();   
                        }
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }


    public static void main(String[] args) {
        System.out.println(getConnection());
    }
}

SQL的注入問題

  • 早年登錄邏輯,就是把用戶在表單中輸入的用戶名和密碼 帶入如下sql語句. 如果查詢出結果,那麼 認爲登錄成功.
SELECT * FROM USER WHERE NAME='' AND PASSWORD='xxx';
  • 如果將用戶名以及密碼數據寫成如下的形式:
SELECT * FROM USER WHERE NAME='xxx' OR 1=1 -- ' and password='xxx';
  • 發現sql語句失去了判斷效果,條件部分成爲了恆等式.
  • 導致網站可以被非法登錄, 以上問題就是sql注入的問題.

那麼我們如何解決SQL注入問題?

  • 解決辦法:在運送sql時,我們使用的是Statement對象. 如果換成prepareStatement對象,那麼就不會出現該問題.
  • sql語句不要再直接拼寫.而要採用預編譯的方式來做.
  • 完成如上兩步.即可解決問題.
    *爲什麼使用PrepareStatement對象能解決問題?
    sql的執行需要編譯. 注入問題之所以出現,是因爲用戶填寫 sql語句 參與了編譯.  使用PrepareStatement對象
    在執行sql語句時,會分爲兩步. 第一步將sql語句 "運送" 到mysql上編譯.  再回到 java端 拿到參數 運送到mysql端.
    用戶填寫的 sql語句,就不會參與編譯. 只會當做參數來看. 避免了sql注入問題;
    PrepareStatement 在執行 母句相同, 參數不同的 批量執行時. 因爲只會編譯一次.節省了大量編譯時間.效率會高.

演示向mysql中存放大文本文件

public class Demo {
    @Test
    //演示向mysql中存放大文本數據
    //存儲大文本必須使用PrepareStatement對象
    public void fun1() throws Exception{

        //1 獲得連接
        Connection conn = JDBCUtils.getConnection();
        //2 書寫sql
        String sql = "insert into mytext values(null,?)";
        //3 創建PrepareStatement
        PreparedStatement ps = conn.prepareStatement(sql);
        //4 設置參數
        //參數1:參數的索引
        //參數2:需要保存的文本的流
        //參數3:文件長度

        File f = new File("src/text.txt");

        FileReader reader = new FileReader(f);

        ps.setCharacterStream(1, reader, (int)f.length());

        //5 執行sql
        int result = ps.executeUpdate();
        System.out.println(result);
        //6關閉資源
        JDBCUtils.close(conn, ps, null);
        }

}

通過JDBC存儲二進制文件

public class Demo {
    @Test
    //演示向mysql中存放圖片
    //存儲圖片必須使用PrepareStatement對象
    public void fun1() throws Exception{

        //1 獲得連接
        Connection conn = JDBCUtils.getConnection();
        //2 書寫sql
        String sql = "insert into myblob values(null,?)";
        //3 創建PrepareStatement
        PreparedStatement ps = conn.prepareStatement(sql);
        //4 設置參數
        //參數1:參數的索引
        //參數2:需要保存的圖片的流
        //參數3:圖片文件長度

        File f = new File("src/wg.PNG");

        InputStream  is = new FileInputStream(f);

        ps.setBinaryStream(1, is, (int)f.length());

        //5 執行sql
        int result = ps.executeUpdate();
        System.out.println(result);
        //6關閉資源
        JDBCUtils.close(conn, ps, null);
        }

}

批量執行MYSQL語句

public class Demo {
    @Test
    //1 使用Statement對象批量執行sql
    public void fun1() throws Exception{

        //1 獲得連接
        Connection conn = JDBCUtils.getConnection();
        //2 獲得Statement
    Statement st =  conn.createStatement();
        //3 添加多條sql語句到st中

    st.addBatch("create table t_stu ( id int primary key auto_increment , name varchar(20) )");
    st.addBatch("insert into t_stu values(null,'tom')");
    st.addBatch("insert into t_stu values(null,'jerry')");
    st.addBatch("insert into t_stu values(null,'jack')");
    st.addBatch("insert into t_stu values(null,'rose')");
        //4 執行sql
    int[]  results = st.executeBatch();
    System.out.println(Arrays.toString(results));
        //5關閉資源
        JDBCUtils.close(conn, st, null);
        }
    @Test
    //2 使用PrepareStatement對象批量執行sql
    public void fun2() throws Exception{

        //1 獲得連接
        Connection conn = JDBCUtils.getConnection();
        //2 書寫sql語句
        String sql = "insert into t_stu values(null,?)";
        //3 創建PrepareStatement
        PreparedStatement ps = conn.prepareStatement(sql);
        //4 循環.添加參數
        for(int i=0;i<100;i++){
            ps.setString(1, "用戶"+i);
            ps.addBatch();
        }
        //5 批量執行
        int[]  results =ps.executeBatch();
        System.out.println(Arrays.toString(results));
        //5關閉資源
        JDBCUtils.close(conn, ps, null);
        }
}
發佈了36 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章