前言
這段時間狀態有一點浮躁,希望自己靜下心來。還有特別多的東西還沒有學懂。需要學習的東西非常的多,加油!
一、JDBC復習
Java Data Base Connectivity,java數據庫連接,在需要存儲一些數據,或者拿到一些數據的時候,就需要往數據庫里存取數據。那么java如何連接數據庫呢?需要哪些步驟?
1.1、注冊驅動
1)什么是驅動
驅動就是JDBC實現類,通俗點講,就是能夠連接到數據庫功能的東西就是驅動,由於市面上有很多數據庫,Oracle、MySql等等,所以java就有一個連接數據庫的實現規
范接口,定義一系列的連接數據庫接口(java.sql.Driver接口),但是不提供實現,而每個數據庫廠家來提供這些接口的具體實現,這樣一來,不管使用的是什么數據庫,我
們開發者寫的代碼都是相同的,就不必因為數據庫的不同,而寫法不同,唯一的不同就是數據庫驅動不一樣,使用mysql,那么就必須使用mysql的驅動,使用Oracle就必
須使用oracle的驅動實現類。
看下面mysql連接數據的原理圖,看看驅動是在哪里,起什么作用?
2)DriverManager,一個工具類,是用於操作管理JDBC實現類的
原始寫法:DriverManager.register(new Driver()); //因為使用的是MySql,所以在導包時就需要導入com.mysql.jdbc.Driver 現在寫法:Class.forName("com.mysql.jdbc.Driver"); //不用導包,會執行com.mysql.jdbc.Driver類中的靜態代碼塊,其靜態代碼塊的內容為 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
分析:
會發現第二種加載驅動的方法的底層其實就是第一種加載驅動。為什么要這樣呢?原因很簡單, 第一種是硬編程,直接將數據庫驅動給寫死了,無法擴展,如果使用第一
種,那么連接的數據庫只能是mysql,因為導包導的是mysql的驅動包,如果換成Oracle,就會報錯,需要在代碼中將Oracle的驅動包導入,這樣很麻煩,而第二種寫法就不
一樣了,第二種是使用的字符串方法注冊驅動的,我們只需要將該字符串提取到一個配置文件中,以后想換成oracle數據庫,只需要將該字符串換成oracle驅動的類全名即可
,而不需要到代碼中去修改什么東西。
1.2、獲取連接
使用DriverManage來獲得連接,因為DriverManager是驅動實現類的管理者
Connection conn = DriverManager.getConnection(url,user,password);
url:確定數據庫服務器的位置,端口號,數據庫名
jdbc:mysql://localhost:3306/db
user:登錄名稱,默認root
password:密碼,默認root
這里只是說mysql,別的數據庫,url格式就不同了。
MySQL jdbc:mysql://localhost:3306/db 默認端口是3306,粗體為連接時使用的數據庫名
Oracle jdbc:oracle:thin:@localhost:1521:db 默認端口號1521
DB2 jdbc:db2://localhost:6789/db 默認端口號6789
SQLServer jdbc:microsoft:sqlserver://localhost:1433;databaseName=db 默認端口號1433
SQLServer 2005 jdbc:sqlserver://localhost:1433;databaseName=db 默認端口號1433
1.3、獲取執行sql對象,PreparedStatement對象
通過Connection對象獲取Statement或者PraparedStament對象(使用它)處理sql
1)Statement
Statement st = conn.createStatement(); //獲取sql語句執行對象
st.excuteUpdate(sql); //執行增刪改語句
st.excuteQuery(sql); //執行查詢語句
sql語句必須是完整的。
2)PraparedStatment
sql語句可以不是完整的,可以將參數用?替代,然后在預編譯后加入未知參數
PraparedStatment ps = conn.prapareStatement(sql); //獲取sql語句執行對象praparedStatment
賦值
ps.setInt(Index,value); ps.setString(index,value); //可以設置很多中類型,index從1開始,代表sql語句中的第幾個未知參數,
ps.excuteUpdate(); //執行增刪改語句
ps.excuteQuery(sql); //執行查詢語句
這兩個的區別,常使用的是PraparedStatment對象,因為它可以預編譯,效率高,可以設置參數等等優點。
1.4、獲取結果集對象
int count = ps.excuteUpdate(); //執行增刪改的sql語句時,返回一個int類型的整數,代表數據庫表影響的行數,
Result result = ps.excuteQuery(); //執行查詢sql語句時,返回一個結果集對象,該對象裝着所有查詢到的數據信息,一行一行的存儲數據庫表信息。
1.5、處理結果集
對查詢到的Result結果進行處理,拿到所有數據,並封裝成對象。
while(rs.next()){ 獲取行數據的第一種方式 rs.getString(index);//index代表第幾列,從1開始 獲取行數據的第二中方式 rs.getString(string); //string:代表字段名稱。 }
總結:java的JDBC就分為5步,4個屬性
屬性:driver、url、user、password
五步:
注冊驅動、獲取連接、獲取執行sql語句對象、獲取結果集對象、處理結果。
二、JDBC的CURD操作
創建(Create)、更新(Update)、讀取(Retrieve)和刪除(Delete)操作
查詢所有(讀取Retrieve)
2.1、查詢所有記錄讀取(Retrieve)

@Test public void findAll() throws Exception{ //1 注冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2 獲得連接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"); //3語句執行者,sql語句 Statement st = conn.praparedStatement("select * from t_user"); //4 執行查詢語句 ResultSet rs = st.executeQuery(); //5處理數據 // * 如果查詢多個使用,使用while循環進行所有數據獲取 // * 技巧:如果查詢結果最多1條,使用 if(rs.next()) { 查詢到了 } else { 沒有數據 } while(rs.next()){ int id = rs.getInt(1); String username = rs.getString(2); String password =rs.getString(3); System.out.print(id + ", "); System.out.print(username + ", "); System.out.println(password); } //6釋放資源 rs.close(); st.close(); conn.close(); }
2.2、增加操作(創建Create)

@Test public void save() throws Exception{ //1 注冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2 獲得連接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"); //3語句執行者 Statement st = conn.praparedStatement("insert into t_user(username,password) values(?,?)"); //3.1賦值 st.setString(1,"xiaoming"); st.setString(2,"123"); //4 執行DML語句 int r = st.executeUpdate(); //5處理數據 System.out.println(r); //6釋放資源 //rs.close(); st.close(); conn.close(); }
2.3、更新操作 (Update)

@Test public void update() throws Exception{ //1 注冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2 獲得連接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"); //3語句執行者 Statement st = conn.praparedStatement("update t_user set username = ? where id = ? "); //3.1賦值參數 st.setString(1,"xiaoye"); st.setInt(2,2); //4 執行DML語句 int r = st.executeUpdate(); //5處理數據 System.out.println(r); //6釋放資源 //rs.close(); st.close(); conn.close(); }
2.4、刪除操作(delete)

@Test public void delete() throws Exception{ //1 注冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2 獲得連接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"); //3語句執行者 Statement st = conn.praparedStatement("delete from t_user where id = ?"); //3.1賦值參數 st.setInt(1,2); //4 執行DML語句 int r = st.executeUpdate(); //5處理數據 System.out.println(r); //6釋放資源 //rs.close(); st.close(); conn.close(); }
也可以用我前面寫的JDBC連接的工具類去連接!
三、數據庫連接池
在上面,我們在進行CRUD時,一直重復性的寫一些代碼,比如最開始的注冊驅動,獲取連接代碼,一直重復寫,通過編寫一個獲取連接的工具類后,解決了這個問題,但是又
會出現新的問題,每進行一次操作,就會獲取一個連接,用完之后,就銷毀,就這樣一直新建連接,銷毀連接,新建,銷毀,連接Connection 創建與銷毀 比較耗時的。所以應
該要想辦法解決這個問題?
解決方法:
連接池就是為了解決這個問題而出現的一個方法,為了提高性能,開發連接池,連接池中一直保持有n個連接,供調用者使用,調用者用完返還給連接池,繼續給別的調用
者使用,比如連接池中一開始就有10個連接,當有5個用戶拿走了5個連接后,池中還剩5個,當第6個用戶在去池中拿連接而前面5個連接還沒歸還時,連接池就會新建一個
連接給第六個用戶,讓池中一直能夠保存最少5個連接,而當這樣新建了很多連接后,用戶歸還連接回來時,會比原先連接池中的10個連接更多,連接池就會設置一個池中
最大空閑的連接數,如果超過了這個數,就會將超過的連接給釋放掉,連接池就是這樣工作的。
3.1、連接池概述
數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是再重新建立一個;釋放空閑時間超過最大空閑時間的數據庫連接來避
免因為沒有釋放數據庫連接而引起的數據庫連接遺漏。這項技術能明顯提高對數據庫操作的性能。
3.2、比較應用程序直接獲取連接和使用連接池
1)應用程序直接獲取連接
缺點:用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。
假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,並且極易造成數據庫服務器內存溢出、宕機。
2)使用連接池連接
目的:解決建立數據庫連接耗費資源和時間很多的問題,提高性能。
四、常用的數據庫連接池
現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之為數據源,
數據源中都包含了數據庫連接池的實現。
也有一些開源組織提供了數據源的獨立實現:
DBCP 數據庫連接池
C3P0 數據庫連接池
實際應用時不需要編寫連接數據庫代碼,直接從數據源獲得數據庫的連接。程序員編程時也應盡量使用這些數據源的實現,以提升程序的數據庫訪問性能。
DBCP、C3P0、tomcat內置連接池(JNDI)是我們開發中會用到的。
4.1、DBCP連接池
1)概述
DBCP 是 Apache 軟件基金組織下的開源連接池實現,使用DBCP數據源,應用程序應在系統中增加如下兩個 jar 文件:
Commons-dbcp.jar:連接池的實現
Commons-pool.jar:連接池實現的依賴庫
Tomcat 的連接池正是采用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。
1)獲取連接的兩種方式
兩種方式獲得連接,使用配置文件,不使用配置文件
1.1)不使用配置文件,自己手動設置參數
第一:導包
第二:測試例子

