我們知道Java中的jdbc是用來連接應用程序和數據系統的,本篇文章主要就來看看關於JDBC的實現和使用細節。主要包含以下幾點內容:
- JDBC的基本知識(數據驅動程序)
- JDBC的連接配置
- 使用JDBC增刪改查
- JDBC的一些使用細節
一、JDBC的基本知識
我們用Java寫的程序,無論是桌面應用程序還是web應用程序都是不能直接訪問我們本機上的數據庫系統的,這就需要使用驅動程序去構成兩者之間的連接。
像這樣,我們的應用程序需要針對不同的數據庫系統調用不同的驅動程序來連接操作數據庫系統,但是對於不同的數據庫系統,我們就需要學習他們各自提供的驅動程序接口的使用,還是比較麻煩的。並且程序一旦數據遷移,將導致關於數據操作的代碼模塊需要重寫。
於是sun公司為了簡化Java對數據庫的連接操作,定義了一套Java操作數據庫的規范,JDBC(Java Database Connectivity)。從此程序員就可以使用純Java代碼連接和操作數據庫了。
JDBC向上提供了一系列的使用接口,包括連接數據庫,增刪改查操作等。向下會去調用相對應了驅動程序,然后這些驅動程序又會去直接的操作數據庫,執行sql語句,返回結果。對於我們程序員,只需要學習怎么使用JDBC,不用再去關心各個驅動程序怎么使用。
二、JDBC的連接配置
想要成功的使用jdbc連接我們的本地數據庫主要需要以下幾個步驟:
- 下載對應的數據庫系統提供的驅動程序
- 將驅動程序包添加到jdk包中
- 調用 DriverManager 類的 getConnection()獲取數據庫連接對象
下面一步步演示並解釋,首先下載對應的DBMS(數據庫管理系統提供驅動程序),你可以使用IBM的DB2,或者微軟的Sql Server,或者mysql。本篇文章使用的是sql server。下載地址:https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774 。運行sqljdbc_6.0.8112.100_chs.exe解壓文件,或者直接解壓sqljdbc_6.0.8112.100_chs.tar.gz,然后進入解壓出來的文件夾,找到jre文件夾,(應該有兩個版本7,8),找到對應自己的jdk版本,復制里面的sqljdbc42.jar包,到本機的jdk文件夾。
默認應該在:C:\Program Files\Java中,進入jdk文件夾中(不要進錯了),然后jre\lib\ext,將剛剛的jdbc包粘貼到其中即可。
下面打開IDE創建一個project,main函數中粘貼以下代碼,具體什么意思,后文會說。
public static void main(String[] args){
String driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=你的某個數據庫的名字";
String userName="你的數據庫登錄名";
String userPwd="你的數據庫登錄密碼";
try{
//加載驅動程序
Class.forName(driverName);
//獲取連接對象
Connection dbConn= DriverManager.getConnection(dbURL, userName, userPwd);
System.out.println("數據庫連接成功");
}
catch(Exception e)
{
e.printStackTrace();
}
}
運行以上代碼如果打印連接成功,就繼續下文。否則,可以評論留言或者自行百度解決。下面開始解釋每一條語句:
首先,所有的操作的前提都是告訴jvm我們的程序將要使用的數據驅動是什么,是mysql,sqlserver,還是oracle。Class.forName(driverName);這條語句就是這個作用,我們看driverName字符串。
裝載MySql驅動:Class.forName("com.mysql.jdbc.Driver");
裝載Oracle驅動:Class.forName("oracle.jdbc.driver.OracleDriver");
裝載SqlServer驅動:Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
我們上述代碼中使用的driverName的值是第三種中的參數值,主流的數據庫管理系統是這三者,這三句代碼是不變的,讀者可以根據自己電腦上的數據庫自行選擇。
第二步就是獲取連接對象,如果沒有拋異常就說明連接是成功的,我們首先從三個參數說起。第一個參數是一個URL,他的格式是:jdbc:<子協議>:<子名稱>,這種格式基本上也是被每個數據庫提供商定死了,你只需要選擇他們並增加自己的參數即可。常見的三個URL格式:
對於 Oracle 數據庫連接,采用如下形式:
jdbc:oracle:thin:@localhost:1521:sid
對於 SQLServer 數據庫連接,采用如下形式:
jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid
對於 MYSQL 數據庫連接,采用如下形式:
jdbc:mysql://localhost:3306/sid
sid就是本地數據庫中某個具體數據庫的名字。下面說第二個參數,從命名上讀者也是可以輕松的判斷出這是在判別身份,第三個參數是密碼。相信這三個參數還是可以很輕松理解的。下面看看DriverManager類和他的一些方法。
DriverManager可以叫它驅動程序管理接口,主要實現的是對驅動程序的管理的功能。例如:初始化驅動程序,啟動驅動程序建立jdbc連接對象,還有一些獲取日志信息的操作。我們主要用它來創建數據庫連接對象,然后通過這個對象操作數據庫的增刪改查。此間如果數據庫連接失敗將會拋出異常。下面我們介紹這個連接對象,並通過它完成增刪改查。
三、使用JDBC增刪改查
實現增刪改查的操作的前提是需要獲取數據庫連接對象。以下是這個Connection接口的主要方法:
Statement createStatement()
PreparedStatement prepareStatement(String sql)
CallableStatement prepareCall(String sql)
先看第一個方法,這個方法返回一個Statement對象,這個對象是用來向數據庫發送靜態sql語句的,第二個方法返回了一個PreparedStatement對象,這是一個預編譯的Statement對象,主要用來發送動態的sql語句的。具體的區別下文說。第三個方法返回的對象是用來執行存儲過程的相關操作的。
下面我們看可以操作一般sql語句的對象Statement。他有如下常用方法:
ResultSet executeQuery(String sql)
int executeUpdate(String sql)
boolean execute(String sql)
int[] executeBatch()
void addBatch( String sql )
測試方法executeQuery
public static void main(String[] args){
try{
//加載驅動程序
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//獲取連接對象
Connection dbConn= DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=FitWeb", "sa", "123456");
Statement statement = dbConn.createStatement();
ResultSet rs = statement.executeQuery("SELECT * FROM users");
while(rs.next()){
System.out.println(rs.getString("name"));
}
rs.close();
dbConn.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//程序的輸出結果是我的表FitWeb中users表中字段名為name的所有的值
對於上代碼中的ResultSet結果集的相關操作,暫時可以先過。只需要知道我們向executeQuery方法中傳入可執行的sql語句,他會返回執行之后的結果。對於方法executeUpdate主要是完成對數據表的增刪改。
int x = statement.executeUpdate("UPDATE users SET NAME = '華為' WHERE NAME ='李斯'");
可以像這樣修改表中數據,還可以insert,delete等操作。對於execute方法,它不區分是查詢還是修改操作,你可以向他傳入任意的sql語句,只是對於查詢不會返回結果集,如果成功的修改了表中內容返回true,否則false。下面看看批處理sql操作:
Statement statement = dbConn.createStatement();
statement.addBatch("UPDATE users SET NAME ='Walker' WHERE NAME = '華為'");
statement.addBatch("UPDATE users SET NAME ='華為' WHERE NAME = 'Walker'");
int[] count = statement.executeBatch();
for(int c : count){
System.out.print(c);
System.out.print(",");
}
statement.close();
dbConn.close();
//輸出結果:1,1
之前的方法都只能一次操作一條sql語句,而我們的executeBatch是用來一次執行多條sql語句的,返回值是int數組,它表示了每一次操作數據庫之后影響的行數。第一次sql執行之后影響的行數保存在索引為0中,后面以此類推。關於預編譯Statement的操作,我們放在下一個小節說。
四、JDBC的一些使用細節
SQL注入的大名想必大家都是知道的,而在我們之前介紹的方法中,好像都沒有關於如何防止這種黑客行為。SQL注入就是指在帶有參數的sql語句中注入的sql語法。類似這樣:
select * from users where name = userName
如果我們的應用程序中需要查詢某個人的信息,而查詢的條件是需要用戶輸入自己的用戶名,然后我們根據用戶名進行查詢,向上面一樣如果用戶輸入的是以下內容:
userName = "'張三' or '李四'";
select * from users where name = userName
等價:
select * from users where name = '張三' or '李四'
這樣豈不是將張三和李四的信息都查詢出來了,如果此人通過大數據列舉,很可能數據表中的所有數據都會被查詢出來。
我們可以使用,預編譯Statement對象來避免這件事情。先看代碼:
PreparedStatement statement = dbConn.prepareStatement("insert into user(id,Name) values (?,?)");
statement.setInt(1,1);
statement.setString(2,"walker");
statement.executeUpdate();
statement.close();
dbConn.close();
程序運行的結果是,插入一條數據到了我的user表中。接下來我們看看每條語句是什么含義。首先,?表示占位符的意思,就是說此處會有參數傳入,只是具體的值是什么暫時不知道。下面的兩條setXXX就是在為占位符賦值,然后執行更改操作,完成數據庫更新。我們說為什么它能夠防止Sql注入呢?因為所有用戶的輸入參數都是用?占位,也就是說無論你傳入的是什么,我都只把你當做參數。
userName = "'張三' or '李四'";
select * from users where name = ?
等價:
select * from users where name = ''張三' or '李四''
//相當於:'?',無論?是什么都不會產生任何問題
另外,使用這種預編譯Statement,可以很大程度上提高性能,因為他會緩存sql模板,就是除了參數部分,其余內容會被緩存,等到下次再遇到的時候會直接調用,提高性能。因此建議使用這種方式來操作數據庫。
最后詳細的介紹一下,關於結果集的操作。本篇剛開始的時候用過,但是那只是很簡單的一部分。ResultSet(結果集)接口的主要方法如下:
boolean next()
String getString(int columnIndex)
boolean getBoolean(int columnIndex)
int getInt(int columnIndex)
String getString(String columnLabel)
java.sql.Date getDate(int columnIndex)
java.sql.Time getTime(int columnIndex)
java.sql.Timestamp getTimestamp(int columnIndex)
void beforeFirst()
void afterLast()
Blob getBlob(int columnIndex)
Clob getClob(int columnIndex)
這個接口有着四千多行的代碼,我們只列舉了常用的方法。我們可以將整個結果集理解為一張二維的表,每張表都有一個游標用於遍歷所有的行。next()方法用於判斷是否還有下一行,返回值是boolean。getXXX方法表示獲取當前游標指向的行中指定的字段,可以使用索引來定位字段,也可以是通過字段的名字來定位。如果是索引,1為起始位置。
關於LOB類型數據,這是一類大對象,往往是圖片或者是一些其他的數據。二進制大對象稱作BLOB,字符型大對象稱作CLOB。調用getBlob或者getClob可以獲取到相應的對象。通過他們自己內部的getBytes,或者getInputStream等方法,可以獲取其中的內容。此處不細說。
下面看看多結果集的操作,看代碼:
Statement statement = dbConn.createStatement();
boolean b = statement.execute("select * from users;select * from sports;");
if(b){
ResultSet rs = statement.getResultSet();
while (rs.next()){
System.out.println(rs.getString("name"));
}
b = statement.getMoreResults();
if (b){
ResultSet rs2 = statement.getResultSet();
while (rs2.next()){
System.out.println(rs2.getString("sportsName"));
}
}
}
statement.close();
dbConn.close();
我們調用execute方法執行sql語句,而需要執行的是兩條sql語句,如果成功的獲取了結果集則返回true。通過getResultSet方法獲取第一個結果集,輸出所有字段名為name的信息,調用getMoreResults方法判斷是否有別的結果集,如果有返回true。然后通過getResultSet獲取當前結果集,也就是第二個結果集,輸出信息。
為了不使文章篇幅過長,還剩下一個知識點放在下篇。