JDBC、單元測試、DAO模式

JDBC簡介

一、什麼是JDBC?java

  JDBC(Java DataBase Connectivity,java數據庫鏈接)是一種用於執行SQL語句的Java API(工具)。JDBC是Java訪問數據庫的標準規範。mysql

  規範:在java中的直接體現是接口sql

  做用:爲不一樣關係型數據庫提供統一的訪問,由一組用java語言編寫的接口和工具類組成,由各大數據庫廠商實現對應接口數據庫

二、鏈接數據庫時要先加載驅動緩存

  什麼是驅動?安全

  兩個設備要進行通訊時,須要知足必定通訊數據格式,數據格式由設備提供商規定,設備提供商爲設備提供驅動軟件,經過軟件能夠與該設備進行通訊。服務器

  Java和數據庫要想進行連接,必須提早規定一些數據格式,格式由數據庫廠商實現。ide

  mysql鏈接工具下載地址:https://dev.mysql.com/downloads/connector/j/函數

三、JDBC是接口,而JDBC驅動纔是接口的實現,沒有驅動沒法完成數據庫鏈接!每一個數據庫廠商都有本身的驅動,用來鏈接本身公司的數據庫。工具

JDBC鏈接詳解

1.經過JDBC鏈接數據庫須要五步

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Demo01 {

    public static void main(String[] args) throws Exception {
        //一、加載驅動
        Class.forName("com.mysql.cj.jdbc.Driver");
        //二、建立鏈接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8", "root", "root");
        //三、編譯sql語句 編譯器
        Statement statement = conn.createStatement();
        String sql = "select * from test";
        //四、發送sql 並接受結果
        ResultSet eq = statement.executeQuery(sql);
        //根據列名獲取數據 迭代器
        while(eq.next()) {
            int sid = eq.getInt(1);
            String name = eq.getString(2);
            int chinese = eq.getInt("chinese");
            int math = eq.getInt("math");
            System.out.println(sid+"\t"+name+"\t"+chinese+"\t"+math);
        }
        //五、關閉資源 
        eq.close();
        statement.close();
        conn.close();
    }

}

二、詳解每一步

  一、Class.forName("com.mysql.cj.jdbc.Driver"); //這裏寫下載的鏈接工具包中的驅動路徑

    經過反射,創建驅動的對象

  二、Connection conn = DriverManager.getConnection(url,username,password);  //獲取鏈接

  url="jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8" 

  協議,有嚴格書寫規範   主協議名:子協議名://主機名:端口號/數據庫名  在鏈接工具到了6.0以後須要設置時區

    serverTimezone是時區,若是設定serverTimezone=UTC,會比中國時間早8個小時,設定中國則爲GMT%2B8

  username = "root", password =  "root"  數據庫的用戶名和密碼

  三、獲取發送sql語句的對象

  Statement statement = conn.createStatement();

  四、發送sql,執行並接受結果

  executeQuery(String sql),只能執行select語句,若是執行insert into,update,delete from 都會報錯,會把SQL語句傳遞給mysql數據庫去執行

  executeUpdate(String sql),執行更新的SQL語句。只能執行insert into,update,delete from,若是執行select語句會報錯,會把SQL語句傳遞給mysql數據庫去執行

  execute(String sql)  執行任意語句,執行select 返回true,執行insert into,update,delete from  返回false

  五、釋放資源

  eq.close();statement.close();conn.close();

三、JDBC的增刪改查

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;

public class Demo2 {

