JDBC連接池&DBUtils
使用連接池改造JDBC的工具類:
1.1.1 需求:
傳統(tǒng)JDBC的操作,對連接的對象銷毀不是特別好.每次創(chuàng)建和銷毀連接都是需要花費(fèi)時間.可以使用連接池優(yōu)化的程序.
- 在程序開始的時候,可以創(chuàng)建幾個連接,將連接放入到連接池中.用戶使用連接的時候,可以從連接池中進(jìn)行獲取.用完之后,可以將連接歸還連接池.
1.1.2 分析:
1.1.2.1 技術(shù)分析:
【自定義連接池】(了解)
SUN公司提供了一個連接池的接口.(javax.sql.DataSource).
定義一個連接池:實(shí)現(xiàn)這個接口.
使用List集合存放多個連接的對象.
【自定義連接池的代碼】
public class MyDataSource implements DataSource{
// 創(chuàng)建一個List集合用于存放多個連接對象.
private List<Connection> list = new ArrayList<Connection>();
// 在程序開始的時候饺汹,初始化幾個連接,將連接存放到list中.
public MyDataSource() {
// 初始化3個連接:
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
@Override
// 獲得連接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
// 歸還連接的方法:
public void addBack(Connection conn){
list.add(conn);
}
...
}
【自定義連接池中問題及如何解決】
? 問題?
1.如果使用自定義連接池,那么需要額外記住自定義連接池中的API.
2.能不能使用面向接口的編程方式.
? 解決:
不額外提供API方法房铭,就可以解決上述兩個問題>澄觥Jァ!
能不能還調(diào)用Connection的close方法.能不能增強(qiáng)Connection的close方法,原有的銷毀變?yōu)闅w還!!!
? 如何增強(qiáng)Connection的close方法:
增強(qiáng)一個Java類中的某個方法有幾種方式???
一種方式:繼承的方式
能夠控制這個類的構(gòu)造的時候才可以使用繼承
二種方式:裝飾者模式方式
包裝對象和被包裝的對象都要實(shí)現(xiàn)相同的接口
包裝的對象中需要獲得到被包裝對象的引用
缺點(diǎn):如果接口的方法比較多增強(qiáng)其中的某個方法其他的功能的方法需要原有調(diào)用
三種方式:動態(tài)代理的方式
被增強(qiáng)的對象實(shí)現(xiàn)接口就可以
【繼承和裝飾者的案例】
/
* 繼承的方式增強(qiáng)一個類中某個方法:
*/
class Man{
public void run(){
System.out.println("跑....");
}
}
class SuperMan extends Man{
public void run(){
// super.run();
System.out.println("飛....");
}
}
/**
* 使用裝飾者的方式完成類的方法的增強(qiáng)
*/
interface Waiter{
public void server();
}
class Waiteress implements Waiter{
@Override
public void server() {
System.out.println("服務(wù)...");
}
}
class WaiteressWrapper implements Waiter{
private Waiter waiter;
public WaiteressWrapper(Waiter waiter) {
this.waiter = waiter;
}
@Override
public void server() {
System.out.println("微笑...");
// this.waiter.server();
}
}
【使用裝飾者模式增強(qiáng)Connection的close方法】
public class MyConnection implements Connection{
private Connection conn;
private List<Connection> list;
public MyConnection(Connection conn,List<Connection> list) {
this.conn = conn;
this.list = list;
}
@Override
public void close() throws SQLException {
list.add(conn);
}
...
}
連接池的getConnection方法: @Override
// 獲得連接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
MyConnection myConn = new MyConnection(conn, list);
return myConn;
}
【常見的開源的數(shù)據(jù)庫連接池】:
DBCP:
DBCP(DataBase connection pool),數(shù)據(jù)庫連接池苇侵。是 apache 上的一個 java 連接池項(xiàng)目乎串,也是 tomcat 使用的連接池組件凡傅。單獨(dú)使用dbcp需要2個包:commons-dbcp.jar,commons-pool.jar由于建立數(shù)據(jù)庫連接是一個非常耗時耗資源的行為旺上,所以通過連接池預(yù)先同數(shù)據(jù)庫建立一些連接瓶蚂,放在內(nèi)存中,應(yīng)用程序需要建立數(shù)據(jù)庫連接時直接到連接池中申請一個就行抚官,用完后再放回去扬跋。
C3P0:
C3P0是一個開源的JDBC連接池阶捆,它實(shí)現(xiàn)了數(shù)據(jù)源和JNDI綁定凌节,支持JDBC3規(guī)范和JDBC2的標(biāo)準(zhǔn)擴(kuò)展。目前使用它的開源項(xiàng)目有Hibernate洒试,Spring等倍奢。
Tomcat內(nèi)置連接池:
【DBCP連接池的使用】
第一步:引入DBCP連接池的jar包.
第二步:編寫DBCP代碼:
手動設(shè)置參數(shù):
配置文件設(shè)置參數(shù):
【DBCP連接池的使用】
@Test
/**
* 手動方式:
*/
public void demo1(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///web_07");
dataSource.setUsername("root");
dataSource.setPassword("123");
try{
// 獲得連接:
conn = dataSource.getConnection();
// 編寫SQL:
String sql = "select * from category";
// 預(yù)編譯SQL:
stmt = conn.prepareStatement(sql);
// 執(zhí)行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
@Test
/**
* 配置文件方式:
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
Properties properties = new Properties();
try{
properties.load(new FileInputStream("src/dbcpconfig.properties"));
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
// 獲得連接:
conn = dataSource.getConnection();
// 編寫SQL:
String sql = "select * from category";
// 預(yù)編譯SQL:
stmt = conn.prepareStatement(sql);
// 執(zhí)行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
【C3P0連接池的使用】
第一步:引入C3P0連接池的jar包
- 配置文件 c3p0.properties
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- This is default config! -->
<default-config>
<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>
</default-config>
<!-- This is my config for mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF8</property>
<property name="user">root</property>
<property name="password"></property>
<!-- 初始化連接池中的連接數(shù),取值應(yīng)在minPoolSize與maxPoolSize之間垒棋,默認(rèn)為3-->
<property name="initialPoolSize">10</property>
<!--最大空閑時間卒煞,60秒內(nèi)未使用則連接被丟棄。若為0則永不丟棄叼架。默認(rèn)值: 0 -->
<property name="maxIdleTime">30</property>
<!--連接池中保留的最大連接數(shù)畔裕。默認(rèn)值: 15 -->
<property name="maxPoolSize">100</property>
<!-- 連接池中保留的最小連接數(shù),默認(rèn)為:3-->
<property name="minPoolSize">10</property>
<!--c3p0全局的PreparedStatements緩存的大小乖订。如果maxStatements與maxStatementsPerConnection均為0扮饶,則緩存不生效,只要有一個不為0乍构,則語句的緩存就能生效甜无。如果默認(rèn)值: 0-->
<property name="maxStatements">200</property>
<!-- 當(dāng)連接池連接耗盡時,客戶端調(diào)用getConnection()后等待獲取新連接的時間哥遮,超時后將拋出SQLException岂丘,如設(shè)為0則無限期等待。單位毫秒眠饮。默認(rèn): 0 -->
<property name="checkoutTimeout" value="3000"/>
<!--當(dāng)連接池中的連接耗盡的時候c3p0一次同時獲取的連接數(shù)奥帘。默認(rèn)值: 3 -->
<property name="acquireIncrement" value="2"/>
<!--定義在從數(shù)據(jù)庫獲取新連接失敗后重復(fù)嘗試的次數(shù)。默認(rèn)值: 30 仪召;小于等于0表示無限次-->
<property name="acquireRetryAttempts" value="0"/>
<!--重新嘗試的時間間隔翩概,默認(rèn)為:1000毫秒-->
<property name="acquireRetryDelay" value="1000" />
<!--關(guān)閉連接時,是否提交未提交的事務(wù)返咱,默認(rèn)為false钥庇,即關(guān)閉連接,回滾未提交的事務(wù) -->
<property name="autoCommitOnClose">false</property>
<!--c3p0將建一張名為Test的空表咖摹,并使用其自帶的查詢語句進(jìn)行測試评姨。如果定義了這個參數(shù)那么屬性preferredTestQuery將被忽略。你不能在這張Test表上進(jìn)行任何操作,它將只供c3p0測試使用吐句。默認(rèn)值: null -->
<property name="automaticTestTable">Test</property>
<!--如果為false胁后,則獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常,但是數(shù)據(jù)源仍有效保留嗦枢,并在下次調(diào)用getConnection()的時候繼續(xù)嘗試獲取連接攀芯。如果設(shè)為true,那么在嘗試獲取連接失敗后該數(shù)據(jù)源將申明已斷開并永久關(guān)閉文虏。默認(rèn): false-->
<property name="breakAfterAcquireFailure">false</property>
<!--每60秒檢查所有連接池中的空閑連接侣诺。默認(rèn)值: 0,不檢查 -->
<property name="idleConnectionTestPeriod">60</property>
<!--maxStatementsPerConnection定義了連接池內(nèi)單個連接所擁有的最大緩存statements數(shù)氧秘。默認(rèn)值: 0 -->
<property name="maxStatementsPerConnection"></property>
</named-config>
<!-- This is my config for oracle -->
<named-config name="oracle">
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="jdbcUrl">jdbc:oracle:thin:@localhost:1521:orcl</property>
<!--mysql:jdbc:mysql://103.249.130.171:3306/mallshop?useUnicode=true&characterEncoding=UTF8-->
<property name="user">scott</property>
<property name="password">liang</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>
</named-config>
</c3p0-config>
遠(yuǎn)程連接jdbc:mysql://103.249.130.171:3306/mallshop?useUnicode=true&characterEncoding=UTF8
第二步:編寫代碼:
* 手動設(shè)置參數(shù):
* 配置文件設(shè)置參數(shù):
【C3P0改造工具類】
public class JDBCUtils2 {
private static final ComboPooledDataSource DATA_SOURCE =new ComboPooledDataSource();
/**
* 獲得連接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = DATA_SOURCE.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
ResultSetHandler
我們知道在執(zhí)行select語句之后得到的是ResultSet年鸳,然后我們還需要對ResultSet進(jìn)行轉(zhuǎn)換,得到最終我們想要的數(shù)據(jù)丸相。你可以希望把ResultSet的數(shù)據(jù)放到一個List中搔确,也可能想把數(shù)據(jù)放到一個Map中,或是一個Bean中灭忠。
DBUtils提供了一個接口ResultSetHandler膳算,它就是用來ResultSet轉(zhuǎn)換成目標(biāo)類型的工具。你可以自己去實(shí)現(xiàn)這個接口弛作,把ResultSet轉(zhuǎn)換成你想要的類型涕蜂。
DBUtils提供了很多個ResultSetHandler接口的實(shí)現(xiàn),這些實(shí)現(xiàn)已經(jīng)基本夠用了缆蝉,我們通常不用自己去實(shí)現(xiàn)ResultSet接口了宇葱。
MapHandler:單行處理器!把結(jié)果集轉(zhuǎn)換成Map<String,Object>刊头,其中列名為鍵
MapListHandler:多行處理器黍瞧!把結(jié)果集轉(zhuǎn)換成List<Map<String,Object>>;
BeanHandler:單行處理器原杂!把結(jié)果集轉(zhuǎn)換成Bean印颤,該處理器需要Class參數(shù),即Bean的類型穿肄;
BeanListHandler:多行處理器年局!把結(jié)果集轉(zhuǎn)換成List<Bean>;
ColumnListHandler:多行單列處理器咸产!把結(jié)果集轉(zhuǎn)換成List<Object>矢否,使用ColumnListHandler時需要指定某一列的名稱或編號,例如:new ColumListHandler(“name”)表示把name列的數(shù)據(jù)放到List中脑溢。
ScalarHandler:單行單列處理器僵朗!把結(jié)果集轉(zhuǎn)換成Object。一般用于聚集查詢,例如select count(*) from tab_student验庙。
Map處理器
Bean處理器
Column處理器
Scalar處理器
QueryRunner之查詢
QueryRunner的查詢方法是:
public <T> T query(String sql, ResultSetHandler<T> rh, Object… params)
public <T> T query(Connection con, String sql, ResultSetHandler<T> rh, Object… params)
query()方法會通過sql語句和params查詢出ResultSet顶吮,然后通過rh把ResultSet轉(zhuǎn)換成對應(yīng)的類型再返回。
@Test
public void fun1() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
Map<String,Object> map = qr.query(sql, new MapHandler()[[ThinkPad1]](#_msocom_1) , "S_2000");
System.out.println(map);
}
@Test
public void fun2() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Map<String,Object>> list = qr.query(sql, new MapListHandler() );
for(Map<String,Object> map : list) {
System.out.println(map);
}
}
@Test
public void fun3() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
Student stu = qr.query(sql, new BeanHandler<Student>(Student.class);
System.out.println(stu);
}
@Test
public void fun4() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
for(Student stu : list) {
System.out.println(stu);
}
}
@Test
public void fun5() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Object> list = qr.query(sql, new ColumnListHandler("name") );
for(Object s : list) {
System.out.println(s);
}
}
@Test
public void fun6() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select count(*) from tab_student";
Number number = (Number)qr.query(sql, new ScalarHandler());
int cnt = number.intValue();
System.out.println(cnt);
}
Druid 阿里數(shù)據(jù)庫連接池
使用方式
- 導(dǎo)入jar包
- 定義配置文件
- 配置文件是properties形式的
- 可以自定義名成可以放到任意目錄下
- 獲取DataSource
通過工廠類來進(jìn)行獲取 DruidDataSourceFactory - 獲取連接 getConnection()
Spring JDBC
由Spring框架提供的對JDBC簡單封裝粪薛,提供了一個JDBCTemplate對象簡化JDBC的開發(fā)
基本步驟
- 導(dǎo)入jar包
- 創(chuàng)建JdbcTemplate對象悴了,依賴于數(shù)據(jù)源DataSource
- JdbcTemplate template = new JdbcTemplate(ds);
- 調(diào)用JdbcTemplate的方法來完成CRUD操作
- update():執(zhí)行DML語句。增违寿、刪湃交、改語句
- queryForMap():查詢結(jié)果將結(jié)果封裝為map集合
- queryForList():查詢結(jié)果將結(jié)果封裝為list集合
- query():查詢結(jié)果,將結(jié)果封裝為JavaBean對象
- queryForObject:查詢結(jié)果陨界,將結(jié)果封裝為Object對象