JDBC入門與簡單使用

weixin_34007886發表於2018-05-30

前面介紹過資料庫的建立,與SQL語句的增刪改查,但是Java是如何連線資料庫的呢?當然是我們的JDBC了,本篇總結了Java通過JDBC連線資料庫的基本寫法。這裡不細講JDBC的原理,記住,JDBC操作的不是資料庫,而是資料庫的驅動,至於不同驅動由不同的資料庫的廠商提供。

匯入驅動jar包並且註冊驅動

匯入驅動

在工程下新建lib目錄,然後將mysql-connector-java-5.1.37-bin.jar驅動jar包匯入,然後新增到build path即可。

註冊驅動

核心程式碼

         Class.forName("com.mysql.jdbc.Driver");

JDBC規範定義驅動介面:java.sql.Driver,MySql驅動包提供了實現類:com.mysql.jdbc.Driver
DriverManager工具類,提供註冊驅動的方法 registerDriver(),方法的引數是java.sql.Driver,所以我們可以通過如下語句進行註冊:

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

以上程式碼不推薦使用,存在兩方面不足

  1. 硬編碼,後期不易於程式擴充套件和維護
  2. 驅動被註冊兩次。
    通常開發我們使用Class.forName() 載入一個使用字串描述的驅動類。
    如果使用Class.forName()將類載入到記憶體,該類的靜態程式碼將自動執行。
    通過查詢com.mysql.jdbc.Driver原始碼,我們發現Driver類“主動”將自己進行註冊
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
……
}

連線資料庫

核心程式碼

Connection con = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb”,”root”,”root”);

獲取連線需要方法 DriverManager.getConnection(url,username,password),三個引數分別表示,url 需要連線資料庫的位置(網址) user使用者名稱 password 密碼
url比較複雜,下面是mysql的url:

jdbc:mysql://localhost:3306/mydb

JDBC規定url的格式由三部分組成,每個部分中間使用冒號分隔。
第一部分是jdbc,這是固定的;
第二部分是資料庫名稱,那麼連線mysql資料庫,第二部分當然是mysql了;
第三部分是由資料庫廠商規定的,我們需要了解每個資料庫廠商的要求,mysql的第三部分分別由資料庫伺服器的IP地址(localhost)、埠號(3306),以及DATABASE名稱(mydb)組成。

獲取可執行SQL語句的物件

String sql = "某SQL語句";
Statement stmt = con.createStatement();

常用方法:
int executeUpdate(String sql); --執行insert update delete語句.
ResultSet executeQuery(String sql); --執行select語句.
boolean execute(String sql); --執行select返回true 執行其他的語句返回false.

處理結果集(執行insert、update、delete無需處理)

ResultSet實際上就是一張二維的表格,我們可以呼叫其boolean next()方法指向某行記錄,當第一次呼叫next()方法時,便指向第一行記錄的位置,這時就可以使用ResultSet提供的getXXX(int col)方法(與索引從0開始不同個,列從1開始)來獲取指定列的資料:

rs.next();//指向第一行
rs.getInt(1);//獲取第一行第一列的資料

常用方法:

  • Object getObject(int index) / Object getObject(String name) 獲得任意物件
  • String getString(int index) / Object getObject(String name) 獲得字串
  • int getInt(int index) / Object getObject(String name) 獲得整形
  • double getDouble(int index) / Object getObject(String name) 獲得雙精度浮點型

釋放資源

與IO流一樣,使用後的東西都需要關閉!關閉的順序是先得到的後關閉,後得到的先關閉。

rs.close();
stmt.close();
con.close();

如何解決SQL語句注入問題

什麼注入問題

假設有登入案例SQL語句如下:

SELECT * FROM 使用者表 WHERE NAME = 使用者輸入的使用者名稱 AND PASSWORD = 使用者輸的密碼;

此時,當使用者輸入正確的賬號與密碼後,查詢到了資訊則讓使用者登入。但是當使用者輸入的賬號為XXX 密碼為:XXX’ OR ‘a’=’a時,則真正執行的程式碼變為:

SELECT * FROM 使用者表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’  OR ’a’=’a’;

此時,上述查詢語句時永遠可以查詢出結果的。那麼使用者就直接登入成功了,顯然我們不希望看到這樣的結果,這便是SQL隱碼攻擊問題。
為此,我們使用PreparedStatement來解決對應的問題。