package com.zyh.util; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.commons.dbcp.BasicDataSource; public class TestDBCP { public static void main(String[] args) { Connection conn = TestDBCP.getConnection(); try { String sql = "select id,name,price from book"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()){ String id = rs.getString(1); String name = rs.getString(2); double price = rs.getDouble(3); System.out.println("id="+id+",name="+name+",price="+price); System.out.println("-------------------------"); } } catch (SQLException e) { e.printStackTrace(); } } public static Connection getConnection(){ //創建核心類 BasicDataSource bds = new BasicDataSource(); //配置4個基本參數 bds.setDriverClassName("com.mysql.jdbc.Driver"); bds.setUrl("jdbc:mysql:///book"); bds.setUsername("root"); bds.setPassword("654321"); //管理連接配置 bds.setMaxActive(50); //最大活動數 bds.setMaxIdle(20); //最大空閑數 bds.setMinIdle(5); //最小空閑數 bds.setInitialSize(10); //初始化個數 //獲取連接 try { Connection conn = bds.getConnection(); return conn; } catch (SQLException e) { throw new RuntimeException(e); } } }
1.2)使用配置文件,參數寫入配置文件中即可,也就是通過配置文件來配置驅動、用戶名、密碼、等信息
第一:導包
和上面的一樣的
第二:導入配置文件dbcpconfig.properties