    public static void main(String[] args) throws Exception{
        Connection conn=null;
        Scanner sc=new Scanner(System.in);
        PreparedStatement ps =null;
        ResultSet eq =null;
        Class.forName("com.mysql.cj.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql:///db_701?serverTimezone=GMT%2B8", "root", "ccc");
        login(conn,sc,eq,ps);
        
    }
    public static void login(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
        ps = conn.prepareStatement("select username,pwd from users where id='1'");
        eq = ps.executeQuery();
        int flag=1;
        if(eq.next()) {
            do{
                System.out.println("請輸入用戶名:");
                String s1 = sc.nextLine();
                String s2 = eq.getString(1);
                System.out.println(s2);
                if(s1.equals(s2)) {
                    System.out.println("請輸入密碼:");
                    String pwd1 = sc.nextLine();
                    String pwd2 = eq.getString(2);
                    if(pwd1.equals(pwd2)) {
                        System.out.println("登陸成功,歡迎使用");
                        flag=0;
                    }else {
                        System.out.println("用戶名密碼錯誤 ,請從新輸入");
                        continue;
                    }
                }else {
                    System.out.println("用戶名不存在,請從新輸入");
                    continue;
                }
            }while(flag==1);
            test(conn,sc,eq,ps);
        }
    }
    public static void test(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
        System.out.println("請選擇功能:一、添加  二、刪除  三、修改  四、查詢  0、退出");
        int num =Integer.parseInt(sc.nextLine());
        switch(num){
            case 1:
                add(conn,sc,eq,ps);
                break;
            case 2:
                delete(conn,sc,eq,ps);
                break;
            case 3:
                alter(conn,sc,eq,ps);
                break;
            case 4:
                select(conn,sc,eq,ps);
                break;
            case 5 :
                System.out.println("謝謝您的使用,再見");
                after(conn,sc,eq,ps);
                break;
        }
    }
    public static void add(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
        ps = conn.prepareStatement("insert into users values(null,?,?)");
        System.out.println("請輸入用戶名:");
        ps.setString(1, sc.nextLine());
        int flag=1;
        while(flag==1) {
            System.out.println("請輸入密碼 :");
            String pwd1 = sc.nextLine();
            System.out.println("確認密碼:");
            String pwd2 = sc.nextLine();
            if(pwd1.equals(pwd2)) {
                ps.setString(2, pwd2);
                flag=0;
            }else {
                System.out.println("兩次密碼輸入不一致,請從新輸入密碼 ");
                continue;
            }
        }
        int count=ps.executeUpdate();
        if(count>0) {
            System.out.println("添加成功");
        }else {
            System.out.println("添加失敗");
        }
        test2(conn,sc,eq,ps);    
    }
    public static void delete(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
        ps = conn.prepareStatement("delete from users where username=? and pwd=?");
        int flag=1;
        while(flag==1) {
            System.out.println("請輸入想要刪除的用戶名:");
            String s = sc.nextLine();
            ps.setString(1, s);
            System.out.println("請輸入此用戶的密碼 :");
            String pwd1 = sc.nextLine();
            ps.setString(2, pwd1);
            int count=ps.executeUpdate();
            if(count>0) {
                System.out.println("刪除成功");
                flag=0;
            }else {
                System.out.println("用戶名密碼錯誤,刪除失敗  1.繼續刪除  0.返回 ");
                String s1 = sc.nextLine();
                if(s1.equals("1")) {
                    continue;
                }else {
                    flag=0;
                    System.out.println("正在返回上一級");
                }
            }
        }
        test2(conn,sc,eq,ps);    
    }
    public static void alter(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
        ps = conn.prepareStatement("update users set pwd=? where username=? and pwd=?");
        int flag=1;
        int flag1=1;
        while(flag==1) {
            System.out.println("請輸入想要修改密碼的用戶名:");
            String s1 = sc.nextLine();
            ps.setString(2, s1);
            System.out.println("請輸入原密碼 ");
            String pwd = sc.nextLine();
            ps.setString(3, pwd);
            
            while(flag1==1) {
                System.out.println("請輸入修改的密碼 :");
                String pwd1 = sc.nextLine();
                System.out.println("確認密碼:");
                String pwd2 = sc.nextLine();
                if(pwd1.equals(pwd2)) {
                    ps.setString(1, pwd2);
                    flag1=0;
                }else {
                    System.out.println("兩次密碼輸入不一致,請從新輸入 ");
                    continue;
                }
            }
            int count=ps.executeUpdate();
            if(count>0) {
                System.out.println("修改爲功");
                flag=0;
            }else {
                System.out.println("修改失敗");
                System.out.println("用戶名密碼錯誤,修改失敗  1.繼續修改  0.返回 ");
                String s3 = sc.nextLine();
                if(s3.equals("1")) {
                    continue;
                }else {
                    flag=0;
                    System.out.println("正在返回上一級");
                }
                
            }
        }
        test2(conn,sc,eq,ps);
    }
    public static void select(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception {
        System.out.println("1.查詢所有  2.指定用戶名查詢");
        String s = sc.nextLine();
        if(s.equals("2")) {
            ps = conn.prepareStatement("select * from users where username=?");
            System.out.println("請輸入要查詢的用戶名:");
            String s1 = sc.nextLine();
            ps.setString(1, s1);
            eq = ps.executeQuery();
        }else if(s.equals("1")) {
            ps = conn.prepareStatement("select * from users");
            eq = ps.executeQuery();    
        }
        if(eq.next()) {
            do{
                String username = eq.getString(2);
                String pwd = eq.getString(3);
                System.out.println(username+"\t"+pwd);
            }while(eq.next()) ;
        }else {
            System.out.println("查詢失敗");
        }
        test2(conn,sc,eq,ps);
    }
    public static void test2(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
        System.out.println("1.繼續  0.退出");
        String s3 = sc.nextLine();
        if(s3.equals("1")) {
            test(conn,sc,eq,ps);
        }else {
            System.out.println("謝謝您的使用,再見");
            after(conn,sc,eq,ps);
        }
    }
    public static void after(Connection conn,Scanner sc,ResultSet eq,PreparedStatement ps) throws Exception{
        if(conn!=null) {
            conn.close();
        }
        if(ps!=null) {
            ps.close();
        }
        if(sc!=null) {
            sc.close();
        }
        if(eq !=null) {
            eq.close();
        }
    }
}
JDBC增刪改查練習之用戶管理系統mini

四、工具類的抽取

