一、JDBC入門
1、JDBC 架構
JDBC 的 API 支持兩層和三層處理模式進行數據庫訪問,但一般的 JDBC 架構由兩層處理模式組成:
-
JDBC API: 提供了應用程序對 JDBC 管理器的連接。(提供給開發者使用)
- JDBC Driver API: 提供了 JDBC 管理器對驅動程序連接。(由各個數據庫廠商提供實現,如:數據庫驅動)
JDBC API 使用驅動程序管理器和數據庫特定的驅動程序來提供異構(heterogeneous)數據庫的透明連接。
JDBC 驅動程序管理器可確保正確的驅動程序來訪問每個數據源。該驅動程序管理器能夠支持連接到多個異構數據庫的多個併發的驅動程序。
以下是結構圖,其中顯示了驅動程序管理器相對於在 JDBC 驅動程序和 Java 應用程序所處的位置
2、常見的 JDBC 組件
JDBC 的 API 提供了以下接口和類:
DriverManager :這個類管理一系列數據庫驅動程序。匹配連接使用通信子協議從 JAVA 應用程序中請求合適的數據庫驅動程序。識別 JDBC 下某個子協議的第一驅動程序將被用於建立數據庫連接。
其實我們今後只需要會用DriverManager的getConnection()方法即可:
1. Class.forName(“com.mysql.jdbc.Driver”);//註冊驅動
2. String url = “jdbc:mysql://localhost:3306/mydb1”;
3. String username = “root”;
4. String password = “123”;
5. Connection con = DriverManager.getConnection(url, username,password);
注意,上面代碼可能出現的兩種異常:
1. ClassNotFoundException:這個異常是在第1句上出現的,出現這個異常有兩個可能:
l 你沒有給出mysql的jar包;
l 你把類名稱打錯了,查看類名是不是com.mysql.jdbc.Driver。
2. SQLException:這個異常出現在第5句,出現這個異常就是三個參數的問題,往往username和password一般不是出錯,所以需要認真查看url是否打錯。
對於DriverManager.registerDriver()方法瞭解即可,因爲我們今後註冊驅動只會Class.forName(),而不會使用這個方法。
Driver : 這個接口處理與數據庫服務器的通信。你將很少直接與驅動程序互動。相反,你使用 DriverManager 中的對象,它管理此類型的對 象。它也抽象與驅動程序對象工作相關的詳細信息。
Connection : 此接口具有接觸數據庫的所有方法。該連接對象表示通信上下文,即,所有與數據庫的通信僅通過這個連接對象進行。
Connection最爲重要的方法就是獲取Statement:
l Statement stmt = con.createStatement();
後面在學習ResultSet方法時,還要學習一下下面的方法:
l Statement stmt = con.createStatement(int,int );
Statement : 使用創建於這個接口的對象將 SQL 語句提交到數據庫。除了執行存儲過程以外,一些派生的接口也接受參數。
Statement最爲重要的方法是:
l int executeUpdate(String sql):執行更新操作,即執行insert、update、delete語句,其實這個方法也可以執行create table、 alter table,以及drop table等語句,但我們很少會使用JDBC來執行這些語句;
l ResultSet executeQuery (String sql):執行查詢操作,執行查詢操作會返回ResultSet,即結果集。
Statement還有一個boolean execute()方法,這個方法可以用來執行增、刪、改、查所有SQL語句。該方法返回的是boolean類型,表示 SQL語句是否有結果集!(一般不用)
PreparedStatement
l 它是Statement接口的子接口;
l 強大之處:(預編譯聲明)
防SQL攻擊;
提高代碼的可讀性、可維護性;
提高效率!
reparedStatement的使用
l 使用Connection的prepareStatement(Stringsql):即創建它時就讓它與一條SQL模板綁定;
l 調用PreparedStatement的setXXX()系列方法爲問號設置值
l 調用executeUpdate()或executeQuery()方法,但要注意,調用沒有參數的方法;
ResultSet : 在你使用語句對象執行 SQL 查詢後,這些對象保存從數據獲得的數據。它作爲一個迭代器,讓您可以通過它的數據來移動。
結果集特性:當使用Connection的createStatement時,已經確定了Statement生成的結果集是什麼特性。
l 是否可滾動
l 是否敏感
l 是否可更新
con.createSttement():生成的結果集:不滾動、不敏感、不可更新!
con.createStatement(int,int):
第一個參數:
l ResultSet.TYPE_FORWARD_ONLY:不滾動結果集;
l ResultSet.TYPE_SCROLL_INSENSITIVE:滾動結果集,但結果集數據不會再跟隨數據庫而變化;
l ResultSet.TYPE_SCROLL_SENSITIVE:滾動結果集,但結果集數據不會再跟隨數據庫而變化;
第二個參數:
l CONCUR_READ_ONLY:結果集是隻讀的,不能通過修改結果集而反向影響數據庫;
l CONCUR_UPDATABLE:結果集是可更新的,對結果集的更新可以反向影響數據庫。
默認特性的結果集:
可以通過next()方法使ResultSet的遊標向下移動,當遊標移動到你需要的行時,就需要來獲取該行的數據了,ResultSet提供了一系列的獲取列數據的方法:
l String getString(int columnIndex):獲取指定列的String類型數據;
l int getInt(int columnIndex):獲取指定列的int類型數據;
l double getDouble(int columnIndex):獲取指定列的double類型數據;
l boolean getBoolean(int columnIndex):獲取指定列的boolean類型數據;
l Object getObject(int columnIndex):獲取指定列的Object類型的數據。
獲取結果集元數據:
l 得到元數據:rs.getMetaData(),返回值爲ResultSetMetaData;
l 獲取結果集列數:intgetColumnCount()
l 獲取指定列的列名:StringgetColumnName(int colIndex)
SQLException : 這個類處理髮生在數據庫應用程序的任何錯誤。
3、下面開始編寫第一個JDBC程序
3.1 mysql數據庫的驅動jar包:mysql-connector-java-5.1.13-bin.jar;
3.2 獲取連接
獲取連接需要兩步,一是使用DriverManager來註冊驅動,二是使用DriverManager來獲取Connection對象。
1. 註冊驅動
看清楚了,註冊驅動就只有一句話:Class.forName(“com.mysql.jdbc.Driver”),下面的內容都是對這句代碼的解釋。今後我們的代碼中,與註冊驅動相關的代碼只有這一句。
DriverManager類的registerDriver()方法的參數是java.sql.Driver,但java.sql.Driver是一個接口,實現類由mysql驅動來提供,mysql驅動中的java.sql.Driver接口的實現類爲com.mysql.jdbc.Driver!那麼註冊驅動的代碼如下:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
上面代碼雖然可以註冊驅動,但是出現硬編碼(代碼依賴mysql驅動jar包),如果將來想連接Oracle數據庫,那麼必須要修改代碼的。並且其實這種註冊驅動的方式是註冊了兩次驅動!
JDBC中規定,驅動類在被加載時,需要自己“主動”把自己註冊到DriverManger中,下面我們來看看com.mysql.jdbc.Driver類的源代碼:
com.mysql.jdbc.Driver.java
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } …… } |
com.mysql.jdbc.Driver類中的static塊會創建本類對象,並註冊到DriverManager中。這說明只要去加載com.mysql.jdbc.Driver類,那麼就會執行這個static塊,從而也就會把com.mysql.jdbc.Driver註冊到DriverManager中,所以可以把註冊驅動類的代碼修改爲加載驅動類。
Class.forName(“com.mysql.jdbc.Driver”);
2. 獲取連接
獲取連接的也只有一句代碼:DriverManager.getConnection(url,username,password),其中username和password是登錄數據庫的用戶名和密碼,如果我沒說錯的話,你的mysql數據庫的用戶名和密碼分別是:root、123。
url查對複雜一點,它是用來找到要連接數據庫“網址”,就好比你要瀏覽器中查找百度時,也需要提供一個url。下面是mysql的url:
jdbc:mysql://localhost:3306/mydb1
JDBC規定url的格式由三部分組成,每個部分中間使用逗號分隔。
l 第一部分是jdbc,這是固定的;
l 第二部分是數據庫名稱,那麼連接mysql數據庫,第二部分當然是mysql了;
l 第三部分是由數據庫廠商規定的,我們需要了解每個數據庫廠商的要求,mysql的第三部分分別由數據庫服務器的IP地址(localhost)、端口號(3306),以及DATABASE名稱(mydb1)組成。
下面是獲取連接的語句:
Connection con= DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb1”,”root”,”123”);
還可以在url中提供參數:
jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=UTF8
useUnicode參數指定這個連接數據庫的過程中,使用的字節集是Unicode字節集;
characherEncoding參數指定穿上連接數據庫的過程中,使用的字節集編碼爲UTF-8編碼。請注意,mysql中指定UTF-8編碼是給出的是UTF8,而不是UTF-8。要小心了!
3.3 獲取Statement
在得到Connectoin之後,說明已經與數據庫連接上了,下面是通過Connection獲取Statement對象的代碼:
Statement stmt =con.createStatement();
Statement是用來向數據庫發送要執行的SQL語句的!
3.4 發送SQL增、刪、改語句
String sql = “insertinto user value(’zhangSan’, ’123’)”;
int m =stmt.executeUpdate(sql);
其中int類型的返回值表示執行這條SQL語句所影響的行數,我們知道,對insert來說,最後只能影響一行,而update和delete可能會影響0~n行。
如果SQL語句執行失敗,那麼executeUpdate()會拋出一個SQLException。
3.5 發送SQL查詢語句
String sql = “select* from user”;
ResultSet rs =stmt.executeQuery(sql);
請註冊,執行查詢使用的不是executeUpdate()方法,而是executeQuery()方法。executeQuery()方法返回的是ResultSet,ResultSet封裝了查詢結果,我們稱之爲結果集。
3.6 讀取結果集中的數據
ResultSet就是一張二維的表格,它內部有一個“行光標”,光標默認的位置在“第一行上方”,我們可以調用rs對象的next()方法把“行光標”向下移動一行,當第一次調用next()方法時,“行光標”就到了第一行記錄的位置,這時就可以使用ResultSet提供的getXXX(intcol)方法來獲取指定列的數據了:
rs.next();//光標移動到第一行
rs.getInt(1);//獲取第一行第一列的數據
當你使用rs.getInt(1)方法時,你必須可以肯定第1列的數據類型就是int類型,如果你不能肯定,那麼最好使用rs.getObject(1)。在ResultSet類中提供了一系列的getXXX()方法,比較常用的方法有:
ObjectgetObject(int col)
String getString(intcol)
int getInt(intcol)
doublegetDouble(int col)
3.7 關閉
與IO流一樣,使用後的東西都需要關閉!關閉的順序是先得到的後關閉,後得到的先關閉。
rs.close();
stmt.close();
con.close();
public static Connection getConnection() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mydb1";
return DriverManager.getConnection(url, "root", "123");
}
@Test
public void insert() throws Exception {
Connection con = getConnection();
Statement stmt = con.createStatement();
String sql = "insert into user values('zhangSan', '123')";
stmt.executeUpdate(sql);
System.out.println("插入成功!");
}
@Test
public void update() throws Exception {
Connection con = getConnection();
Statement stmt = con.createStatement();
String sql = "update user set password='456' where username='zhangSan'";
stmt.executeUpdate(sql);
System.out.println("修改成功!");
}
@Test
public void delete() throws Exception {
Connection con = getConnection();
Statement stmt = con.createStatement();
String sql = "delete from user where username='zhangSan'";
stmt.executeUpdate(sql);
System.out.println("刪除成功!");
}
@Test
public void query() throws Exception {
Connection con = getConnection();
Statement stmt = con.createStatement();
String sql = "select * from user";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
System.out.println(username + ", " + password);
}
}
4、時間類型
4-1、數據庫類型與java中類型的對應關係:
DATE -> java.sql.Date
TIME -> java.sql.Time
TIMESTAMP -> java.sql.Timestamp
4-2、
l 領域對象(domain)中的所有屬性不能出現java.sql包下的東西!即不能使用java.sql.Date;
l ResultSet#getDate()返回的是java.sql.Date()
l PreparedStatement#setDate(int, Date),其中第二個參數也是java.sql.Date
4-3、時間類型的轉換
util --> sql
java.utl.Date d= new java.util.Date();//既包含時分秒,有包含年月日
java.sql.Datedate = new java.sql.Date(d.getTime());//會丟失時分秒
Time time = newTime(d.getTime());//會丟失年月日
Timestamptimestamp = new Timestamp(d.getTime());
sql --> util
5、大數據
1 什麼是大數據
所謂大數據,就是大的字節數據,或大的字符數據。標準SQL中提供瞭如下類型來保存大數據類型:
類型 |
長度 |
tinyblob |
28--1B(256B) |
blob |
216-1B(64K) |
mediumblob |
224-1B(16M) |
longblob |
232-1B(4G) |
tinyclob |
28--1B(256B) |
clob |
216-1B(64K) |
mediumclob |
224-1B(16M) |
longclob |
232-1B(4G) |
但是,在mysql中沒有提供tinyclob、clob、mediumclob、longclob四種類型,而是使用如下四種類型來處理文本大數據:
類型 |
長度 |
tinytext |
28--1B(256B) |
text |
216-1B(64K) |
mediumtext |
224-1B(16M) |
longtext |
232-1B(4G) |
首先我們需要創建一張表,表中要有一個mediumblob(16M)類型的字段。
CREATE TABLE tab_bin( id INT PRIMARY KEY AUTO_INCREMENT, filename VARCHAR(100), data MEDIUMBLOB ); |
向數據庫插入二進制數據需要使用PreparedStatement爲原setBinaryStream(int, InputSteam)方法來完成。
con = JdbcUtils.getConnection(); String sql = "insert into tab_bin(filename,data) values(?, ?)"; pstmt = con.prepareStatement(sql); pstmt.setString(1, "a.jpg"); InputStream in = new FileInputStream("f:\\a.jpg");[崔1] pstmt.setBinaryStream(2, in);[崔2] pstmt.executeUpdate(); |
讀取二進制數據,需要在查詢後使用ResultSet類的getBinaryStream()方法來獲取輸入流對象。也就是說,PreparedStatement有setXXX(),那麼ResultSet就有getXXX()。
con = JdbcUtils.getConnection(); String sql = "select filename,data from tab_bin where id=?"; pstmt = con.prepareStatement(sql); pstmt.setInt(1, 1); rs = pstmt.executeQuery(); rs.next();
String filename = rs.getString("filename"); OutputStream out = new FileOutputStream("F:\\" + filename)[崔3] ;
InputStream in = rs.getBinaryStream("data")[崔4] ; out.close(); |
還有一種方法,就是把要存儲的數據包裝成Blob類型,然後調用PreparedStatement的setBlob()方法來設置數據
con = JdbcUtils.getConnection(); String sql = "insert into tab_bin(filename,data) values(?, ?)"; pstmt = con.prepareStatement(sql); pstmt.setString(1, "a.jpg"); File file = new File("f:\\a.jpg"); byte[] datas = FileUtils.getBytes(file);//獲取文件中的數據 Blob blob = new SerialBlob(datas);//創建Blob對象 pstmt.setBlob(2, blob);//設置Blob類型的參數 pstmt.executeUpdate(); |
con = JdbcUtils.getConnection(); String sql = "select filename,data from tab_bin where id=?"; pstmt = con.prepareStatement(sql); pstmt.setInt(1, 1); rs = pstmt.executeQuery(); rs.next();
String filename = rs.getString("filename"); File file = new File("F:\\" + filename) ; Blob blob = rs.getBlob("data"); byte[] datas = blob.getBytes(0, (int)file.length()); FileUtils.writeByteArrayToFile(file, datas); |
6、批處理
PreparedStatement批處理
因爲每個PreparedStatement對象都綁定一條SQL模板。所以向PreparedStatement中添加的不是SQL語句,而是給“?”賦值。
con = JdbcUtils.getConnection();
String sql = "insert into stu values(?,?,?,?)";
pstmt = con.prepareStatement(sql);
for(int i = 0; i < 10; i++) {
pstmt.setString(1, "S_10" + i);
pstmt.setString(2, "stu" + i);
pstmt.setInt(3, 20 + i);
pstmt.setString(4, i % 2 == 0 ? "male" : "female");
pstmt.addBatch() ;
}
pstmt.executeBatch ();