#連接設置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/book username=root password=654321 #<!-- 初始化連接 --> initialSize=10 #最大連接數量 maxActive=50 #<!-- 最大空閑連接 --> maxIdle=20 #<!-- 最小空閑連接 --> minIdle=5 #<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等於60秒 --> maxWait=60000 #JDBC驅動建立連接時附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;] #注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們。 connectionProperties=useUnicode=true;characterEncoding=gbk #指定由連接池所創建的連接的自動提交(auto-commit)狀態。 defaultAutoCommit=true #driver default 指定由連接池所創建的連接的只讀(read-only)狀態。 #如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動並不支持只讀模式,如:Informix) defaultReadOnly= #driver default 指定由連接池所創建的連接的事務級別(TransactionIsolation)。 #可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED
第三:獲取連接,測試例子

package com.zyh.util; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class TestDBCPPro { public static void main(String[] args) { Connection conn = TestDBCPPro.getConnection(); try { String sql = "select id,name,price from book"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()){ String id = rs.getString(1); String name = rs.getString(2); double price = rs.getDouble(3); System.out.println("id="+id+",name="+name+",price="+price); System.out.println("-------------------------"); } } catch (SQLException e) { e.printStackTrace(); } } public static Connection getConnection(){ //通過類加載器獲取指定配置文件的輸入流,TestDBCPPro是一個類名 InputStream in = TestDBCPPro.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); try { properties.load(in); //加載配置文件,獲得配置信息 DataSource ds = BasicDataSourceFactory.createDataSource(properties); Connection conn = ds.getConnection(); return conn; } catch (Exception e) { e.printStackTrace(); } return null; } }
1.3)編寫一個數據源工具類

package com.zyh.util; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.imageio.stream.FileImageInputStream; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class DBCPUtils { private static DataSource ds = null; static{ Properties prop = new Properties(); try { prop.load(DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));//根據DBCPUtil的classes的路徑,加載配置文件 ds = BasicDataSourceFactory.createDataSource(prop);//得到一個數據源 } catch (Exception e) { throw new ExceptionInInitializerError("初始化錯誤,請檢查配置文件"); } } public static Connection getConnection(){ try { return ds.getConnection(); } catch (SQLException e) { throw new RuntimeException("服務器忙。。。"); } } public static void release(Connection conn,Statement stmt,ResultSet rs){ //關閉資源 if(rs!=null){ try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if(stmt!=null){ try { stmt.close(); } catch (Exception e) { e.printStackTrace(); } stmt = null; } if(conn!=null){ try { conn.close();//關閉 } catch (Exception e) { e.printStackTrace(); } conn = null; } } }
4.2、C3P0
1)導包
2)從配置信息中獲取 配置文件必須為xml(c3p0-config.xml)

