摘要:帶再愛你一次一入門概述數據庫連接,,簡稱是語言中用來規(guī)范客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。是面向關系型數據庫的。
BWH_Steven :帶JDBC再愛你一次 (一) JDBC 入門 (1) 概述
Java數據庫連接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規(guī)范客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。JDBC也是Sun Microsystems的商標。JDBC是面向關系型數據庫的。
簡單解釋: 通過Java語言執(zhí)行sql語句,從而操作數據庫
(2) 來由想要通過Java操作不同的數據庫,應該根據數據庫的不同而執(zhí)行特定的API,而出于簡化的想法,Sun公司,定義了一套面向所有關系型數據庫的 API 即 JDBC ,其只提供接口,而具體實現去交給數據庫廠商實現,而我們作為開發(fā)者,我們針對數據數據庫的操作,只需要基于JDBC即可
(二) 簡單使用 JDBC我們簡單的使用JDBC去查詢數據庫中的數據,并且輸出到控制臺中
為了快速演示,我們新建一張非常簡單的表
CREATE TABLE student( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), score DOUBLE(4,1) ); INSERT student(id,NAME,score) VALUES (1,"張三",98); INSERT student(id,NAME,score) VALUES (2,"李四",96); INSERT student(id,NAME,score) VALUES (3,"王五",100);
我們根據數據庫中的信息寫一個對應的學生類
public class Student { private int id; private String name; private double score; //省略構造、Get、Set、toString方法 ...... }
下面是對 JDBC 查詢功能的簡單使用
package cn.ideal.jdbc; import cn.ideal.domain.Student; import java.sql.*; public class JdbcDemo { public static void main(String[] args) { //導入數據庫驅動包 Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { //加載驅動 Class.forName("com.mysql.jdbc.Driver"); //獲取與數據庫的連接對象 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root99"); //定義sql語句 String sql = "SELECT * FROM student"; //獲取執(zhí)行sql語句的對象statement statement = connection.createStatement(); //執(zhí)行sql語句,獲取結果集 resultSet = statement.executeQuery(sql); //遍歷獲取到的結果集 while (resultSet.next()) { int id = resultSet.getInt(1); String name = resultSet.getString(2); Double score = resultSet.getDouble(3); Student student = new Student(); student.setId(id); student.setName(name); student.setScore(score); System.out.println(student.toString()); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //釋放資源,后調用的先釋放 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } //運行結果 Student{id=1, name="張三", score=98.0} Student{id=2, name="李四", score=96.0} Student{id=3, name="王五", score=100.0}
下面我們開始詳細的解釋一下上面所用到的各個對象
(三) JDBC 對象詳解 (1) DriverManagerA:加載驅動 --> 注冊驅動
首先我們要知道加載驅動和注冊驅動這兩個詞是什么意思,剛剛接觸的時候,會有人總有朋友將Class.forName(com.mysql.jdbc.Driver) 當做注冊數據庫驅動的語句,但實際不然,它的作用是將參數表示的類加載到內存中,并且初始化,同時其中的靜態(tài)變量也會被初始化,靜態(tài)代碼塊也會被執(zhí)行
疑惑:能否使用ClassLoader 類中的loadClass()方法呢?
答案是否定的,這個方法的特點是加載但不對該類初始化
//Class類源碼節(jié)選 -jdk8 * A call to {@code forName("X")} causes the class named * {@code X} to be initialized.
關于初始化問題這里簡單提及一下,我們還是先回到我們主線來
為什么不對類進行初始化,就不能選擇了呢?
這是因為真正實現注冊驅動(告訴程序使用哪一個數據庫驅動jar)的是:
static void registerDriver(Driver driver)
我們在jar包中找到Driver這個類,查看其源碼
//com.mysql.jdbc.Driver類中的靜態(tài)代碼塊 static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can"t register driver!"); } }
類被加載后,執(zhí)行了類中的靜態(tài)方法DriverManager進行了注冊驅動
我們也可能有見過下面2中的代碼,但是實際上驅動會被加載兩次,因為執(zhí)行
new com.mysql.jdbc.Driver() 已經加載了一次驅動
//1.推薦 Class.forName("com.mysql.jdbc.Driver"); //2.不推薦 DriverManager.registerDriver(new com.mysql.jdbc.Driver())
那么何必這么麻煩呢?new com.mysql.jdbc.Driver() 直接這樣寫不就挺好了嗎?
但我們還是選擇 拒絕!為什么呢?
如果我們這樣寫,對于jar包的依賴就比較重了,我們如果面臨多個項目,或者需要修改數據庫,就需要修改代碼,重新編譯,但是如果使用Class類加載的方式,既保證了靜態(tài)代碼塊中所包含的注冊驅動方法會被執(zhí)行 ,而又將參數變成了字符串形式,我們之后便可以通過修改配置文件 “ ” 內的內容 + 添加jar包 的方式更靈活的處理問題,并且不需要重新編譯!
注意:mysql5之后的驅動jar包可以省略注冊驅動這一步,原因查看jar包中META-INF/services/java.sql.Driver文件
com.mysql.jdbc.Driver com.mysql.fabric.jdbc.FabricMySQLDriver
B:獲取數據庫連接
static Connection getConnection(String url, String user, String password) /* jdbc:mysql://ip地址(域名):端口號/數據庫名稱 Eg:jdbc:mysql://localhost:3306/db1 本地mysql,且端口為默認3306,則可簡寫:jdbc:mysql:///數據庫名稱 */(2) Connection (數據庫連接對象)
A:獲取執(zhí)行sql的對象
//創(chuàng)建向數據庫發(fā)送sql語句的statement對象 Statement createStatement() //創(chuàng)建向數據庫發(fā)送預編譯sql語句的PrepareStement對象 PreparedStatement prepareStatement(String sql)
B:管理事務
//開啟事務:設置參數為false,即開啟事務 setAutoCommit(boolean autoCommit) //提交事務 commit() //回滾事務 rollback()(3) Statement (執(zhí)行sql語句的對象)
//執(zhí)行DQL(查詢數據庫中表的記錄(數據)) ResultSet executeQuery(String sql) //執(zhí)行DML(對數據庫中表的數據進行增刪改) int executeUpdate(String sql) //執(zhí)行任意sql語句,但是目標不夠明確,較少使用 boolean execute(String sql) //把多條sql的語句放到同一個批處理中 addBatch(String sql) //向數據庫總發(fā)送一批sql語句執(zhí)行 executeBatch()
代碼演示(以增加一條數據為例)
package cn.ideal.jdbc; import java.sql.*; public class StatementDemo { public static void main(String[] args) { Connection connection = null; Statement statement = null; try { //加載驅動 Class.forName("com.mysql.jdbc.Driver"); //獲取數據庫連接對象 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root99"); //定義sql語句 String sql = "INSERT student(id,NAME,score) VALUES (NULL,"馬六",88);"; //獲取執(zhí)行sql語句的對象 statement = connection.createStatement(); //執(zhí)行sql語句 int count = statement.executeUpdate(sql); System.out.println(count); if (count > 0) { System.out.println("添加成功"); } else { System.out.println("添加失敗"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if(statement != null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }(4) ResultSet(結果集對象,封裝查詢結果)
ResultSet所代表的的是sql語句的結果集——執(zhí)行結果,當Statement對象執(zhí)行excuteQuery()后,會返回一個ResultSet對象
//游標向下移動一行,判斷當前行是否是最后一行末尾(是否有數據) //如果是,則返回false,如果不是則返回true boolean next() //獲取數據,Xxx代表數據類型 getXxx(參數) Eg:int getInt() , String getString() 1. int:代表列的編號,從1開始 如: getString(1) 2. String:代表列名稱。 如: getDouble("name")
案例可參考開頭快速使用部分,自行嘗試讀取數據庫中數據后用集合框架裝載
(四) 事半功倍——工具類通過封裝一些方法,使得出現一個更加通用的工具類,我們可以通過properties配置文件 ,使得信息更加直觀且容易維護
package cn.ideal.jdbc; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.util.Properties; public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件讀取 */ static { try { //創(chuàng)建Properties集合類 Properties pro = new Properties(); //獲取src路徑下的文件 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); //加載文件 pro.load(new FileReader(path)); //獲取數據 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //注冊驅動 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 獲取連接 * * @return 連接對象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 釋放資源 * * @param statement * @param connection */ public static void close(Statement statement, Connection connection) { if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 釋放資源 * * @param resultSet * @param statement * @param connection */ public static void close(ResultSet resultSet, Statement statement, Connection connection) { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
工具類測試類
package cn.ideal.jdbc; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class JDBCUtilsTest { public static void main(String[] args) { Connection connection = null; Statement statement = null; try { connection = JDBCUtils.getConnection(); //定義sql語句 String sql = "INSERT student(id,NAME,score) VALUES (NULL,"馬六",88)"; //獲取執(zhí)行sql語句的對象 statement = connection.createStatement(); //執(zhí)行sql語句 int count = statement.executeUpdate(sql); System.out.println(count); if (count > 0) { System.out.println("添加成功"); } else { System.out.println("添加失敗"); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(statement,connection); } } }
之前的文章中分別通過集合實現、IO實現、而學習數據庫后,我們可以試著通過數據庫存儲數據,寫一個簡單的登錄注冊小案例!在第五大點中有提到吼
(五) 補充:PreparedStatment//創(chuàng)建向數據庫發(fā)送預編譯sql語句的prepareStatement PreparedStatement prepareStatement(String sql)
prepareStatement繼承自Statement,總而言之,它相較于其父類,更強更簡單!
(1) 優(yōu)點Statement 直接編譯 SQL 語句,直接送到數據庫去執(zhí)行,而且其多次重復執(zhí)行sql語句,PreparedStatement 會對SQL進行預編譯,再填充參數,這樣效率會比較高(預編譯的SQL存儲在PreparedStatement中)
定義 SQL 語句的時候,常常需要使用到 Java 中的變量,在一些復雜的情況下,需要頻繁的使用到引號和單引號的問題,變量越多,越復雜,而PreparedStatement可以使用占位符 ‘ ?’ 代替參數,接下來再進行參數的賦值,這樣有利于代碼的可讀性
PreparedStatement 由于預編譯,可以避免Statement中可能需要采取字符串與變量的拼接而導致SQL注入攻擊【編寫永等式,繞過密碼登錄】
我們先按照我們之前的做法,寫一個簡單的登錄Demo,先創(chuàng)一張表!
CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32), PASSWORD VARCHAR(32) ); SELECT * FROM USER; INSERT INTO USER VALUES(NULL,"admin","admin888"); INSERT INTO USER VALUES(NULL,"zhangsan","123456");
接著編寫代碼
package cn.ideal.login; import cn.ideal.jdbc.JDBCUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Scanner; public class LoginDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入用戶名"); String username = sc.nextLine(); System.out.println("請輸入密碼"); String password = sc.nextLine(); boolean flag = new LoginDemo().login(username, password); if (flag) { System.out.println("登錄成功"); } else { System.out.println("用戶名或密碼錯誤"); } } /** * 登錄方法 */ public boolean login(String username, String password) { if (username == null || password == null) { return false; } Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { connection = JDBCUtils.getConnection(); //定義sql String sql = "SELECT * FROM USER WHERE username = "" + username + "" AND password = "" + password + "" "; //獲取執(zhí)行sql的對象 statement = connection.createStatement(); //執(zhí)行查詢 resultSet = statement.executeQuery(sql); return resultSet.next(); } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtils.close(resultSet,statement, connection); } return false; } }
簡單的來說,這樣一個簡單的登錄Demo就寫好了,但是這個時候,SQL注入問題中的一種情況就出現了,或許你聽過,在早些年的時候,漏洞還是蠻常見的,一些黑客或者腳本小子們常常使用一些SQL注入的手段進行目標網站后臺的入侵,我們今天所講的這一種,就是其中一種,叫做SQL萬能注入(SQL萬能密碼)
我們先來觀察一下上述代碼中關于SQL語句的部分
String sql = "SELECT * FROM USER WHERE username = "" + username + "" AND password = "" + password + "" ";
也就是說它將我們所輸入的 username 和 password合成為SQL查詢語句, 當數據庫中不存在這樣的字段就代表輸入錯誤,但是對于存在SQL注入漏洞的程序,則可以通過構造一些特殊的字符串,達到登錄的目的,先貼出來測試結果
//運行結果 請輸入用戶名 admin 請輸入密碼 1" or "1" = "1 登錄成功
如果我們將上述代碼中密碼 (username) 部分用我們的這些內容代替是怎么樣的呢
String sql = "SELECT * FROM USER WHERE username = "admin" AND PASSWORD = "1" or "1" = "1" ";
補充:在SQL語句中邏輯運算符具有優(yōu)先級,= 優(yōu)先于 and ,and 優(yōu)先于 or
所以上面的式子中 AND先被執(zhí)行,當然返回錯,接著執(zhí)行or部分,對于一個永等式 ‘1’ = ‘1‘ 來說返回值永遠是true,所以SQL查詢結果為true,即可以登錄成功
//使用PrepareStemen替代主要部分 //定義sql String sql = "SELECT * FROM USER WHERE username = ? AND password = ?"; //獲取執(zhí)行sql的對象 preparedStatement = connection.prepareStatement(sql); //給?賦值 preparedStatement.setString(1, username); preparedStatement.setString(2, password); //執(zhí)行查詢 resultSet = preparedStatement.executeQuery(); //運行結果 請輸入用戶名 admin 請輸入密碼 1" or "1" = "1 用戶名或密碼錯誤
?
結尾:如果內容中有什么不足,或者錯誤的地方,歡迎大家給我留言提出意見, 蟹蟹大家 !^_^
如果能幫到你的話,那就來關注我吧?。ㄏ盗形恼戮鶗诠娞柕谝粫r間更新)
在這里的我們素不相識,卻都在為了自己的夢而努力 ?一個堅持推送原創(chuàng)Java技術的公眾號:理想二旬不止
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/75797.html
摘要:前言為了鞏固開發(fā)的流程,我們再拿一個客戶關系管理系統(tǒng)來練手成果圖我們完成的就是下面的項目搭建配置環(huán)境配置導入開發(fā)包建立開發(fā)用到的程序包在數據庫創(chuàng)建相對應的表開發(fā)實體開發(fā)實體十分簡單,對照著數據庫的表就行了各種開發(fā)獲取數據庫連接池的導入配置文 前言 為了鞏固開發(fā)的流程,我們再拿一個客戶關系管理系統(tǒng)來練手...! 成果圖 我們完成的就是下面的項目! showImg(https://segm...
摘要:第三部分對于參加工作年到年的同學。我當時看的是大話設計模式這本書,并且寫了完整版的設計模式博客。這一年,你必須對于設計模式了如指掌,大話設計模式可以作為你的開端。與此同時,這個階段你要做的事情還遠不止如此。 這一部分其實也算是今天的重點,這一部分用來回答很多群里的朋友所問過的問題,那就是大佬你是如何學習Java的,能不能給點建議? 今天我是打算來點干貨,因此咱們就不說一些學習方法和技巧...
摘要:國內互聯(lián)網巨頭百度也在近期表明,將發(fā)起建立一個名為深盟的分布式機器學習開源平臺,由旗下深度學習研究院牽頭,聯(lián)合來自卡耐基梅隴大學華盛頓大學紐約大學香港科技大學的多位系統(tǒng)開發(fā)者,共同推出旨在大幅降低機器深度學習門檻的蟲洞項目。 當前人工智能之所以能夠引起大家的興奮和廣泛關注,在很大程度上是源于深度學習的研究進展。這項機器學習技術為計算機視覺、語音識別和自然語言處理帶來了巨大的、激動人心的進步,...
閱讀 3777·2021-11-11 11:02
閱讀 3507·2021-10-11 10:57
閱讀 3620·2021-09-22 16:00
閱讀 1856·2021-09-02 15:15
閱讀 1342·2019-08-30 15:56
閱讀 1018·2019-08-30 15:54
閱讀 2745·2019-08-30 12:43
閱讀 3551·2019-08-29 16:06