  將重複的加載驅動和獲取鏈接和關閉資源封裝到工具類,方便使用

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCUtils {
    public static final String driver = "com.mysql.cj.jdbc.Driver";
    public static final String url = "jdbc:mysql://localhost:3306/db_test?serverTimezone=GMT%2B8";
    public static final String user = "root";
    public static final String password="root";
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
    }
    public static Connection getConn() {
        try {
            return DriverManager.getConnection(url,user,password);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static void closeAll(ResultSet rs,Statement st,Connection conn) {
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
工具類

SQL注入問題

一、在上面的JDBC使用中,存在着一個安全問題,那就是SQL的注入問題

  這個問題主要發生在編譯對象身上,由於statement對象每次都是將字符串拼接完畢以後才發送給數據庫執行,所以就產生了安全問題。

  eg:用戶登陸  假設用戶名密碼必須是root 和root才能登陸得到查看結果

  sql語句爲  String sql = "select * from users where username='"+username+"' and password='"+password+"'";

  username  和 password須要用戶輸入

  注入問題以下:

  username = a' or 'a'='a;

  password = a' or 'a'='a;

  此時sql語句變爲 select * from users where username = 'a' or 'a'='a' and password='a' or 'a'='a'

  雖然用戶名密碼均不正確,但依然能夠登陸成功進行查看,這就是SQL注入問題

二、那麼該如何解決呢?

  使用預編譯對象 preparedStatement

  PreparedStatement與Statement的區別:

    前者對sql語句進行了預編譯

    PreparedStatement將語句先送回數據庫

    使語句的邏輯已經肯定爲select * from users where username=? and password=?

    此時用戶若是輸入一樣的內容,卻沒法改變原有的邏輯,會產生語法錯誤,因此避免了注入問題

  且預編譯對象在使用同一條語句傳不一樣內容時,也只需編譯一次,大大提升了效率

三、PreparedStatement的預編譯是數據庫進行的,編譯後的函數key是緩存在PreparedStatement中的,編譯後的函數是緩存在數據庫服務器中的。預編譯前有檢查sql語句語法是否正確的操做。只有數據庫服務器支持預編譯功能時,JDBC驅動纔可以使用數據庫的預編譯功能,不然會報錯。

  jdbc:mysql://localhost:3306/db?useServerPrepStmts=true  能夠設置數據庫開啓預編譯

四、使用不一樣的PreparedStatement對象來執行相同的SQL語句時,仍是會出現編譯兩次的現象,這是由於驅動沒有緩存編譯後的函數key,致使二次編譯。若是但願緩存編譯後函數的key,那麼就要設置cachePrepStmts參數爲true

  jdbc:mysql://localhost:3306/db?useServerPrepStmts=true&cachePrepStmts=true

五、PreparedStatement的預編譯還有注意的問題,在數據庫端存儲的函數和在PreparedStatement中存儲的key值,都是創建在數據庫鏈接的基礎上的,若是當前數據庫鏈接斷開了,數據庫端的函數會清空,創建在鏈接上的PreparedStatement裏面的函數key也會被清空,各個鏈接之間的預編譯都是互相獨立的。

六、PreparedStatement的使用

setInt(int index,int value)

爲?佔位符,賦予int值

setString(int index,String value)

爲?佔位符,賦予String值

setObject(int index,Object value)

 

executeQuery()

執行查詢的SQL語句。

只能執行select語句,

若是執行insert into,update,delete from 都會報錯

會把SQL語句傳遞給mysql數據庫去執行

 

返回結果:ResultSet對象---一張二維表格

 

executeUpdate()

執行更新的SQL語句。

只能執行insert into,update,delete from

若是執行select語句會報錯

會把SQL語句傳遞給mysql數據庫去執行

 

返回結果:int   SQL語句執行後更新了幾行數據

 

上面的代碼練習中採用的就是PreparedStatement。

單元測試

一、導包 右擊項目-->prooerties-->java build path -->libraries-->add library-->JUint-->next finish

二、給想要測試的方法加上註解(在上方加上 @Test)

三、能夠在其餘方法上加@Before 這個方法會在測試方法以前執行  用來加載資源

   @After 這個方法會在測試方法以後執行   用來關閉資源

DAO模式

Database  Access  Object

把對數據庫進行的JDBC操做(增、刪、改、查) 都放在一個類中,用不一樣的方法分別來完成增、刪、改、查。

一張數據庫表作一個實體類,數據庫的列是類的屬性,數據庫的一行數據是類的一個對象

相關文章
相關標籤/搜索