<c3p0-config> <!-- 默認配置,如果沒有指定則使用這個配置 --> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/book</property> <property name="user">root</property> <property name="password">654321</property> <property name="checkoutTimeout">30000</property> <property name="idleConnectionTestPeriod">30</property> <property name="initialPoolSize">10</property> <property name="maxIdleTime">30</property> <property name="maxPoolSize">100</property> <property name="minPoolSize">10</property> <property name="maxStatements">200</property> <user-overrides user="test-user"> <property name="maxPoolSize">10</property> <property name="minPoolSize">1</property> <property name="maxStatements">0</property> </user-overrides> </default-config> <!-- 命名的配置 --> <named-config name="jxpx"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/myums</property> <property name="user">root</property> <property name="password">root</property> <!-- 如果池中數據連接不夠時一次增長多少個 --> <property name="acquireIncrement">5</property> <property name="initialPoolSize">20</property> <property name="minPoolSize">10</property> <property name="maxPoolSize">40</property> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> </named-config> </c3p0-config>
從配置文件中看,需要注意一個地方,一個是default-config,一個是name-config,兩者都區別在於創建核心類對象時,如果將name-config作為參數傳進去,
那么將會調用name-config下的配置信息,否則將調用default-config下的配置信息,
兩種方式使用c3p0,加參數,使用named-config 的配置信息,不加參數,自動加載配置信息,加載的是default-config中的信息。
//1 c3p0...jar 將自動加載配置文件。規定:WEB-INF/classes (src) c3p0-config.xml,也就是將配置文件放在src下就會自動加載。 //ComboPooledDataSource dataSource = new ComboPooledDataSource(); //自動從配置文件 <default-config> ComboPooledDataSource dataSource = new ComboPooledDataSource(); //手動指定配置文件 <named-config name="jxpx"> Connection conn = dataSource.getConnection(); System.out.println(conn);
3)編寫一個數據源工具類
這個工具類的xml是:

<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/book/property> <property name="user">root</property> <property name="password">654321</property> <property name="initialPoolSize">10</property> <property name="maxIdleTime">30</property> <property name="maxPoolSize">100</property> <property name="minPoolSize">10</property> </default-config> </c3p0-config>
工具類

package com.zyh.util; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Util { //得到一個數據源 private static DataSource dataSource = new ComboPooledDataSource(); //從數據源中得到一個連接對象 public static Connection getConnection(){ try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException("服務器錯誤"); } } public static void release(Connection conn,Statement stmt,ResultSet rs){ //關閉資源 if(rs!=null){ try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if(stmt!=null){ try { stmt.close(); } catch (Exception e) { e.printStackTrace(); } stmt = null; } if(conn!=null){ try { conn.close();//關閉 } catch (Exception e) { e.printStackTrace(); } conn = null; } } }
4.3、用JavaWeb服務器管理數據源:Tomcat
開發JavaWeb應用,必須使用一個JavaWeb服務器,JavaWeb服務器都內置數據源。
Tomcat:(DBCP)
數據源只需要配置服務器即可。
配置數據源的步驟:
1)拷貝數據庫連接的jar到tomcatlib目錄下
2)配置數據源XML文件
a)如果把配置信息寫在tomcat下的conf目錄的context.xml中,那么所有應用都能使用此數據源。
b)如果是在當前應用的META-INF中創建context.xml, 編寫數據源,那么只有當前應用可以使用。

<Context> <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" username="root" password="654321" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/book" maxActive="8" maxIdle="4"/> </Context>
在servlet中進行測試:
Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); //path dataSource = (DataSource)envCtx.lookup("jdbc/datasource"); //context.xml中resource的name
注意:此種配置下,驅動jar文件需放置在tomcat的lib下
擴展:
JNDI技術介紹:
1)JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應於J2SE中的javax.naming包。
2)這套API的主要作用在於:
它可以把Java對象放在一個容器中(JNDI容器),並為容器中的java對象取一個名稱,以后程序想獲得Java對象,只需通過名稱檢索即可。
3)其核心API為Context,它代表JNDI容器,其lookup方法為檢索容器中對應名稱的對象。