如何解決

使用PreparedStatement預處理物件時,建議每條sql語句所有的實際引數,都使用逗號分隔。

String sql = "insert into sort(sid,sname) values(?,?)";;
PreparedStatement預處理物件程式碼:
PreparedStatement psmt = conn.prepareStatement(sql)

常用方法:

  1. 執行SQL語句:
  • int executeUpdate(); --執行insert update delete語句.
  • ResultSet executeQuery(); --執行select語句.
  • boolean execute(); --執行select返回true 執行其他的語句返回false.
  1. 設定實際引數
  • void setXxx(int index, Xxx xx) 將指定引數設定為給定Java的xx值。在將此值傳送到資料庫時,驅動程式將它轉換成一個 SQL Xxx型別值。
    例如:
  • setString(2, "家用電器") 把SQL語句中第2個位置的佔位符? 替換成實際引數 "家用電器"

詳解預處理物件executeUpdate方法

通過預處理物件的executeUpdate方法,完成記錄的insert\update\delete語句的執行。操作格式統一如下:
1. 註冊驅動
2. 獲取連線
3. 獲取預處理物件
4. SQL語句佔位符設定實際引數
5. 執行SQL語句
6. 釋放資源

插入記錄:insert

實現向分類表中插入指定的新分類

public void demo01() throws Exception {
        // 1註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2獲取連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
        // 3獲得預處理物件
        String sql = "insert into sort(sname) values(?)";
        PreparedStatement stat = conn.prepareStatement(sql);
        // 4 SQL語句佔位符設定實際引數
        stat.setString(1, "奢侈品");
        // 5執行SQL語句
        int line = stat.executeUpdate();
        System.out.println("新新增記錄數:" + line);
        // 6釋放資源
        stat.close();
        conn.close();
    }
更新記錄:update

實現更新分類表中指定分類ID所對應記錄的分類名稱

public void demo02() throws Exception {
        // 1註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2獲取連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
        // 3獲得預處理物件中
        String sql = "update sort set sname=? where sid=?";
        PreparedStatement stat = conn.prepareStatement(sql);
        // 4 SQL語句佔位符設定實際引數
        stat.setString(1, "數碼產品");
        stat.setInt(2, 1);
// 5執行SQL語句
        int line = stat.executeUpdate();
        System.out.println("更新記錄數:" + line);
        // 6釋放資源
        stat.close();
        conn.close();
    }
刪除記錄:delete

實現刪除分類表中指定分類ID的記錄

public void demo03() throws Exception {
        // 1註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2獲取連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
        // 3獲得預處理物件
        String sql = "delete from sort where sid=?";
        PreparedStatement stat = conn.prepareStatement(sql);
        // 4 SQL語句佔位符設定實際引數
        stat.setInt(1, 1);
// 5執行SQL語句
        int line = stat.executeUpdate();
        System.out.println("刪除記錄數:" + line);
        // 6釋放資源
        stat.close();
        conn.close();
    }

詳解預處理物件executeQuery方法

通過預處理物件的executeQuery方法,完成記錄的select語句的執行。操作格式統一如下:
1. 註冊驅動
2. 獲取連線
3. 獲取預處理物件
4. SQL語句佔位符設定實際引數
5. 執行SQL語句
6. 處理結果集(遍歷結果集合)
7. 釋放資源

查詢記錄:select

  • 實現查詢分類表所有記錄
public void demo04() throws Exception {
        // 1註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2獲取連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
        // 3獲得預處理物件
        String sql = "select * from sort";
        PreparedStatement stat = conn.prepareStatement(sql);
        // 4 SQL語句佔位符設定實際引數
    // 5執行SQL語句
        ResultSet rs = stat.executeQuery();
        // 6處理結果集(遍歷結果集合)
        while( rs.next() ){
            //獲取當前行的分類ID
            String sid = rs.getString("sid");//方法引數為資料庫表中的列名
            //獲取當前行的分類名稱
            String sname = rs.getString("sname");
            //顯示資料
            System.out.println(sid+"-----"+sname);
        }
        // 7釋放資源
        rs.close();
        stat.close();
        conn.close();
    }
  • 實現查詢分類表中指定分類名稱的記錄
