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