【JDBC詳解】連線池與分頁
阿新 • • 發佈:2019-01-07
一、連線池
1.由來:
一個使用者至少要用到一個連線。當用戶過多時,需要建立巨大數量的連線物件,這會使資料庫承受極大的壓力,為了解決這種現象,出現了資料庫連線池。
2.定義:
在使用者和資料庫之間建立一個”池”,這個池中有若干個連線物件,當用戶想要連線資料庫,就要先從連線池中獲取連線物件,然後操作資料庫。即資料庫連線池就是提供連線的
3.自定義連線池
例:
public class MyPool {
private int init_count = 3; // 初始化連線數目
private int max_count = 6; // 最大連線數
private int current_count = 0; // 記錄當前使用連線數
// 連線池 (存放所有的初始化連線)
private LinkedList<Connection> pool = new LinkedList<Connection>();
//1. 建構函式中,初始化連線放入連線池
public MyPool() {
// 初始化連線
for (int i=0; i<init_count; i++){
// 記錄當前連線數目
current_count++;
// 建立原始的連線物件
Connection con = createConnection();
// 把連線加入連線池
pool.addLast(con);
}
}
//2. 建立一個新的連線的方法
private Connection createConnection(){
try {
Class.forName("com.mysql.jdbc.Driver");
// 原始的目標物件
final Connection con = DriverManager.getConnection("jdbc:mysql:///test" , "root", "965847");
// 對con建立其代理物件
Connection proxy = (Connection) Proxy.newProxyInstance(
con.getClass().getClassLoader(), // 類載入器
//con.getClass().getInterfaces(), // 當目標物件是一個具體的類的時候
new Class[]{Connection.class}, // 目標物件實現的介面
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法返回值
Object result = null;
// 當前執行的方法的方法名
String methodName = method.getName();
// 判斷當執行了close方法的時候,把連線放入連線池
if ("close".equals(methodName)) {
System.out.println("begin:當前執行close方法開始!");
// 連線放入連線池
pool.addLast(con);
System.out.println("end: 當前連線已經放入連線池了!");
} else {
// 呼叫目標物件方法
result = method.invoke(con, args);
}
return result;
}
}
);
return proxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//3. 獲取連線
public Connection getConnection(){
// 3.1 判斷連線池中是否有連線, 如果有連線,就直接從連線池取出
if (pool.size() > 0){
return pool.removeFirst();
}
// 3.2 連線池中沒有連線: 判斷,如果沒有達到最大連線數,建立;
if (current_count < max_count) {
// 記錄當前使用的連線數
current_count++;
// 建立連線
return createConnection();
}
// 3.3 如果當前已經達到最大連線數,丟擲異常
throw new RuntimeException("當前連線已經達到最大連線數目 !");
}
//4. 釋放連線
public void realeaseConnection(Connection con) {
// 4.1 判斷: 池的數目如果小於初始化連線,就放入池中
if (pool.size() < init_count){
pool.addLast(con);
} else {
try {
// 4.2 關閉
current_count--;
con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws SQLException {
MyPool pool = new MyPool();
System.out.println("當前連線: " + pool.current_count);
// 使用連線
pool.getConnection();
pool.getConnection();
Connection con3 = pool.getConnection();
Connection con2 = pool.getConnection();
Connection con1 = pool.getConnection();
// 釋放連線, 連線放回連線池
con1.close();
// 再獲取
pool.getConnection();
System.out.println("連線池:" + pool.pool.size());
System.out.println("當前連線: " + pool.current_count);
}
}
4.DBCP連線池
(1)引入jar包:
• Commons-dbcp.jar:連線池的實現
• Commons-pool.jar:連線池實現的依賴庫
(2)核心類:BasicDataSource
例:
public class Dbcp {
// 1. 硬編碼方式實現連線池
@Test
public void testDbcp() throws Exception {
// DBCP連線池核心類
BasicDataSource dataSouce = new BasicDataSource();
// 連線池引數配置:初始化連線數、最大連線數 / 連線字串、驅動、使用者、密碼
dataSouce.setUrl("jdbc:mysql:///test");//資料庫連線字串
dataSouce.setDriverClassName("com.mysql.jdbc.Driver");//資料庫驅動
dataSouce.setUsername("root"); //資料庫連線使用者
dataSouce.setPassword("965847"); //資料庫連線密碼
dataSouce.setInitialSize(3); // 初始化連線
dataSouce.setMaxActive(6); // 最大連線
dataSouce.setMaxIdle(3000); // 最大空閒時間
// 獲取連線
Connection con = dataSouce.getConnection();
con.prepareStatement("delete from admin where id=5").executeUpdate();
// 關閉
con.close();
}
@Test
// 2. 【推薦】配置方式實現連線池
public void testProp() throws Exception {
// 載入prop配置檔案
Properties prop = new Properties();
// 獲取檔案流
InputStream inStream = Dbcp.class.getResourceAsStream("db.properties");
// 載入屬性配置檔案
prop.load(inStream);
// 根據prop配置,直接建立資料來源物件
DataSource dataSouce = BasicDataSourceFactory.createDataSource(prop);
// 獲取連線
Connection con = dataSouce.getConnection();
con.prepareStatement("delete from admin where id=4").executeUpdate();
// 關閉
con.close();
}
}
5.C3P0連線池
(1)優點:最常用的連線池技術!Spring框架,預設支援C3P0連線池技術!
(2)核心類:CombopooledDataSource
例:
public class C3p0 {
@Test
//1. 硬編碼方式,使用C3P0連線池管理連線
public void testCode() throws Exception {
// 建立連線池核心工具類
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 設定連線引數:url、驅動、使用者密碼、初始連線數、最大連線數
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc_demo");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setInitialPoolSize(3);
dataSource.setMaxPoolSize(6);
dataSource.setMaxIdleTime(1000);
// ---> 從連線池物件中,獲取連線物件
Connection con = dataSource.getConnection();
// 執行更新
con.prepareStatement("delete from admin where id=7").executeUpdate();
// 關閉
con.close();
}
@Test
//2. XML配置方式,使用C3P0連線池管理連線
public void testXML() throws Exception {
// 建立c3p0連線池核心工具類
// 自動載入src下c3p0的配置檔案【c3p0-config.xml】
ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用預設的配置
// 獲取連線
Connection con = dataSource.getConnection();
// 執行更新
con.prepareStatement("delete from admin where id=4").executeUpdate();
// 關閉
con.close();
}
}
二、分頁
1.優點:
利於頁面佈局,且顯示的效率高!
2.要點:
關鍵點:
(1) 分頁SQL語句;
(2) 後臺處理: dao/service/servlet/JSP
3.一個初步的例子
例:
(1)建立分頁的屬性及方法
public class PageBean<T> {
private int currentPage = 1; // 當前頁, 預設顯示第一頁
private int pageCount = 4; // 每頁顯示的行數(查詢返回的行數), 預設每頁顯示4行
private int totalCount; // 總記錄數
private int totalPage; // 總頁數 = 總記錄數 / 每頁顯示的行數 (+ 1)
private List<T> pageData; // 分頁查詢到的資料
// 返回總頁數
public int getTotalPage() {
if (totalCount % pageCount == 0) {
totalPage = totalCount / pageCount;
} else {
totalPage = totalCount / pageCount + 1;
}
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public List<T> getPageData() {
return pageData;
}
public void setPageData(List<T> pageData) {
this.pageData = pageData;
}
}
(2)具體的分頁操作
public class EmployeeDao {
public void getAll(PageBean<Employee> pb) {
//2. 查詢總記錄數; 設定到pb物件中
int totalCount = this.getTotalCount();
pb.setTotalCount(totalCount);
//若當前頁 <= 0; 當前頁設定當前頁為1;
//若當前頁 > 最大頁數; 當前頁設定為最大頁數;
if (pb.getCurrentPage() <=0) {
pb.setCurrentPage(1); // 把當前頁設定為1
} else if (pb.getCurrentPage() > pb.getTotalPage()){
pb.setCurrentPage(pb.getTotalPage()); // 把當前頁設定為最大頁數
}
//1. 獲取當前頁: 計算查詢的起始行、返回的行數
int currentPage = pb.getCurrentPage();
int index = (currentPage -1 ) * pb.getPageCount(); // 查詢的起始行
int count = pb.getPageCount(); // 查詢返回的行數
//3. 分頁查詢資料; 把查詢到的資料設定到pb物件中
String sql = "select * from employee limit ?,?";
try {
// 得到Queryrunner物件
QueryRunner qr = JdbcUtils.getQueryRuner();
// 根據當前頁,查詢當前頁資料(一頁資料)
List<Employee> pageData = qr.query(sql, new BeanListHandler<Employee>(Employee.class), index, count);
// 設定到pb物件中
pb.setPageData(pageData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int getTotalCount() {
String sql = "select count(*) from employee";
try {
// 建立QueryRunner物件
QueryRunner qr = JdbcUtils.getQueryRuner();
// 執行查詢, 返回結果的第一行的第一列
Long count = qr.query(sql, new ScalarHandler<Long>());
return count.intValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
本人才疏學淺,若有錯誤,請指出
謝謝!