public void demo05() throws Exception {
        // 1註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2獲取連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
        // 3獲得預處理物件
        String sql = "select * from sort where sname=?";
        PreparedStatement stat = conn.prepareStatement(sql);
        // 4 SQL語句佔位符設定實際引數
        stat.setString(1, "奢侈品");
// 5執行SQL語句
        ResultSet rs = stat.executeQuery();
        // 6處理結果集(遍歷結果集合)
        while( rs.next() ){
            //獲取當前行的分類ID
            String sid = rs.getString("sid");//方法引數為資料庫表中的列名
            //獲取當前行的分類名稱
            String sname = rs.getString("sname");
            //顯示資料
            System.out.println(sid+"-----"+sname);
        }
        // 7釋放資源
        rs.close();
        stat.close();
        conn.close();
    }

JDBC程式碼優化

上面發現有許多重複的程式碼,所以可以將注入連線操作進行封裝,並且可以將一些配置寫入一個配置檔案中。

JDBC工具類

“獲得資料庫連線”操作,將在以後的增刪改查所有功能中都存在,可以封裝工具類JDBCUtils。提供獲取連線物件的方法,從而達到程式碼的重複利用。
該工具類提供方法:public static Connection getConn ()。程式碼如下:

public class JDBCUtils {
    public static final  String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final  String URL = "jdbc:mysql://localhost:3306/mydb";
    public static final  String USER = "root";
    public static final  String PASSWORD = "root";

    static {
        try {
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            System.out.println("資料庫驅動註冊失敗!");
        }
    }
    //提供獲取連線的方法
    public static Connection getConn() throws Exception {
        // 2. 獲得連線
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        // 返回連線
        return conn;
    }
}
properties配置檔案
  • 使用properties配置檔案
    開發中獲得連線的4個引數(驅動、URL、使用者名稱、密碼)通常都存在配置檔案中,方便後期維護,程式如果需要更換資料庫,只需要修改配置檔案即可。
    通常情況下,我們習慣使用properties檔案,此檔案我們將做如下要求:
  1. 檔案位置:任意,建議src下
  2. 檔名稱:任意,副檔名為properties
  3. 檔案內容:一行一組資料,格式是“key=value”.
    key命名自定義,如果是多個單詞,習慣使用點分隔。例如:jdbc.driver
    value值不支援中文,如果需要使用非英文字元,將進行unicode轉換。
  • 建立配置檔案
    在專案跟目錄下,建立檔案,輸入“db.properties”檔名。
    檔案中的內容

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root

  • 載入配置檔案:Properties物件
    對應properties檔案處理,開發中也使用Properties物件進行。我們將採用載入properties檔案獲得流,然後使用Properties物件進行處理。
    JDBCUtils.java中編寫程式碼
public class JDBCUtils {

    private static String driver;
    private static String url;
    private static String user;
    private static String password;
    // 靜態程式碼塊
    static {
        try {
            // 1 使用Properties處理流
            // 使用load()方法載入指定的流
            Properties props = new Properties();
            Reader is = new FileReader("db.properties");
            props.load(is);
            // 2 使用getProperty(key),通過key獲得需要的值,
            driver = props.getProperty("driver");
            url = props.getProperty("url");
            user = props.getProperty("user");
            password = props.getProperty("password");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 獲得連線
     */
    public static Connection getConnection() {
        try {
            // 1 註冊驅動
            Class.forName(driver);
            // 2 獲得連線
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
使用JDBCUtils工具類

測試程式碼:

public class Demo {
    @Test
    public void insert(){
        try{
            //1,獲取連線物件
            Connection conn = JDBCUtils.getConnection();
            //2,指定要執行的SQL語句
            String sql = "INSERT INTO zhangwu(name,money,parent) VALUES(?,?,?)";
            //4,獲取SQL語句的執行物件 PreparedStatement
            PreparedStatement ppstat = conn.prepareStatement(sql);
            //5,執行SQL語句
            ppstat.setString(1, "股票收入");
            ppstat.setDouble(2, 5000);
            ppstat.setString(3, "收入");
            int line = ppstat.executeUpdate();
            //6,處理結果集
            System.out.println("line=" + line);
            //7,關閉連線
            ppstat.close();
            conn.close();
        } catch(SQLException e){
            throw new RuntimeException(e);
        }
    }
}

相關文章