JDBC小记
JDBC小記
前言
昨天剛沖完了jdbc,今天來總結
一.JDBC介紹
JDBC (百度百科): Java數據庫連接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規范客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。JDBC也是Sun Microsystems的商標。我們通常說的JDBC是面向關系型數據庫的。
說白了 JDBC就是Java數據庫連接,就是用Java語言來操作數據庫。原來我們操作數據庫是在控制臺使用SQL語句來操作數據庫,JDBC是用Java語言向數據庫發送SQL語句.
二.JDBC原理圖
三.JDBC的本質:
JDBC本質:其實是官方(sun公司)定義的一套操作所有關系型數據庫的規則,即接口。各個數據庫廠商去實現這套接口,提供數據庫驅動jar包。我們可以使用這套接口(JDBC)編程,真正執行的代碼是驅動jar包中的實現類。
JDBC到底是什么
SUN公司制定的一套接口(interface),(在java.sql.*;包下有很多接口)
目的:解耦合——>降低程序的耦合度,提高程序的擴展力
**附:**java鏈接MySQL驅動包最新版下載地址:https://dev.mysql.com/downloads/connector/j/,解壓后得到jar庫文件,然后在項目中導入該庫文件.
博主使用的版本是mysql-connector-java-5.1.47.jar
四.什么是驅動
驅動,是指驅動計算機里軟件的程序。驅動程序全稱設備驅動程序,是添加到操作系統中的特殊程序,其中包含有關硬件設備的信息。此信息能夠使計算機與相應的設備進行通信。驅動程序是硬件廠商根據操作系統編寫的配置文件,可以說沒有驅動程序,計算機中的硬件就無法工作。
例如:網卡,聲卡,顯卡等等 必須要安裝驅動程序,不然這些硬件就無法正常工作。
五.為什么要面向接口編程?
解耦合:降低程序的耦合度,提高程序的擴展力
多態機制就是非常典型的:面向抽象編程(不要面向具體編程)
思考:為什么sun公司要制定一套JDBC接口呢?
因為每個數據庫的實現原理不一樣,oracle、mysql、ms sqlserver都有自己的原理,每個數據庫產品都有自己獨特的實現原理.
六.JDBC核心類(接口)介紹
JDBC中的核心類有:DriverManager、Connection、Statement,和ResultSet
DriverManger(驅動管理器)的作用有兩個:
-
注冊驅動:這可以讓JDBC知道要使用的是哪個驅動;
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
-
獲取Connection:如果可以獲取到Connection,那么說明已經與數據庫連接上了。
DriverManager.getConnection(url,username,password)
Connection對象表示連接,與數據庫的通訊都是通過這個對象展開的:
-
Connection最為重要的一個方法就是用來獲取Statement對象;
Statement stmt = con.createStatement();
-
Statement是用來向數據庫發送SQL語句的,這樣數據庫就會執行發送過來的SQL語句
-
void executeUpdate(String sql):執行更新操作(insert、update、delete等);
-
ResultSet executeQuery(String sql):執行查詢操作,數據庫在執行查詢后會把查詢結果,查詢結果就是ResultSet;
ResultSet對象表示查詢結果集,只有在執行查詢操作后才會有結果集的產生。結果集是一個二維的表格,有行有列。操作結果集要學習移動ResultSet內部的“行光標”,以及獲取當前行上的每一列上的數據:
-
boolean next():使“行光標”移動到下一行,并返回移動后的行是否存在;
rs.next();//光標移動到第一行 rs.getInt(1);//獲取第一行第一列的數據 -
XXX getXXX(int col):獲取當前行指定列上的值,參數就是列數,列數從1開始,而不是0。
//常用的方法 Object getObject(int col) String getString(int col) int getInt(int col) double getDouble(int col)
七.JDBC編程6步(超級重要,需要背會!!!)
第1步:注冊驅動 (只做一次)
第2步:獲取數據庫連接對象(Connection)
第3步:獲取數據庫操作對象(Statement)
第4步:執行sql語句(增刪改查)
第5步:處理查詢結果集(ResultSet)
第6步:釋放資源
第1步:注冊驅動 (只做一次)
作用:告訴java程序,即將要連接的是哪個牌子的數據庫.
注冊驅動的兩種方法 (例如,注冊MySQL的數據庫驅動) :
// 第一種: 推薦 Class.forName(“com.mysql.jdbc.Driver”);Class.forName是把這個類加載到JVM中,加載的時候,就會執行其中的靜態初始化塊,完成驅動的初始化的相關工作。
// 第二種: 不推薦 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // DriverManager類的registerDriver()方法的參數是java.sql.Driver,但java.sql.Driver是一個接口,實現類由mysql驅動來提供,mysql驅動中的java.sql.Driver接口的實現類為com.mysql.jdbc.Driver
上面代碼雖然可以注冊驅動,但是出現硬編碼(代碼依賴mysql驅動jar包),如果將來想連接Oracle數據庫,那么必須要修改代碼的。并且其實這種注冊驅動的方式是注冊了兩次驅動!
通過初始化驅動類com.mysql.jdbc.Driver,該類就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle數據庫那么該驅動類將不同。
**注意:**Class.forName需要捕獲ClassNotFoundException.
try {Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace();}第2步:獲取數據庫連接對象(Connection)
表示jvm的進程和數據庫進程之間的通道打開了,這屬于進程之間的通信,重量級的,使用完一定要關閉
可以使用 DriverManager.getConnection()方法建立連接。根據傳入參數的不同,有三種重載的DriverManager.getConnection()方法:
- getConnection(String url)
- getConnection(String url, Properties prop)
- getConnection(String url, String user, String password)
還可以在url中提供參數:
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF8
useUnicode參數指定這個連接數據庫的過程中,使用的字節集是Unicode字節集;
characherEncoding參數指定穿上連接數據庫的過程中,使用的字節集編碼為UTF-8編碼。請注意,mysql中指定UTF-8編碼是給出的是UTF8,而不是UTF-8。
這里每個格式都需要一個數據庫URL。 數據庫**URL是指向數據庫的地址。**制定數據庫URL是建立連接相關聯的大多數錯誤問題發生的地方。各數據庫對應的URL如下所示:
假設我們現在需要連接MySQL數據庫,格式為:jdbc:mysql://hostname:port/datebaseName。我們需要的信息是hostname主機名和端口號,一般默認為localHost:3306;還需要datebaseName數據庫名,假設為mydb;當然還有URL格式未包含的也是必須的信息:連接數據庫的用戶名和密碼,假設為root和123456。那么就有URL:
- 1 String url = "jdbc:mysql//localhost:3306/mydb";
下面分別使用四種方法來實現:
-
使用一個URL作為參數的方式:需要將username+password以參數的形式放到URL中,但是每種數據庫的放置都不太相同
//連接mysql的純URL String url = "jdbc:mysql//localhost:3306/mydb?username=root&password=123456"; Connection conn = DriverManager.getConnection(url,p); //連接Oracle的純URL String url = "jdbc:oracle:thin:root/123456@192.0.0.10:1521:mydb";Connection conn = DriverManager.getConnection(url); -
使用URL、properties作為參數的方式:即需要將username和password以鍵值對形式存放在properties對象中作為參數
//MySql String url = "jdbc:mysql//localhost:3306/mydb"; Properties p = new Properties(); p.put("username","root"); p.put("password","123456"); Connection conn = DriverManager.getConnection(url,p); -
使用URL、username、password三個參數分開的方式(推薦)
String url = "jdbc:mysql//localhost:3306/mydb"; String username = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url,username,password); -
利用java.sql.Drivermanager類中的getConnection()方法與數據庫建立連接。
注意: Connection是與特定數據庫連接回話的接口,使用的時候需要導包,而且必須在程序結束的時候將其關閉。getConnection方法也需要捕獲SQLException異常。
因為在進行數據庫的增刪改查的時候都需要與數據庫建立連接,所以可以在項目中將建立連接寫成一個工具方法,用的時候直接調用即可:
/*** 取得數據庫的連接* @return 一個數據庫的連接*/ public static Connection getConnection(){Connection conn = null;try {//初始化驅動類com.mysql.jdbc.DriverClass.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?characterEncoding=UTF-8","root", "123456");//該類就在 mysql-connector-java-5.0.8-bin.jar中,如果忘記了第一個步驟的導包,就會拋出ClassNotFoundException} catch (ClassNotFoundException e) { e.printStackTrace();}catch (SQLException e) { e.printStackTrace();}return conn;}第3步:獲取數據庫操作對象(Statement或者PreparedStatement)
SQL語句的執行對象按理說有Statement和PreparedStatement兩個,但我們一般都不會去使用Statement,先看下兩者的基本描述:
- Statement 是 Java 執行數據庫操作的一個重要接口,用于在已經建立數據庫連接的基礎上,向數據庫發送要執行的SQL語句。Statement對象,用于執行不帶參數的簡單SQL語句,即靜態SQL語句。
- PreparedStatement 繼承于Statement。實例包含已編譯的 SQL 語句,這就是使語句“準備好”。包含于 PreparedStatement 對象中的 SQL 語句可具有一個或多個 IN 參數。IN參數的值在 SQL 語句創建時未被指定。相反的,該語句為每個 IN 參數保留一個問號(“?”)作為占位符。每個問號的值必須在該語句執行之前,通過適當的setXXX() 方法來提供。
簡言之,Statement執行靜態SQL語句,而它的子類PreparedStatement執行預編譯SQL,即可傳入參數。兩者相比之下,PreparedStatement有以下優勢:
- **預編譯處理,可動態執行SQL語句。**很明顯,SQL語句的預編譯,使用占位符?去代替未知數據,因而一個句子可以執行多種不同的SQL,而Statement需要重新書寫SQL語句,笨重。
- 速度快,執行效率高。SQL語句會預編譯在數據庫系統中。執行計劃同樣會被緩存起來,它允許數據庫做參數化查詢。使用預處理語句比普通的查詢更快,因為它做的工作更少(數據庫對SQL語句的分析,編譯,優化已經在第一次查詢前完成了)。我們要利用預編譯的特性
(1). 利用java.sql.Connection創建用于執行SQL語句的Statement。
Statement stmt = connection.createStatement();注意:使用Statement會引起sql注入問題,在實際開發中很少使用,用的的更多的是它的子類PreparedStatement
比如sql注入問題如下:
String sql = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"驗證需要用戶輸入用戶名和密碼,正確則執行查詢語句(登錄),但如果這樣輸入:
userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";那么執行語句就變成了:
1 String sql = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"這樣,where語句恒為真,就能實現無賬號登錄。此外便可能被惡意修改甚至刪除數據表。然而使用PreparedStatement的參數化的查詢可以阻止大部分的SQL注入。在使用參數化查詢的情況下,數據庫系統(eg:MySQL)不會將參數的內容視為SQL指令的一部分來處理,而是在數據庫完成SQL指令的編譯后,才套用參數運行,且占位符?不允許多值,只能填入一個值,因此就算參數中含有破壞性的指令,也不會被數據庫所運行。
(2) .使用Connection對象的PreparedStatement(sql)方法進行獲取:
String sql = "select * from user where name=? and ange=?";//預處理,需要我們先寫好sql語句 PreparedStatement ps = conn.preparedStatement(sql);//conn是連接對象,參數為sql語句第4步:執行sql語句(增刪改查)
SQL語言共分為四大類:數據查詢語言DQL,數據操縱語言DML,數據定義語言DDL,數據控制語言DCL
sql語句有增刪查改等幾種類型,所以執行方法有以下三種:
- execute():執行SQL語句,可以是任何種類的 SQL 語句。返回值是boolean類型。
- executeQuery():執行SQL語句查詢,查詢結果返回為**ResultSet 對象**。
- executeUpdate() :執行更新語句。該語句必須是一個 SQL 數據操作語言(Data Manipulation Language,DML)語句,比如 INSERT、UPDATE 或 DELETE 語句;或者是無返回內容的 SQL 語句,比如 DDL 語句。返回值是int。
例如本例中的語句是查詢語句,所以執行代碼為:
- //執行查詢語句,并把結果集返回給集合ResultSet ResultSet rs = ps.executeQuery();
第5步:處理查詢結果集(ResultSet)
如果返回值是boolean或者int很好處理,但如果是查詢結果集ResultSet對象,一般使用while循環來處理:
ResultSet 對象具有指向其當前數據行的光標。最初,光標被置于第一行之前。next() 方法將光標移動到下一行;因為該方法在 ResultSet 對象沒有下一行時返回 false,所以可以在 while 循環中使用它來迭代結果集。另外,可以使用ResultSet對象的getXXX(int columnIndex)獲得游標所在行指定列的值。原理如下圖所示:
所以,本例的結果集處理如下:
1 while(rs.next()){ 2 system.out.println(rs.getString(1)); 3 system.out.println(rs.getInt(2)); 4 }第6步:釋放資源
在JDBC程序結束之后,顯式地需要關閉與數據庫的所有連接以結束每個數據庫會話。 但是,如果在編寫程序中忘記了關閉也沒有關系,Java的垃圾收集器將在清除過時的對象時也會關閉這些連接。
依靠垃圾收集,特別是數據庫編程,是一個非常差的編程實踐。所以應該要使用與連接對象關聯的close()方法關閉連接。要確保連接已關閉,可以將關閉連接的代碼中編寫在“finally”塊中。 一個finally塊總是會被執行,不管是否發生異常。
conn.close();八.JDBCUtils工具類
因為傳統JDBC的開發,注冊驅動,獲得連接,釋放資源這些代碼都是重復編寫的。所以可以將重復的代碼提取到一個類中來完成
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /*** JDBC的工具類* @author CodeAnime**/ public class JDBCUtils {private static final String driverClassName;private static final String url;private static final String username;private static final String password;static{driverClassName="com.mysql.jdbc.Driver";url="jdbc:mysql:///web_test3";username="root";password="123456";}/*** 注冊驅動的方法*/public static void loadDriver(){try {Class.forName(driverClassName);} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 獲得連接的方法*/public static Connection getConnection(){Connection conn = null;try{// 將驅動一并注冊:loadDriver();// 獲得連接conn = DriverManager.getConnection(url,username, password);}catch(Exception e){e.printStackTrace();}return conn;}/*** 釋放資源的方法*/public static void release(Statement stmt,Connection conn){if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}stmt = null;}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}conn = null;}}public static void release(ResultSet rs,Statement stmt,Connection conn){// 資源釋放:if(rs != null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}rs = null;}if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}stmt = null;}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}conn = null;}} }測試案例:導入工具類,查詢用戶信息
import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import org.junit.Test; import com.xdr630.jdbc.utils.JDBCUtils;/*** JDBC工具類的測試* @author xdr**/ public class JDBCDemo3 {@Test/*** 查詢操作:使用工具類*/public void demo1(){Connection conn = null;Statement stmt = null;ResultSet rs = null;try{// 獲得連接:conn = JDBCUtils.getConnection();// 創建執行SQL語句的對象:stmt = conn.createStatement();// 編寫SQL:String sql = "select * from user";// 執行查詢:rs = stmt.executeQuery(sql);// 遍歷結果集:while(rs.next()){System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));}}catch(Exception e){e.printStackTrace();}finally{// 釋放資源:JDBCUtils.release(rs, stmt, conn);}} }另一種寫法:
jdbc.properties屬性文件
注意:屬性配置文件必須以properties結尾,且存儲在src源目錄下
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb user=root password=123456JDBCUtils.java工具類
public class JDBCUtil(){//類文件屬性,可以在類文件所有的方法中使用private Connection conn = null; private PreparedStatement ps = null;/*** 在當前類文件第一次被加載到JVM時,JVM將會自動調用當前類文件靜態語句塊*/static {//1. 注冊數據庫服務器提供的Driver接口實現類try{ResourceBundle bundle = ResourceBundle.getBundle("jdbc");String driver = bundle.getString("driver");Class.forName(driver);}catch(ClassNotFoundException e){e.printStackTrace();}}/*** 封裝Connection對象創建細節* @return 數據庫連接的對象*///這里我們選擇拋出異常,而不是自行內部處理。是讓調用此方法的人知道出現異常時的控制臺出現的異常信息public Connection creatConnection() throws SQLException {//采用資源綁定器來綁定屬性配置文件ResourceBundle bundle = ResourceBundle.getBundle("jdbc");String url = bundle.getString("url");String username = bundle.getString("username");String password = bundle.getString("password");//2. 創建一個連接通道交給Connection接口的實例對象[Connection]管理conn = DriverManager.getConnection(url,username,password);return conn;}/*** 封裝PreparedStatement對象創建細節* @param sql sql語句* @return */public PreparedStatement createStatement(String sql) throws SQLException {Connection conn = creatConnection();//3. 創建一個交通工具交給 PreparedStatement 接口的實例對象[PreparedStatement]管理ps = conn.prepareStatement(sql);//4. 由交通工具在Java工程與數據庫服務器之間進行傳輸,推送SQL命令并帶回執行結果return ps;}/*** 封裝PreparedStatement與Connection對象銷毀細節*/public void close(){if (ps != null){try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 封裝PreparedStatement與Connection與ResultSet對象銷毀細節* @param rs 查詢結果集*/public void close(ResultSet rs){if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}close();} }JDBCTest01.java測試
public class JDBCTest01 {public static void main(String[] args) {//創建JDBC工具類對象JdbcUtil util = new JdbcUtil();PreparedStatement ps = null;ResultSet rs = null;try {String sql = "select * from studnet where studentid = ?";//這里由于在工具類中是拋出異常的,那么在調用時再拋出異常給JVM虛擬機就顯然不太合理了,此時我們選擇try...catch...內部處理ps = util.createStatement(sql);ps.setString(5,"田七");rs = ps.executeQuery();while (rs.next()){System.out.println(rs.getInt("studentid") + rs.getString("studnetname"));}} catch (SQLException e) {e.printStackTrace();}finally {//在finally語句塊中的語句必定執行util.close(rs);}} }參考文章:
-
作者 : Jungle_Rao
- 鏈接:https://blog.csdn.net/Jungle_Rao/article/details/81274720
-
作者 : Qiao_Zhi
- https://www.cnblogs.com/qlqwjy/p/8227665.html
-
作者 : 叫我玉弟大人
- https://www.cnblogs.com/jwyddr/p/11661096.html
-
作者 : 風大
- https://www.cnblogs.com/fzz9/p/8970210.html
-
作者 : newwaylau
- https://zhuanlan.zhihu.com/p/114709445
總結
- 上一篇: java如何在指定索引位置插入新元素
- 下一篇: mysql 钩子_面试官: 什么是 Ho