一、什麼是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驅動纔是接口的實現,沒有驅動沒法完成數據庫鏈接!每一個數據庫廠商都有本身的驅動,用來鏈接本身公司的數據庫。工具
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(); } } }
四、工具類的抽取
將重複的加載驅動和獲取鏈接和關閉資源封裝到工具類,方便使用
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(); } } } }
一、在上面的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 這個方法會在測試方法以後執行 用來關閉資源
Database Access Object
把對數據庫進行的JDBC操做(增、刪、改、查) 都放在一個類中,用不一樣的方法分別來完成增、刪、改、查。
一張數據庫表作一個實體類,數據庫的列是類的屬性,數據庫的一行數據是類的一個對象