01_数据库连接池,数据源,ResultSetMetaData,jdbc优化
一、數(shù)據(jù)庫連接池
1. 什么是連接池
傳統(tǒng)的開發(fā)模式下,Servlet處理用戶的請求,找Dao查詢數(shù)據(jù),dao會創(chuàng)建與數(shù)據(jù)庫之間的連接,完成數(shù)據(jù)查詢后會關閉數(shù)據(jù)庫的鏈接。
這樣的方式會導致用戶每次請求都要向數(shù)據(jù)庫建立鏈接而數(shù)據(jù)庫創(chuàng)建連接通常需要消耗相對較大的資源,創(chuàng)建時間也較長。假設網站一天10萬訪問量,數(shù)據(jù)庫服務器就需要創(chuàng)建10萬次連接,極大的浪費數(shù)據(jù)庫的資源,并且極易造成數(shù)據(jù)庫服務器內存溢出、宕機。
?
解決方案就是數(shù)據(jù)庫連接池
連接池就是數(shù)據(jù)庫連接對象的一個緩沖池
我們可以先創(chuàng)建10個數(shù)據(jù)庫連接緩存在連接池中,當用戶有請求過來的時候,dao不必創(chuàng)建數(shù)據(jù)庫連接,而是從數(shù)據(jù)庫連接池中獲取一個,用完了也不必關閉連接,而是將連接換回池子當中,繼續(xù)緩存
?
使用數(shù)據(jù)庫連接池可以極大地提高系統(tǒng)的性能
?
2. 實現(xiàn)數(shù)據(jù)庫連接池
jdbc統(tǒng)一了數(shù)據(jù)庫的操作? 定義了規(guī)范
jdbc針對數(shù)據(jù)庫連接池也定義的接口java.sql.DataSource,所有的數(shù)據(jù)庫連接池實現(xiàn)都要實現(xiàn)該接口
該接口中定義了兩個重載的方法
Connection getConnection()
Connection getConnection(String?username,String?password)
?
數(shù)據(jù)庫連接池實現(xiàn)思路
1)定義一個類實現(xiàn)java.sql.DataSource接口
2)定義一個集合用于保存Connection對象,由于頻繁地增刪操作,用LinkedList比較好
3)實現(xiàn)getConnection方法,在方法中取出LinkedList集合中的一個連接對象返回
注意:
??? 返回的Connection對象不是從集合中獲得,而是刪除
??? 用戶用完Connection,會調用close方法釋放資源,此時要保證連接換回連接池,而不是關閉連接
??? 重寫close方法是難點,解決方案:裝飾設計模式、動態(tài)代理
?
二、 數(shù)據(jù)源
通常我們把DataSource的實現(xiàn),按其英文含義稱之為數(shù)據(jù)源,數(shù)據(jù)源中都包含了數(shù)據(jù)庫連接池的實現(xiàn)。
?
一些開源組織提供了數(shù)據(jù)源的獨立實現(xiàn),常用的有:
DBCP 數(shù)據(jù)庫連接池
C3P0 數(shù)據(jù)庫連接池
1.? DBCP 數(shù)據(jù)源
介紹
DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn)
tomcat服務器就是使用DBCP作為數(shù)據(jù)庫連接池
使用DBCP數(shù)據(jù)源,需要導入兩個jar包
Commons-dbcp.jar:連接池的實現(xiàn)
Commons-pool.jar:連接池實現(xiàn)的依賴庫
?
DBCP核心 API
BasicDataSource
數(shù)據(jù)源實現(xiàn)
BasicDataSourceFactory
用于創(chuàng)建數(shù)據(jù)源的工廠類
?
dbcp 創(chuàng)建連接池
方法1:?直接創(chuàng)建對象,設置參數(shù)
BasicDataSource bds = new BasicDataSource();
?
// 設置連接數(shù)據(jù)庫需要的配置信息
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/jdbc3");
bds.setUsername("root");
bds.setPassword("root");
?
// 設置連接池的參數(shù)
bds.setInitialSize(5);
bds.setMaxActive(10);
?
ds = bds
?
方法2: 通過工廠類創(chuàng)建對象,讀取配置文件
try {
?? Properties prop =new Properties();
?? // 讀配置文件
?? InputStream in =
?? JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
?? prop.load(in);
?? ds =BasicDataSourceFactory.createDataSource(prop);
}catch (Exception e) {
?? throw newExceptionInInitializerError(e);
}
?
配置文件為dbcpconfig.properties
#連接設置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc3
username=root
password=root
?
#<!-- 初始化連接 -->
initialSize=5
?
#最大連接數(shù)量
maxActive=10
?
#<!-- 最大空閑連接 -->
maxIdle=10
?
?
#<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等于60秒 -->
maxWait=60000
?
?
#JDBC驅動建立連接時附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;]
#注意:"user" 與"password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們。
connectionProperties=useUnicode=true;characterEncoding=gbk
?
#指定由連接池所創(chuàng)建的連接的自動提交(auto-commit)狀態(tài)。
defaultAutoCommit=true
?
#driver default 指定由連接池所創(chuàng)建的連接的只讀(read-only)狀態(tài)。
#如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動并不支持只讀模式,如:Informix)
defaultReadOnly=
?
#driver default 指定由連接池所創(chuàng)建的連接的事務級別(TransactionIsolation)。
#可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED,REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
2.? C3P0 數(shù)據(jù)源
介紹
c3p0是一個開源的jdbc連接池,我們熟悉的Hibernate和 Spring 框架使用的都是該數(shù)據(jù)源
?
創(chuàng)建連接池對象
方法1:直接創(chuàng)建對象,設置參數(shù)
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc3");
cpds.setUser("root");
cpds.setPassword("root");
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(15);
方法2:讀取配置文件
ComboPooledDataSource cpds = newComboPooledDataSource("itcast");
配置文件為c3p0-config.xml 該文件需要放在類路徑下
<c3p0-config>
?
?? <default-config>
?? ?? <!—- 默認配置 –->
????? <propertyname="initialPoolSize">5</property>
????? <propertyname="maxPoolSize">15</property>
????? <propertyname="driverClass">com.mysql.jdbc.Driver</property>
????? <propertyname="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
????? <propertyname="user">root</property>
????? <propertyname="password">root</property>
?? </default-config>
?? <named-configname="xwh">
????? <propertyname="initialPoolSize">5</property>
????? <propertyname="maxPoolSize">15</property>
????? <propertyname="driverClass">com.mysql.jdbc.Driver</property>
????? <propertyname="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
????? <propertyname="user">root</property>
????? <propertyname="password">root</property>
?? </named-config>
</c3p0-config>
?
三、ResultSetMetaData對象
元數(shù)據(jù),可以理解為描述數(shù)據(jù)的數(shù)據(jù)
jdbc中的元數(shù)據(jù)是指數(shù)據(jù)庫、表、列的定義信息
?
ResultSetMetaData對象表示結果集 ResultSet對象的元數(shù)據(jù)
獲得該對象:
ResultSetMetaDatametaData = rs.getMetaData();
?
常用方法:
getColumnCount() ?返回resultset對象的列數(shù)
getColumnName(int?column) ?獲得指定列的名稱
getColumnTypeName(int?column) 獲得指定列的類型
?
四、jdbc優(yōu)化
使用jdbc對數(shù)據(jù)庫進行crud操作時,會有很多重復的代碼,仔細分析不難發(fā)現(xiàn)其實變化的只是其中幾行代碼
?
對于 cud(增刪改) 操作,代碼幾乎完全一樣, 唯一的區(qū)別就是sql語句不同,我們完全可以把相同的代碼抽取出來定義在一個工具方法中,然后定義一個參數(shù)來接收sql語句
?
對于 r(查詢) 操作,除SQL語句不同之外,根據(jù)操作的實體不同,對ResultSet結果集的處理也有所不相同,因此可義一個query方法,除以參數(shù)形式接收變化的SQL語句外,可以使用策略模式由qurey方法的調用者決定如何把ResultSet中的數(shù)據(jù)映射到實體對象中
?
優(yōu)化后的工具類 JdbcUtils
// 通用的增刪改方法
public static int update(String sql, Object[] params) throws SQLException {
?? Connection conn =null;
?? PreparedStatementpstmt = null;
?? ResultSet rs = null;
?? try {
????? // 獲得連接
????? conn =getConnection();
????? // 預編譯sql
????? pstmt =conn.prepareStatement(sql);
????? // 將參數(shù)設置進去
????? for(int i=0;?params!=null&&i<params.length; i++) {
???????? pstmt.setObject(i+1,params[i]);
????? }
????? // 發(fā)送sql
????? int num = pstmt.executeUpdate();
????? return num;
?? } finally {
????? // 釋放資源
????? release(conn,pstmt, rs);
?? }
}
?
// 優(yōu)化查詢
public static Object query(String sql, Object[] params,ResultSetHandler rsh) throws SQLException {
?? Connection conn =null;
?? PreparedStatementpstmt = null;
?? ResultSet rs = null;
?? try {
????? // 獲得連接
????? conn =getConnection();
????? // 預編譯sql
????? pstmt =conn.prepareStatement(sql);
????? // 將參數(shù)設置進去
????? for(int i=0; params!=null&&i<params.length;i++) {
???????? pstmt.setObject(i+1,params[i]);
????? }
????? // 發(fā)送sql
????? rs =pstmt.executeQuery();
????? // 不知道別人想如何處理結果集
????? // 干脆想別人所要一個結果集的處理器
????? // 為了讓當前代碼繼續(xù),定義一個結果集處理器接口
????? // 策略模式, 規(guī)定算法,具體的算法留給將來的調用者實現(xiàn)
????? Object obj =rsh.handle(rs);
????? return obj;
?? } finally {
????? // 釋放資源
????? release(conn,pstmt, rs);
?? }
}
?
?
結果集處理器接口
public interface ResultSetHandler {
?? // 處理結果集的方法
?? public Objecthandle(ResultSet rs);
}
?
實現(xiàn)類:
BeanListHandler
public class BeanListHandler implements ResultSetHandler{
?
?? private Classclazz;
?? publicBeanListHandler(Class clazz) {
????? this.clazz =clazz;
?? }
?? public Objecthandle(ResultSet rs) {
????? try {
???????? // 取出結果集所有的記錄,封裝到bean,存入list返回
???????? List list =new ArrayList();
???????? while(rs.next()) {
??????????? Objectbean = clazz.newInstance();
??????????? // 獲得元數(shù)據(jù)
??????????? ResultSetMetaDatametaData = rs.getMetaData();
??????????? // 獲得列的數(shù)量
??????????? intcount = metaData.getColumnCount();
??????????? // 遍歷列
??????????? for(inti=1; i<=count; i++) {
??????????????? // 取列名
??????????????? StringcolumnName = metaData.getColumnName(i);
??????????????? // 取這列的值
??????????????? Objectvalue = rs.getObject(columnName);
??????????????? // 反射出屬性
??????????????? Fieldfield = clazz.getDeclaredField(columnName);
??????????????? // 設置屬性
??????????????? field.setAccessible(true);
??????????????? field.set(bean,value);
??????????? }
??????????? // 加入list
??????????? list.add(bean);
???????? }
???????? returnlist;
????? } catch(Exception e) {
???????? throw newRuntimeException(e);
????? }
?? }
?
}
?
BeanHandler
public class BeanHandler implements ResultSetHandler {
?? private Classclazz;
?? publicBeanHandler(Class clazz) {
????? this.clazz =clazz;
?? }
?? public Objecthandle(ResultSet rs) {
????? // 不知道有幾列數(shù)據(jù),不知道列名,不知道封裝到什么樣的bean
????? // 表的列明和javabean的字段名一致
????? try {
???????? if(rs.next()){
??????????? // 創(chuàng)建bean
??????????? Objectbean = clazz.newInstance();
??????????? // 封裝數(shù)據(jù)
??????????? // 獲得結果集的元數(shù)據(jù)
??????????? ResultSetMetaDatametaData = rs.getMetaData();
??????????? intcount = metaData.getColumnCount();
??????????? // 迭代取每一列的數(shù)據(jù)
??????????? for(inti=1; i<=count; i++) {
??????????????? // 獲得列名? username
??????????????? StringcolumnName = metaData.getColumnName(i);
??????????????? // 獲得數(shù)據(jù)ddd
??????????????? Objectvalue = rs.getObject(columnName);
??????????????? // 根據(jù)列名反射出映射的屬性 username
??????????????? Fieldfield = clazz.getDeclaredField(columnName);
??????????????? // 為屬性賦值
??????????????? field.setAccessible(true);
??????????????? field.set(bean,value);
??????????? }
??????????? returnbean;
???????? }
???????? return null;
????? } catch(Exception e) {
???????? throw newRuntimeException(e);
????? }
?? }
?
}
?
ArrayHandler
// 取出第一行的所有記錄存入一個Object數(shù)組
public class ArrayHandler implements ResultSetHandler {
?
?? public Objecthandle(ResultSet rs) {
????? try {
???????? if(rs.next()) {
??????????? // 指向了第一行的記錄
??????????? // 獲得元數(shù)據(jù)
??????????? ResultSetMetaDatametaData = rs.getMetaData();
??????????? // 獲得列數(shù)
??????????? intcount = metaData.getColumnCount();
??????????? // 創(chuàng)建數(shù)組
??????????? Object[]arr = new Object[count];
??????????? // 迭代所有列的值,存入數(shù)組
??????????? for(inti=1; i<=count; i++) {
??????????????? Objectvalue = rs.getObject(i); // 獲得指定列的值
??????????????? arr[i-1]= value;
??????????? }
??????????? returnarr;
???????? }
???????? return null;
????? } catch(Exception e) {
???????? throw newRuntimeException(e);
????? }
?? }
}
?
批處理
?
處理大數(shù)據(jù)
Clob Character large Object
text
?
Blob binary?large object
總結
以上是生活随笔為你收集整理的01_数据库连接池,数据源,ResultSetMetaData,jdbc优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 收割机变速箱回油怎么办?
- 下一篇: 2015款ix35有没有刹车片灯?