一、eclipse的使用
1、修改註釋內容
選擇window---->>preferences
選擇Java---->>codestyle---->>code template---->>comments,然後雙擊types,修改裏面的內容即可!
2、修改Eclipse裏面的快捷鍵
舉例:修改複製行內容快捷鍵。
1.點擊window菜單->preferences子菜單->general->keys,進入快捷鍵管理界面
2.在這裏可以查找所有功能的快捷鍵,需要修改或新增時,點擊需要修改或新增的命令,在binding裏設置快捷鍵
3.設置完快捷鍵後,還需要設置在什麼時候可以使用該快捷鍵,eclipse提供各種場景供選擇,一般選擇In Windows(即在eclipse窗口激活狀態)即可
完成以上操作,點擊OK即完成設置
3、設置工作空間的編碼
二、JUnit單元測試
1、介紹:JUnit是一個java語言的單元測試框架。屬於第三方工具,一般情況下需要導入jar包,不過多數java開發環境已經集成了JUnit作爲單元測試工具。
創建“day07”java項目,並創建“cn.itcast.a_junit”包
2、編寫測試類,簡單理解可以用於取代java的main方法
3、在測試類方法上添加註解@Test
4、註解修飾方法要求:public void 方法名(){...},方法名自定義建議test開頭
5、添加STS中集成的Junit庫,鼠標點擊“@Test”,使用快捷鍵“ctrl+1”,點擊“Add Junit...”
結果
6、使用:選中方法右鍵,執行當前方法;選中類名右鍵,執行類中所有方法(方法必須標註@Test)
7、常用註解
@Test,用於修飾需要執行的方法
@Before,測試方法前執行的方法
@After,測試方法後執行的方法
8、常見使用錯誤,如果沒有添加“@Test”,使用“Junit Test”進行運行,將拋異常
三、JDBC
1、什麼是JDBC
JDBC(Java DataBase Connectivity)就是java數據庫連接,簡單地說就是用java語言來操作數據庫。原來我們操作數據庫是在控制檯使用SQL語句來操作數據庫,JDBC是用Java語言向數據庫發送SQL語句
<1>JDBC是一種用於執行SQL語句的java API
<2>JDBC可以爲多種關係數據庫提供統一訪問入口
<3>JDBC由一組java工具類和接口組成
2、JDBC原理
早期SUN公司的天才們想編寫一套可以連接天下所有數據庫的API,但是當他們剛剛開始時就發現這是不可完成的任務,因爲各個廠商的數據庫服務器差異太大了。後來SUN開始與數據庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數據庫的規範(就是一組接口),並提供連接數據庫的協議標準,然後各個數據庫廠商會遵循SUN的規範提供一套訪問自己公司的數據庫服務器的API出現。SUN提供的規範命名爲JDBC。而各個廠商提供的,遵循了JDBC規範的,可以訪問自數據庫的API被稱之爲驅動。
JDBC是接口,而JDBC驅動纔是接口的實現,沒有驅動無法完成數據庫連接!每個數據庫廠商都有自己的驅動,用來連接自己公司的數據庫。
當然還有第三方公司專門爲某一數據庫提供驅動,這樣的驅動往往不是開源免費的!
3、JDBC核心類(接口)介紹
JDBC中的核心類有:DriverManager、Connection、Statement和ResultSet
DriverManager(驅動管理器)的作用有兩個:
<1>註冊驅動:這可以讓JDBC知道要使用的是哪個驅動;
<2>獲取Connection:如果可以獲取到Connection,那麼說明已經與數據庫連接上了。
Connection對象表示連接,與數據庫的通訊都是通過這個對象展開的;
<3>Connection最爲重要的一個方法就是用來獲取Statement對象;
<4>Statement是用來向數據庫發送SQL語句的,這樣數據庫會執行發送過來的的SQL語句
<5>void executeUpdate(String sql):執行更新操作(insert、update、delete等);
<6>ResultSet executeQuery(String sql):執行查詢操作,數據庫在執行查詢後會返回查詢結果,查詢 結果就是ResultSer;
ResultSet對象表示查詢結果集,只有在執行查詢操作後纔會有結果集的產生。結果集是一個人二維 的表格,有行有列。操作結果集要學習移動ResultSet內部的“行光標”,以及獲取當前行上的數據
<7>boolean next():使“行光標”移動到下一行,並返回移動後的行是否存在;
<8>xxx getxxx(int col):獲取當前行指定列上的值,參數就是列數,列數從1開始,而不是0
4、JDBC開發步驟
4.1 導入mysql數據庫的驅動jar包:
<1>創建lib目錄,用於存放當前項目需要的所有jar包
<2>選擇jar包,右鍵執行build path/Add to Build Path
4.2 註冊驅動
註冊驅動就只有一句話:Class.forName("com.mysql.jdbc.Driver"),
下面的內容都是對這句代碼的解釋。今後我們的代碼中,與註冊驅動相關的代碼只有這一句。
<1>分析步驟1:JDBC規範定義驅動接口:java.sql.Driver,
MySql驅動包提供了實現類:com.mysql.jdbc.Driver
<2>分析步驟2:DriverManager工具類,提供註冊驅動的方法registerDriver(),
方法的參數是java.sql.Driver,所以我們可以通過如下語句進行註冊
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代碼不推薦使用,存在兩方面不足:
1.硬編碼,後期不易於程序擴展和維護
2.驅動被註冊兩次
<3>分析步驟3:通常開發我們使用Class.forName()加載一個使用字符串描述的驅動類。
如果使用Class.forName()將加載到內存,該類的靜態代碼將自動執行
通過查詢com.mysql.jdbc.Driver源碼,我們發現Driver類“主動”將自己進行註冊
DriverManager類的registerDriver()方法的參數是java.sql.Driver,但java.sql.Driver是一個接口,實現類由mysql驅動來提供,mysql驅動中的java.sql.Driver接口的實現類爲com.mysql.Driver那麼註冊驅動的代碼如下:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
上面的代碼雖然可以註冊驅動,但是出現硬編碼(代碼依賴mysql驅動jar包),如果將來想連接Oracle數據庫,那麼必須要修改代碼。並且其實這種註冊驅動的方式是註冊了兩次驅動!
JDBC中規定,驅動類在被加載時,需要自己“主動”把自己註冊到DriverManager中,下面來看看com.mysql.jdbc.Driver類的源代碼:
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch(SQLException E) { throw newRuntimeException("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");
4.3 獲取連接
代碼:Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydbl","root","root")
獲取連接需要兩步,一是使用DriverManager來註冊驅動,二是使用DriverManager來獲取Connection對象。
獲取連接的也只有一句話代碼:
DriverManager.getConnection(url,username,password);
其中username和password是登錄數據庫的用戶名和密碼
url相對複雜一點,它是用來找到要連接數據庫的“網址”,就好比你要在瀏覽器中查找百度時,也需要提供一個url。下面是mysql的url:
jdbc:mysql://localhost:3306/mydbl
JDBC規定url的格式由三部分組成,每個部分中間使用冒號分割。
<1>第一部分是jdbc,這是固定的;
<2>第二部分是數據庫名稱,那麼連接mysql數據庫,第二部分當然是mysql了;
<3>第三部分是由數據庫廠商規定的,我們需要了解每個數據庫廠商的要求,mysql的第三部分分別 由數據庫服務器的IP地址(localhost)、端口號(3306),以及database名稱(mybdl)組成
下面是獲取連接的語句:
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","root");
還可以在url中提供參數:
jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=UTF8
useUnicode參數指定這個連接數據庫的過程中,使用的字節集是Unicode字節集;
characterEncoding參數指定java程序連接數據庫的過程中,使用的字符集編碼爲UTF-8編碼。注意,mysql中指定UTF-8編碼給出的是UTF8,而不是UTF-8
4.4 獲得語句執行
String sql="insert into category(cid,cname) values('c007','分類')";
Statement stmt=con.createStatement();
在得到Connection之後,說明已經與數據庫連接上了,下面是通過Connection獲取Statement對象的代碼:
Statement stmt=con.createStatement();
Statement是用來向數據庫發送要執行的SQL語句的
常用方法:
執行SQL語句:
<1>int executeUpdate(String sql);---執行insert update delete語句.(DML語句)
<2>ResultSet executeQuery(String sql);---執行select語句(DQL語句)
<3>boolean execute(String sql);---執行select返回true,執行其他的語句返回false
如果返回true,需要使用getResultSet()獲得查詢結果
如果返回false,需要使用getUpdateCount()獲得影響行數
執行批處理:(可選)
addBatch(String sql);
clearBatch();
executeBatch();
4.5 預處理對象
使用處理對象時,建議每條sql語句所有的實際參數,都使用逗號分隔
String sql="insert into category(cid,cname) values(?,?)";
PreparedStatement psmt=con.prepareStatement(sql);
常用方法:
執行SQL語句:
<1>int executeUpdate(String sql);---執行insert update delete語句.(DML語句)
<2>ResultSet executeQuery(String sql);---執行select語句(DQL語句)
<3>boolean execute(String sql);---執行select返回true,執行其他的語句返回false
設置實際參數:
setxxx(int,T)通過setter方法將?佔位符替換成實際參數
例如:setString()實際參數類型爲hi字符串
執行批處理:(可選)
addBatch; 添加批處理的實際參數,需要調方法前執行對應setter方法
clearBatch();
executeBatch();
4.6 處理結果集
ResultSet就是一張二維的表格,它內部有一個“行光標”,光標默認位置在“第一行上方”,我們可以調用rs對象的next()方法把“行光標”向下移動一行,當第一次調用next()方法時,“行光標”就到了第一行記錄的位置,這時就可以使用ResultSet提供的getxxx(int col)方法來獲取指定列的數據了:
rs.next();//光標移動到第一行
rs.getInt(1);//光標移動到第一列的數據
當你使用rs.getInt(1)方法時,你必須可以肯定第1列的數據類型就是int類型,如果你不能肯定,那麼最好使用rs.getObject(1)。在ResultSet類中提供了一系列的getxxx()方法,比較常用的方法有:
Object getObject(int col)
String getString(int col)
int getInt(int col)
double getDouble(int col)
4.7 釋放資源
與IO流一樣,使用後的東西都需要關閉,關閉的順序是先得到的後關閉,後得到的先關閉
rs.close();
stmt.close();
con.close;
4.8 完成查詢操作代碼
public static Connection getConnection() throws Exception { Class.forName("com.mysql.jdbc.Driver"); String url ="jdbc:mysql://localhost:3306/web08"; return DriverManager.getConnection(url, "root", "root"); }
@Test public void query() throws Exception{ Connectioncon = getConnection(); Statementstmt = 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.9 規範化代碼
所謂規範化代碼就是無論是否出現異常,都要關閉ResultSet、Statement,以及Connection
@Test public void query() { Connection con = null; Statement stmt = null; ResultSet rs= null; try { con = getConnection(); stmt =con.createStatement(); String sql = "select * from user"; rs =stmt.executeQuery(sql); while(rs.next()){ String username = rs.getString(1); String password = rs.getString(2); System.out.println(username+ ", " + password); } } catch(Exceptione) { throw newRuntimeException(e); } finally { try { if(rs != null)rs.close(); if(stmt != null) stmt.close(); if(con != null)con.close(); } catch(SQLExceptione) {} } }
4.10 案例:查詢所有
5、JDBC對象介紹
5.1JDBC中的主要類(接口)
<1>DriverManager
<2>Connection
<3>Statement
<4>ResultSet
5.2 DriverManager
我們今後只需要會用DriverManager的getConnection()方法即可:
<1>Class.forName("com.mysql.jdbc.Driver");
<2>String url="jdbc:mysql://localhost:3306/web08";
<3>String username="root"
<4>String password="root"
<5>Connection con=DriverManager.getConnection(url,username,password);
注意:上面代碼可能出現的兩種異常:
<1>ClassNotFoundException:這個異常是在第1句上出現的,出現這個異常有兩個可能:
(1)你沒有給出mysql的jar包;
(2)你把類名打錯了,查看類名是不是com.mysql.jdbc.Driver。
<2>SQLException:這個異常出現在第5句,出現這個異常就是三個參數的問題,往往username和password一般不會出錯,所以需要認真查看url是否打錯
對於DriverManager.registerDriver()方法瞭解即可,因爲我們今後註冊驅動只會用 Class.forName(),而不會使用這個方法
5.3 Connection
Connection最爲重要的方法就是獲取Statement:
(1)Statement stmt=con.createStatement();
後面在學習ResultSet方法時,還要學習一下下面的方法
(2)Statement stmt=con.createStatement(int,int);
5.4 Statement
Statement最爲重要的方法是:
<1>int executeUpdate(String sql):執行更新操作,即執行insert、update、delete語句,其實
這個方法也可以執行create table、alter table,以及drop table等語句,但我們很少會使JDBC
來執行這些語句;
<2>ResultSet executeQuery(String sql):執行查詢語句,執行查詢操作會返回ResultSet,即結果集;
<3>boolean execute():Statement還有一個boolean execute()方法,這個方法可以用來執行 增、刪、改、查所有的SQL語句,該方法返回的是boolean類型,表示SQL語句是否執行成功。
如果使用execute()方法執行的是更新語句,那麼還要調用int getUpdateCount()來獲取insert、 update、delete語句所影響的行數
如果使用execute()方法執行的是查詢語句,那麼還要調用ResultSet、getResultSet()來獲取 select語句的查詢結果
5.5 ResultSet之滾動結果集(瞭解)
ResultSet表示結果集,它是一個二維的表格!ResultSet內部維護一個行光標(遊標),ResultSet提供了一系列的方法來移動遊標:
<1>void beforeFirst():把光標放到第一行的前面,這也是光標默認的位置;
<2>void afterLast():把光標放到最後一行的後面;
<3>boolean first():把光標放到第一行的位置上,返回值表示調控光標是否成功;
<4>boolean last():把光標放到最後一行的位置上;返回值表示調控光標是否成功
<5>boolean isBeforeFirst():當前光標位置是否在第一行前面;
<6>boolean isAfterLast():當前光標位置是否在最後一行的後面;
<7>boolean isFirst():當前光標位置是否在第一行上;
<8>boolean isLast:當前光標位置是否在最後一行上;
<9>boolean next():把光標向下挪一行;
<10>boolean relative(int row):相對位移,當row爲正數時,表示向下移動row行,爲負數時 表示向上移動row行;
<11>boolean absolute(int row):絕對位移,把光標移動到指定行上;
<12>int getRow():返回當前光標所有行
上面方法分爲兩類,一類用來判斷遊標位置的,另一類是用來移動遊標的。如果結果集是不可滾動的,那麼只能使用next()方法來移動遊標。而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用!
結果集是否支持滾動,要從Connection類的createStatement()方法說起,也就是說創建的Statement決定了使用Statement創建的ResultSet是否支持滾動
Statement createStatement(int resultSetType,int resultSetConcurrency)
resultSetType的可選值:
(1)ResultSet.TYPE_FORWARD_ONLY:不滾動結果集
(2)ResultSet.TYPE_SCROLL_INSENSITIVE:滾動結果集,但結果集數據不會再跟隨數據庫而變 化;
(3)ResultSet.TYPE_SCROLL_SENSITIVE:滾動結果集,但結果集數據不會在跟隨數據庫而變化
可以看出,如果想使用滾動的結果集,我們應該選擇TYPE_SCROLL_INSENSITIVE!其實很少有數據庫驅動會支持TYPE_SCROLL_SENSITIVE的特性!通常我們也不需要查詢到的結果集再受到數據庫變化的影響
resultSetConcurrency的可選值:
(1)CONCUR_READ_ONLY:結果集是隻讀的,不能通過修改結果集而反向影響數據庫;
(2)CONCUR_UPDATABLE:結果集是可更新的,對結果集的更新可以反向影響數據庫;
通常可更新結果集這一“高級特性”我們也是不需要的!
獲取滾動結果集的代碼如下:
Connection con=Statement stmt=con.createStatement(ResultSet.TYPE.SCROLL.INSENSITIVE,CONCUR_READ_ONLY);
String sql=....//查詢語句
ResultSet rs=stmt.executeQuery(sql);//這個結果集是可滾動的
5.6 ResultSet之獲取列數據
可以通過next()方法使ResultSet的遊標向下移動,當遊標移動到你需要的行時,就需要來獲取該行的數據了,ResultSet提供了一系列的獲取列數據的方法:
(1)String getString(int columnIndex):獲取指定列的String類型數據;
(2)int getInt(int columnIndex):獲取指定列的int類型數據
(3)double getDouble(int columnIndex):獲取指定列的double類型數據;
(4)boolean getBoolean(int columnIndex):獲取指定列的boolean類型數據
(5)Object getObject(int columnIndex):獲取指定列的Object類型的數據
上面的方法中,參數columnIndex表示列的索引,列索引從1開始,而不是0,這第一點與數組不同,如果你清楚當前列的數據類型,那麼可以使用getInt()之類的方法來獲取,如果你不清楚列的類型,那麼你應該使用getObject()方法類獲取
ResultSet還提供了一套通過列名稱來獲取列數據的方法:
(1)String getSting(String columnName):獲取名稱爲columnName的列的String數據;
(2)int getInt(String columnName):獲取名稱爲columnName的列的int數據
(3)double getDouble(String columnName):獲取名稱爲columnName的列的double數據;
(4)boolean getBoolean(String columnName):獲取名稱爲columnName的列的boolean數據;
(5)Object getObject(String columnName):獲取名稱爲columnName的列的Object數據
6、SQL注入
6.1 什麼是SQL***
在需要用戶輸入的地方,用戶輸入的是SQL語句的片段,最終用戶輸入的SQL片段與我們dao中寫的SQL語句合成一個完整的SQL語句!例如用戶在登錄時輸入的用戶名和密碼都是爲SQL語句的片段
6.2 演示SQL***
首先我們需要創建一張用戶表,用來存儲用戶的信息
CREATE TABLE user( uid CHAR(32)PRIMARY KEY, username VARCHAR(30)UNIQUE KEY NOT NULL, PASSWORD VARCHAR(30) ); INSERT INTO userVALUES('U_1001', 'zs', 'zs'); SELECT * FROM user;
現在用戶表中只有一行記錄,就是zs。
下面我們寫一個login()方法
public void login(String username, String password) { Connectioncon = null; Statementstmt = null; ResultSet rs= null; try { con =JdbcUtils.getConnection(); stmt =con.createStatement(); String sql = "SELECT * FROM user WHERE " + "username='" +username + "'and password='" + password + "'"; rs =stmt.executeQuery(sql); if(rs.next()){ System.out.println("歡迎" +rs.getString("username")); } else { System.out.println("用戶名或密碼錯誤!"); } } catch(Exception e) { throw newRuntimeException(e); } finally { JdbcUtils.close(con,stmt, rs); } }
下面是調用這個方法的代碼:
login("a' or 'a'='a", "a'or 'a'='a");
這行當前會使我們登錄成功,因爲輸入的用戶名和密碼是SQL語句片段,最終與我們的login()方法中的SQL語句組合在一起,我們來看看組合在一起的SQL語句
SELECT * FROM tab_user WHERE username='a'or 'a'='a' and password='a' or 'a'='a'
6.3防止SQL***
過濾用戶輸入的數據中是否包含非法字符;
分步校驗,先使用用戶名來查詢用戶,如果查找到了,再比較密碼;
使用PreparedStatement
6.4 PreparedStatement是什麼?
PreparedStatement叫預編譯聲明
PreparedStatement是Statement的子接口,你可以使用PreparedStatement來替換Statement
PreparedStatement的好處:
(1)防止SQL***
(2)提高代碼的可讀性,以及可維護性
(3)提高效率
6.5 PreparedStatement的使用
使用Connection的prepareStatement(String sql):即創建它時就讓它與一條SQL模板綁定;
調用PreparedStatement的setXXX()系列方法爲問號設置值;
調用executeUpdate()或executeQuery()方法,但要注意,調用沒有參數的方法;
String sql = “select * from tab_studentwhere s_number=?”; PreparedStatement pstmt =con.prepareStatement(sql); pstmt.setString(1, “S_1001”); ResultSet rs = pstmt.executeQuery(); rs.close(); pstmt.clearParameters(); pstmt.setString(1, “S_1002”); rs= pstmt.executeQuery();
在使用Connection創建PreparedStatement對象時需要給出一個SQL模板,所謂SQL模板就是有“?”的SQL語句,其中“?”就是參數
在得到PreparedStatement對象後,調用它的setxxx()方法爲“?”賦值,這樣就可以得到把模板變成一條完整的SQL語句,然後再調用PreparedStatement對象的executeQuery()方法獲取ResultSet對象
注意:PreparedStatement對象獨有的executeQuery()方法是沒有參數的,而Statement的executeQuery()是需要參數(SQL語句)的。因爲在創建PreparedStatement對象時已經讓它與一條SQL模板綁定在一起了,所以在調用它的executeQuery()和executeUpdate()方法時就不再需要參數了。
PreparedStatement最大的好處就是在於重複使用同一模板,給予其不同的參數來重複的使用它。這纔是真正提高效率的原因
所依,在以後的開發中,無論什麼情況,都去使用PreparedStatement,而不是使用Statement.
7、使用JDBC完成分類表CRUD(增刪改查)的操作
7.1 案例分析
使用JDBC對分類表category進行增刪改查操作
7.2 工具類
“獲得連接”和“釋放資源”兩次代碼將在之後的增刪改查所有功能中都存在,開發中一般遇到此種情況,將採用工具類的方法進行抽取,從而達到代碼的重複利用
7.3 獲得連接
7.4 釋放資源
如果釋放資源採用依次關閉三個對象,那麼第一個對象關閉時拋出了異常,後面兩個對象將無法成功釋放資源,通常我們使用try-catch塊進行處理
方法1:多個try-catch塊,將資源釋放,容易理解
方法2:try-catch-finally嵌套,資源釋放時如果出錯,將通知調用者
7.5 使用properties配置文件
開發中獲得連接的4個參數(驅動、URL、用戶名、密碼)通常都存在配置文件中,方便後期維護,程序如果需要更換數據庫,只需要修改配置文件即可
通常情況下,我們習慣使用properties文件,此文件我們將做如下要求:
<1>文件位置:任意,建議src下
<2>文件名稱:任意,擴展名爲properties
<3>文件內容:一行一組數據,格式是“key=value”
(1)key命名自定義,如果是多個單詞,習慣使用點分割。例如:jdbc.driver
(2)value值不支持中文,如果需要使用非英文字符,將進行unicode轉換
7.5.1 創建配置文件
右鍵/New/File,輸入“db.properties”文件名
7.5.2 加載配置文件:ResourceBundle對象
ResourceBundle提供getBundle()方法用於只提供properties文件即可,之後使用getString(key)通過key獲得value的值
7.5.3 獲得連接
7.5.4 加載配置文件:Properties對象(可選)
對應properties文件處理,開發中也會使用Properties對象進行。在v3版本中我們將採用加載properties文件獲得流,然後使用Properties對象進行處理
7.6 實現
7.6.1 模板
7.6.2 添加:insert into
7.6.3 更新:update...set
7.6.4 刪除:delete
7.6.5 通過ID查詢
四、JDBC連接池
1、案例分析
實際開發中“獲得連接”或“釋放資源”是非常消耗系統資源的兩個過程,爲了解決此類性能問題,通常情況我們採用連接池技術,來共享連接Connection
2、連接池概述
2.1 概念
用池來管理Connection,這樣可以重複使用Connection。有了池,所以我們就不用自己來創建Connection,而是通過池來獲取Connection對象。當使用完Connection後,調用Connection的close()方法也不會真的關閉Connection,而是把Connection"歸還"給池。池就可以再利用這個Connection對象了。
2.2 規範
java爲數據庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商需要讓自己的連接池實現這個接口。這樣應用程序纔可以方便的切換不同廠商的連接池!
常見的連接池:DBCP、C3P0
3、自定義連接池
3.1 案例分析
我們編寫自定義連接池,需要完成一下步驟
3.1.1 創建連接池實現(數據源),並實現接口javax.sql.DataSource。因爲我們只使用該接口中getConnection()方法,簡化本案例,我們將自己提供方法1,而沒有實現接口
3.1.2 提供一個集合,用於存放連接,因爲移除/添加操作過多,所以選擇LinkedList
3.1.3 本案例在靜態代碼塊中,爲連接池初始化3個連接
3.1.4 之後程序如果需要連接,調用實現類的getConnection(),本方法將從連接池(容器List)獲得連接。爲了保證當前連接只能提供給一個線程使用,所以我們將連接先從連接池中移除
3.1.5 當用戶使用完連接,釋放資源時,不執行close()方法,而是將連接添加到連接池中
3.2 案例實現
3.2.1 提供容器及初始化
3.2.2 獲得連接
3.2.3 歸還連接
3.2.4 測試使用
爲了體現連接池優勢,我們將採用多線程併發訪問,使同一個連接在不同的時段,被不同的線程使用
3.3 自定義連接池:方法增強
3.3.1 需求
自定義連接池中存在嚴重問題,用戶調用getConnection()獲得連接後,必須使用release()方法進行連接的歸還,如果用戶調用conn.close()將連接真正的釋放,連接池中將出現無連接可用。
此時我們希望,即使用戶調用了close()方法,連接仍歸還給連接池。close()方法原有功能釋放資源,期望功能:將當前連接歸還連接池。說明close()方法沒有我們希望的功能,我們將對close()方法進行增強,從而實現將連接歸還給連接池的功能
3.3.2 方法增強總結
<1>繼承,子類繼承父類,將父類的方法進行腹複寫,從而進行增強
使用前提:必須有父類,且存在繼承關係
<2>裝飾者設計模式,此設計模式專門用於增強方法
使用前提:必須有接口
缺點:需要將接口的所有方法都實現
<3>動態代理:在運行時動態的創建代理類,完成增強操作。與裝飾者相似
使用前提:必須有接口
難點:需要反射技術
<4>字節碼增強,運行時創建目標類子類,從而進行增強
常見第三方框架:cglib、javassist等
3.3.3 裝飾者設計模式
設計模式:專門爲解決一類問題,而編寫的固定格式的代碼。
裝飾者固定結構:接口A,已知實現類C,需要裝飾者創建代理類B
<1>創建類B,並實現接口A
<2>提供類B的構造方法,參數類型爲A,用於接收A接口的其他實現類(C)
<3>給類B添加類型爲A成員變量,用於存放A接口的其他實現類
<4>增強需要的方法
<5>實現不需要增強的方法,方法體重調用成員變量存放的其他實現類對應的方法
3.3.4 實現
3.3.4.1 裝飾類
3.3.4.2 使用裝飾者(包裝類)
將由DriverManager創建的連接,使用裝飾類包裝一下,然後添加到連接池中,構造方法中將容器pool傳遞進去,方便連接的歸還
3.3.4.3 使用連接
4、C3P0連接池
C3P0是開源免費的連接池,目前使用它的開源項目有:Spring、Hibernate等。使用第三方工具需要導入jar包,C3P0使用時還需要添加配置文件c3p0-config.xml
4.1 導入jar包
我們使用的0.9.2版本,需要導入2個jar包
4.2 配置文件
<1>配置文件名稱:c3p0-config.xml(固定)
<2>配置文件位置:src(類路徑)
<3>配置文件內容:命名配置
配置文件內容:默認配置
4.3 常見配置項
4.4 編寫工具類
C3P0提供核心工具類:ComboPooledDataSource,如果要使用連接池,必須創建該類的實例對象。
5、DBCP連接池
DBCP也是一個開源的連接池,是Apache Common成員之一,在企業開發中也比較常見,tomcat內置的連接池
5.1 導入jar包
5.2 配置文件
<1>配置文件名稱:*.properties
<2>配置文件位置:任意,建議src(classpath/類路徑)
<3>配置文件內容:properties不能編寫中文,不支持在STS中修改,必須使用記事本修改內容,否則中文註釋就亂碼了
5.3 常見配置項
5.4 編寫工具類
五、使用DBUtils增刪改查的操作
1、案例分析
如果只使用JDBC進行開發,我們會發現冗餘代碼過多,爲了簡化JDBC開發,我們將採用apache commons組件一個成員:DEUtils。
DBUtils就是JDBC的簡化開發工具包。需要使用技術:連接池(獲得連接),SQL語句都沒有少
2、案例相關知識
2.1、JavaBean組件
javaBean就是一個類,在開發中常用於封裝數據。具有如下特性
<1>需要實現接口:java.io.Serializable,通常偷懶省略了。
<2>提供私有字段:private類型 字段名;
<3>提供getter/setter方法
<4>提供無參構造
3、DBUtils完成CRUD
3.1 概述
DBUtils是java編程中的數據庫操作實用工具,小巧簡單實用
DBUtils封裝了對JDBC的操作,簡化了JDBC操作,可以少寫代碼
DBUtils三個核心功能介紹
<1>QueryRunner中提供對sql語句操作的API
<2>ResultSetHandler接口,用於定義select操作後,怎樣封裝結果集
<3>DBUtils類,它就是一工具類,定義了關閉資源與事務處理的方法
3.2 QueryRunner核心類
<1>QueryRunner(DataSource ds),提供數據源(連接池),DBUtils底層自動維護連接connection
<2>update(String sql,Object...params),執行更新數據
<3>query(String sql,ResultSetHandler<T>rsh,Object...params),執行查詢
3.3 ResultSetHandler結果集處理類
3.4 DBUtils工具類
closeQuietly(Connection conn) 關閉連接,如果有異常,try後不拋
commitAndCloseQuietly(Connection conn) 提交併關閉連接
rollbackAndCloseQuietly(Connection conn) 回滾並關閉連接
3.5 實現
3.5.1 添加
3.5.2 更新
3.5.3 刪除
3.5.4 通過id查詢
3.5.5 查詢所有
3.5.6 總記錄數