連接池
- 普通的數(shù)據(jù)庫連接在打開和關(guān)閉連接時(shí)比較損耗性能, 而且是和數(shù)據(jù)庫的直接交互, 連接池就是一個(gè)維護(hù)數(shù)據(jù)庫連接的管理員, 手中經(jīng)常有數(shù)據(jù)庫的連接, 當(dāng)需要連接的時(shí)候, 直接從管理員手中拿, 用完了之后還給管理員, 而且不需要直接和數(shù)據(jù)庫交互
- 常見的連接池: Tomcat-dbcp, dbcp, c3p0, druid
- 數(shù)據(jù)源(javax.sql.Datasource)包含了數(shù)據(jù)池, 數(shù)據(jù)源可以管理數(shù)據(jù)池
- 以前需要Class.forName()加載驅(qū)動, 通過DriverManager.getConnection()直接從數(shù)據(jù)庫獲取連接; 而用連接池的核心就是: 將連接的指向改了, 現(xiàn)在指向的是數(shù)據(jù)源而不是數(shù)據(jù)庫
- 數(shù)據(jù)庫訪問的核心 --> pstmt/stmt -> Connection -> 1. 直連數(shù)據(jù)庫 2. 數(shù)據(jù)源 (ds.getConnection())
Tomcat-dbcp: 不常用, 了解
- 類似于 jndi, 在Context.xml中配置下面的MySQL Resource
- Content.xml中MySQL的配置
<Resource
<!-- name指定Resource的JNID名字 -->
name="jdbc/mysql"
<!-- 指定Resource的管理者, 有兩個(gè)可選: Container和Applocation;Container:由容器來創(chuàng)建Resource; Application: 由Web應(yīng)用來創(chuàng)建和管理Resource -->
auth="Container"
<!-- type: 指定Resource的類型 -->
type="javax.sql.DataSource"
<!-- 指定連接池中, 處于活動狀態(tài)的數(shù)據(jù)庫連接的最大數(shù)量, 如果值為0, 標(biāo)識不受限制 -->
maxActive="100"
<!-- 指定連接池中, 處于空閑狀態(tài)的數(shù)據(jù)庫連接的最大數(shù)量, 如果值為0, 標(biāo)識不受限制 -->
maxIdle="30"
<!-- 指定連接池中,連接處于空閑狀態(tài)的最長時(shí)間(單位為毫秒), 如果超出此最長時(shí)間將會拋出異常; 如果值為-1, 表示允許無限制等待 -->
maxWait="10000"
<!-- 指定數(shù)據(jù)庫訪問名 -->
username="root"
<!-- 指定數(shù)據(jù)庫訪問密碼 -->
password="password"
<!-- 指定連接數(shù)據(jù)庫的驅(qū)動程序的類名 -->
driverClassName="com.mysql.cj.jdbc.Driver"
<!-- 指定連接數(shù)據(jù)庫的RUL -->
url="jdbc:mysql://127.0.0.1:3306/blog?useSSL=false&serverTimezone=UTC"
/>
- 在項(xiàng)目里面的web.xml中指定context.xml里面配置的數(shù)據(jù)源; 就是指定name,type, auth三個(gè)屬性的值
- 在DBUtils.java 中將之前通過DriverManager.getConnection(URL, NAME, PWD)獲取連接改為通過 Context ctx = new InitialContext();先獲取Context.xml; 再通過 DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mysql");獲取DataSource對象, 再通過DataSource對象獲取連接 Connection conn = ds.getConnection();
dbcp 連接池
- 需要引入jar包 commons-dbcp-1.4.jar, commons-pool.jar
- 獲取ds的核心類(二選一): BasicDataSource , BasicDataSourceFactory
BasicDataSource類的方式(硬編碼方式)
- 1.創(chuàng)建 BasicDataSource dbcp = new BasicDataSource();
- 2.通過下面的常用方法設(shè)置各種屬性即可拿到 dbcp
- 3.通過 dbcp.getConnrction()獲取數(shù)據(jù)庫連接即可
常用方法
- void serDriverClassName(String driverClassName) : 設(shè)置連接數(shù)據(jù)庫的驅(qū)動名
- void setUrl(String url) : 設(shè)置連接數(shù)據(jù)庫的URL
- void setUsername(Stirng Username) : 設(shè)置連接數(shù)據(jù)庫的用戶名
- void setPassword(Stirng Password) : 設(shè)置連接數(shù)據(jù)庫的木馬
- void setInitialiSize(int initialSize) : 設(shè)置初始化時(shí), 連接數(shù)據(jù)池中的連接數(shù)量
- void setMaxActive(int maxActive) : 設(shè)置連接池中, 處于活動狀態(tài)的數(shù)據(jù)庫連接最大數(shù)量
- void setMinIdle(int minIdle) : 設(shè)置連接池中, 處于空閑狀態(tài)的數(shù)據(jù)庫連接的最小數(shù)量
- Collection getConnection() : 從連接池中獲取一個(gè)數(shù)據(jù)庫連接
BasicDataSourceFactory 配置方式(dbcpconfig.properties配置文件; 全部是k=v)
- 編寫dbcpconfig.properties配置文件(格式k=v中間不要由空格)
- 通過創(chuàng)建 Properties 類的實(shí)例, 調(diào)用該實(shí)例的load()方法將配置文件加載進(jìn)來; 需要注意的是load()方法的參數(shù)是流的形式
- 創(chuàng)建 Properties 實(shí)例: Properties props = new Properties();
- 將字符串編程輸入流: InputStream input = new DBCPDemo().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
- 調(diào)用load()方法: props.load(input);
- 通過 DataSource dbcp = BasicDataSourceFactory.createDataSource(prop); 方法即可創(chuàng)建dbcp
- dbcpconfig.properties 示例
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/blog?useSSL=false&serverTimezone=UTC
username=root
password=password
initialSize=10
C3P0 連接池
- 核心類 ComboPooledDataSource
- C3P0是將DBCP的兩種方式(硬編碼和配置文件)合二為一; 通過ComboPooledDataSource的構(gòu)造參數(shù)來區(qū)分, 無參數(shù)的就是硬編碼; 而有參數(shù)的就是通過配置文件的方式
- 無參數(shù)構(gòu)造方法步驟:
- 創(chuàng)建 ComboPooledDataSource c3p0 = new ComboPooledDataSource(); 對象
- 通過 c3p0的set方法設(shè)置Driver,Url,User,Passwd等
- return c3p0; 即可
- 連接時(shí) 通過 c3p0.getConnection()即可
- 有參數(shù)構(gòu)造方法, 配置文件(c3p0-config.xml)
- 在IDE的src目錄中編寫c3p0-config.xml文件, 注意的點(diǎn): Url中出現(xiàn)分號(;)時(shí), 應(yīng)該使用(amp;)這種形式
- 使用時(shí)直接 return new ComboPooledDataSource("jefxff"); 即可返回 c3p0
- c3p0-config.xml 示例
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
<!-- 默認(rèn) -->
<default-config>
<!-- 如果要深究某個(gè)xml中可以設(shè)置那些屬性, 就找相關(guān)類的屬性, 或者setter()方法 -->
<property name="user">root</property>
<property name="password">password</property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/blog?useSSL=false&serverTimezone=UTC</property>
<property name="checkoutTimeout">30000</property>
</default-config>
<!-- 自定義 -->
<named-config name="jefxff">
<property name="user">root</property>
<property name="password">password</property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/blog?useSSL=false&serverTimezone=UTC</property>
<property name="checkoutTimeout">20000</property>
</named-config>
</c3p0-config>
C3P0的核心類ComboPooledDataSource常用方法
- public ComboPooledDataSource() : 無參構(gòu)造方法(硬編碼發(fā)方式)
- public ComboPooledDataSource(String configName) : 加載配置文件的構(gòu)造方法
- void setDriverClass(String driverClass) : 設(shè)置數(shù)據(jù)庫連接的驅(qū)動
- void setJdbcUrl(String jdbcUrl) : 設(shè)置連接數(shù)據(jù)庫的URL
- void setUser(Stirng User) : 設(shè)置數(shù)據(jù)庫連接的用戶名
- void setPassword(String password) : 設(shè)置數(shù)據(jù)庫連接的密碼
- void setMaxPoolSize(int maxPoolSize) : 設(shè)置連接池的最大連接數(shù)目
- void setMinPoolSize(int minPoolSize) : 設(shè)置連接池的最小連接數(shù)目
- void setInitiaPoolSize(int initiaPoolSize) : 設(shè)置初始化時(shí), 連接池中的連接數(shù)量
- Connection getConnection() : 從連接池中獲取一個(gè)數(shù)據(jù)庫連接, 該方法由ComboPooledDataSource 的父類 AbstractPoolBackedDataSource提供
連接池總結(jié)
- 硬編碼: 獲取某個(gè)連接池?cái)?shù)據(jù)源的對象 ds = new XxxDataSource(); ds.setXxx(); return ds;
- 配置文件: 編寫配置文件, ds = new XxxDataSource(); 加載配置文件; return ds;
連接池代碼示例
package xyz.xmcs.DataSourceUtils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author jefxff
* @date 2020/3/11 - 16:17
* DataSource 工具類, 可以通過該類獲取 Tomcat-dbcp , DBCP, C3P0 類型連接池的 DataSource,
* 通過該 DataSource 即可創(chuàng)建數(shù)據(jù)庫的連接 Connection
*/
public class DataSourceUtils {
/**
* 此方法第一步: 需要配置tomcat目錄下的context.xml, 添加 <Resource 標(biāo)簽
* 第二步: 需要在項(xiàng)目中的web.xml中配置 resource-ref 標(biāo)簽
* @return
* @throws NamingException
*/
public static DataSource getTomcatDbcpBySet() throws NamingException {
// 1. 相當(dāng)于獲取Context.xml
Context ctx = new InitialContext();
// 2. 通過 lookup()方法去web.xml中找配置的<res-res-name字段的值,
// 但是要注意 字段名字前面要添加 "java:comp/env/"
return (DataSource) ctx.lookup("java:comp/env/jdbc/mysql");
}
/**
* 1. 此方法需要兩個(gè)jar包: commons-dbcp-1.4.jar, commons-pool.jar
* 2. 通過創(chuàng)建 BasicDataSource 的實(shí)例, 然后通過該實(shí)例調(diào)用setter方法通過硬編碼的方式,
* 將Driver, url, name, pwd 等值通過setter方法指定
* @return
*/
public static DataSource getDbcpBySet(){
BasicDataSource dbcp = new BasicDataSource();
dbcp.setDriverClassName("com.mysql.cj.jdbc.Driver");
dbcp.setUrl("jdbc:mysql://localhost:3306/blog?useSSL=false&serverTimezone=UTC");
dbcp.setUsername("root");
dbcp.setPassword("password");
dbcp.setInitialSize(20);
dbcp.setMaxActive(10);
return dbcp;
}
/**
* 1. 此方法是通過編寫 dbcpconfig.properties 配置文件, 將Driver, url, name, pwd 參數(shù)通過K=V的方式
* 寫在配置文件中
* 2. 通過new DbcpUtils().getClass().getClassLoader().getResourceAsStream("配置文件"); 將配置文件加載為輸入流
* 3. 通過 Properties實(shí)例.load(input)加載配置文件
* 4. 再通過 BasicDataSourceFactory.createDataSource(properties); 來獲取DBCP對象
* @return
*/
public static DataSource getDbcpByProperties() throws Exception {
Properties prop = new Properties();
InputStream input= new DataSourceUtils().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
prop.load(input);
return BasicDataSourceFactory.createDataSource(prop);
}
/**
* 1. 此方法需要兩個(gè)jar包, c3p0-0.9.5.2.jar, mchange-commons-java-0.2.11.jar
* 2. 此方法是通過獲取 ComboPooledDataSource 的無參對象, 通過該對象的setter方法以硬編碼的方式指定Driver, url, name, pwd 等
* @return
*/
public static DataSource getC3p0BySet() throws PropertyVetoException {
ComboPooledDataSource c3p0 = new ComboPooledDataSource();
c3p0.setDriverClass("com.mysql.cj.jdbc.Driver");
c3p0.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/blog?useSSL=false&serverTimezone=UTC");
c3p0.setUser("root");
c3p0.setPassword("password");
return c3p0;
}
/**
* 1. 此方法需要編寫 c3p0-config.xml文件, 指定default的配置, 以及自己的配置
* 2. 在使用是通過創(chuàng)建有參數(shù)的 ComboPooledDatasource 對象, 參數(shù)中指定c3p0-config.xml配置的name屬性
* @return
*/
public static DataSource getC3p0ByXml(){
return new ComboPooledDataSource("jefxff");
}
}
Apache Commons DBUtils 工具類使用
Apache DButils
- Apache DButils 是對Java JDBC的簡單封裝, 主要好處就是方便返回查詢的結(jié)果
- 下載 commons-dbutils-1.7-bin.zip 文件并解壓, 主要使用 commons-dbutils-1.7.jar 文件, 包括幾個(gè)重點(diǎn)類: DbUtils, QueryRunner, ResultSetHandler
- DbUtils: 打開關(guān)閉連接, 提交事務(wù)
-
關(guān)鍵類 QueryRunner : 增刪改查方法的基礎(chǔ), 所有的增刪改查都必須創(chuàng)建該類的實(shí)例, 通過該類的實(shí)例來調(diào)用query或者update方法來執(zhí)行增刪改查; 其中該實(shí)例參數(shù)可以是一個(gè)DataSource實(shí)例, 又DS實(shí)例后是自動提交事務(wù)
- updte() : 第一個(gè)參數(shù)是SQL語句, 第二個(gè)參數(shù)是Object[] 類型的數(shù)組的參數(shù); 有很多重載的方法
- query() : 有很多重載的方法, 第一個(gè)參數(shù)是SQL語句, 第二個(gè)參數(shù)是ResultSetHandler接口的實(shí)現(xiàn)類, 不同的實(shí)現(xiàn)類代表返回不同的結(jié)果形式, 第三個(gè)參數(shù)是Object[] 類型的數(shù)組的參數(shù);
- ResultSetHandler接口 : 有很多的實(shí)現(xiàn)類, 一個(gè)實(shí)現(xiàn)類對應(yīng)于一種不同 的查詢結(jié)果類型
通過 ResultSetHandler 接口的實(shí)現(xiàn)類來實(shí)現(xiàn)查詢
- ArrayHandler實(shí)現(xiàn)類: 返回結(jié)果集中的第一行數(shù)據(jù), 并用Object[]接收
- ArrayListhandler實(shí)現(xiàn)類: 返回結(jié)果集的多行數(shù)據(jù), 接收類型是 List<Obkect[]>
- BeanHandler實(shí)現(xiàn)類: 返回結(jié)果集的第一行數(shù)據(jù), 并將結(jié)果集放在Bean里,即對象Student里面. (反射會通過無參構(gòu)造來創(chuàng)建對象)
- BeanListHandler實(shí)現(xiàn)類: 返回結(jié)果集的多行數(shù)據(jù),接收類型為 List<Student>
- BeanMapHandler實(shí)現(xiàn)列: 返回結(jié)果集的多行數(shù)據(jù), 接收類型為 Map<>(如果是Oracle數(shù)據(jù)庫, Java中對應(yīng)的Oracle默認(rèn)的數(shù)值類型 BigDecimal(數(shù)字通過 new BigDecimal(int) 方法轉(zhuǎn)換為BigDecimal))
- MapHandler實(shí)現(xiàn)類: 返回一行以Map形式包裝的數(shù)據(jù)
- MapListHandler實(shí)現(xiàn)類: 返回多行以List<\map>形式包裝的數(shù)據(jù)
- KeyedHandler: 返回多行數(shù)據(jù), 并且給每個(gè)添加數(shù)據(jù)添加字段
- ColumnListHandler : 把結(jié)果集的 某一列保存在List中
- ScalarHandler: 查詢單值結(jié)果型
Apache DButils 代碼示例
package xyz.xmcs.Dao.Impl;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import xyz.xmcs.Dao.IArticlesDal;
import xyz.xmcs.Entity.Articles;
import xyz.xmcs.Utils.DataSourceUtils;
import java.sql.SQLException;
import java.util.List;
public class ArticlesDaoImpl implements IArticlesDal {
// 創(chuàng)建QueryRunner對象, 參數(shù)傳入DataSource實(shí)例, 即以配置文件的方式創(chuàng)建的DataSource
static QueryRunner runner = new QueryRunner(DataSourceUtils.getC3p0ByXml());
// update的操作都是一樣的, 所以提取出來單獨(dú)作為一個(gè)方法處理
private static boolean checkUpdate(String sql, Object[] params) {
try {
// 增刪改的方法通過 QueryRunner實(shí)例調(diào)用update()方法來執(zhí)行
// 接受兩個(gè)參數(shù), 第一個(gè)參數(shù)是sql語句, 第二個(gè)參數(shù)是sql語句需要的參數(shù)
int count = runner.update(sql, params);
if(count > 0){
return true;
} else {
return false;
}
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean addArticles(Articles articles){
String sql = "insert into articles values(?,?,?,?,?,?,?)";
Object[] params = {articles.getId(),articles.getAuthor(),articles.getTitle(),articles.getContent(),
articles.getCreatedate(),articles.getUser_id(),articles.getCategory_id()};
return checkUpdate(sql, params);
}
@Override
public boolean isExist(String title) {
return queryArticlesByName(title)==null?false:true;
}
@Override
public Articles queryArticlesById(int id) {
String sql = "select * from articles where id = ?";
Object[] params = {id};
try {
// 查詢的方法通過 QueryRunner實(shí)例調(diào)用query()方法來執(zhí)行
// 第一個(gè)參數(shù)是sql語句, 第二個(gè)參數(shù)是ResultSetHandler實(shí)現(xiàn)類, 代表不同的返回值類型, 第三個(gè)參數(shù)是sql語句的參數(shù)
Articles article = runner.query(sql, new BeanHandler<>(Articles.class), params);
return article;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
@Override
public List<Articles> queryAllArticles() {
String sql = "select * from articles";
try {
// 查詢的方法通過 QueryRunner實(shí)例調(diào)用query()方法來執(zhí)行
// 第一個(gè)參數(shù)是sql語句, 第二個(gè)參數(shù)是ResultSetHandler實(shí)現(xiàn)類, 代表不同的返回值類型, 第三個(gè)參數(shù)是sql語句的參數(shù)
List<Articles> list = runner.query(sql, new BeanListHandler<>(Articles.class));
return list;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
@Override
public int getTotalArticlesCount() {
String sql = "select count(1) from articles";
try {
// 查詢的方法通過 QueryRunner實(shí)例調(diào)用query()方法來執(zhí)行
// 第一個(gè)參數(shù)是sql語句, 第二個(gè)參數(shù)是ResultSetHandler實(shí)現(xiàn)類, 代表不同的返回值類型
// ScalarHandler 實(shí)現(xiàn)類代表的是返回單值性的查詢, 返回的類型是 long 類型的值
long query = runner.query(sql, new ScalarHandler<>());
return (int)query;
} catch (SQLException e) {
e.printStackTrace();
return -1;
}
}
}