摘要:數據庫連接池什么是數據庫連接池簡單來說數據庫連接池就是提供連接的。。。
1.數據庫連接池 什么是數據庫連接池
簡單來說:數據庫連接池就是提供連接的。。。
為什么我們要使用數據庫連接池數據庫的連接的建立和關閉是非常消耗資源的
頻繁地打開、關閉連接造成系統性能低下
編寫連接池編寫連接池需實現java.sql.DataSource接口
創建批量的Connection用LinkedList保存【既然是個池,當然用集合保存、、LinkedList底層是鏈表,對增刪性能較好】
實現getConnetion(),讓getConnection()每次調用,都是在LinkedList中取一個Connection返回給用戶
調用Connection.close()方法,Connction返回給LinkedList
private static LinkedListlist = new LinkedList<>(); //獲取連接只需要一次就夠了,所以用static代碼塊 static { //讀取文件配置 InputStream inputStream = Demo1.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); try { properties.load(inputStream); String url = properties.getProperty("url"); String username = properties.getProperty("username"); String driver = properties.getProperty("driver"); String password = properties.getProperty("password"); //加載驅動 Class.forName(driver); //獲取多個連接,保存在LinkedList集合中 for (int i = 0; i < 10; i++) { Connection connection = DriverManager.getConnection(url, username, password); list.add(connection); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } //重寫Connection方法,用戶獲取連接應該從LinkedList中給他 @Override public Connection getConnection() throws SQLException { System.out.println(list.size()); System.out.println(list); //先判斷LinkedList是否存在連接 return list.size() > 0 ? list.removeFirst() : null; }
我們已經完成前三步了,現在問題來了。我們調用Conncetion.close()方法,是把數據庫的物理連接關掉,而不是返回給LinkedList的
解決思路:
寫一個Connection子類,覆蓋close()方法
寫一個Connection包裝類,增強close()方法
用動態代理,返回一個代理對象出去,攔截close()方法的調用,對close()增強
分析第一個思路:
Connection是通過數據庫驅動加載的,保存了數據的信息。寫一個子類Connection,new出對象,子類的Connction無法直接繼承父類的數據信息,也就是說子類的Connection是無法連接數據庫的,更別談覆蓋close()方法了。
分析第二個思路:
寫一個Connection包裝類。
寫一個類,實現與被增強對象的相同接口【Connection接口】
定義一個變量,指向被增強的對象
定義構造方法,接收被增強對象
覆蓋想增強的方法
對于不想增強的方法,直接調用被增強對象的方法
這個思路本身是沒什么毛病的,就是實現接口時,方法太多了!,所以我們也不使用此方法
分析第三個思路代碼實現:
@Override public Connection getConnection() throws SQLException { if (list.size() > 0) { final Connection connection = list.removeFirst(); //看看池的大小 System.out.println(list.size()); //返回一個動態代理對象 return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果不是調用close方法,就按照正常的來調用 if (!method.getName().equals("close")) { return method.invoke(connection, args); } else { //進到這里來,說明調用的是close方法 list.add(connection); //再看看池的大小 System.out.println(list.size()); } return null; } }); } return null; }
我們上面已經能夠簡單編寫一個線程池了。下面我們來使用一下開源數據庫連接池
DBCP使用DBCP數據源的步驟:
導入兩個jar包【Commons-dbcp.jar和Commons-pool.jar】
讀取配置文件
獲取BasicDataSourceFactory對象
創建DataSource對象
private static DataSource dataSource = null; static { try { //讀取配置文件 InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties properties = new Properties(); properties.load(inputStream); //獲取工廠對象 BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory(); dataSource = basicDataSourceFactory.createDataSource(properties); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } //這里釋放資源不是把數據庫的物理連接釋放了,是把連接歸還給連接池【連接池的Connection內部自己做好了】 public static void release(Connection conn, Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (Exception e) { e.printStackTrace(); } rs = null; } if (st != null) { try { st.close(); } catch (Exception e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } }C3P0
C3P0數據源的性能更勝一籌,并且它可以使用XML配置文件配置信息!
步驟:
導入開發包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】
導入XML配置文件【可以在程序中自己一個一個配,C3P0的doc中的Configuration有XML文件的事例】
new出ComboPooledDataSource對象
private static ComboPooledDataSource comboPooledDataSource = null; static { //如果我什么都不指定,就是使用XML默認的配置,這里我指定的是oracle的 comboPooledDataSource = new ComboPooledDataSource("oracle"); } public static Connection getConnection() throws SQLException { return comboPooledDataSource.getConnection(); }Tomcat數據源
Tomcat服務器也給我們提供了連接池,內部其實就是DBCP
步驟:
在META-INF目錄下配置context.xml文件【文件內容可以在tomcat默認頁面的 JNDI Resources下Configure Tomcat"s Resource Factory找到】
導入Mysql或oracle開發包到tomcat的lib目錄下
初始化JNDI->獲取JNDI容器->檢索以XXX為名字在JNDI容器存放的連接池
context.xml文件的配置:
try { //初始化JNDI容器 Context initCtx = new InitialContext(); //獲取到JNDI容器 Context envCtx = (Context) initCtx.lookup("java:comp/env"); //掃描以jdbc/EmployeeDB名字綁定在JNDI容器下的連接池 DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB"); Connection conn = ds.getConnection(); System.out.println(conn); }使用dbutils框架
dbutils它是對JDBC的簡單封裝,極大簡化jdbc編碼的工作量
DbUtils類提供了關閉連接,裝載JDBC驅動,回滾提交事務等方法的工具類【比較少使用,因為我們學了連接池,就應該使用連接池連接數據庫】
QueryRunner類該類簡化了SQL查詢,配合ResultSetHandler使用,可以完成大部分的數據庫操作,重載了許多的查詢,更新,批處理方法。大大減少了代碼量
ResultSetHandler接口該接口規范了對ResultSet的操作,要對結果集進行什么操作,傳入ResultSetHandler接口的實現類即可。
ArrayHandler:把結果集中的第一行數據轉成對象數組。
ArrayListHandler:把結果集中的每一行數據都轉成一個數組,再存放到List中。
BeanHandler:將結果集中的第一行數據封裝到一個對應的JavaBean實例中。
BeanListHandler:將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List里。
ColumnListHandler:將結果集中某一列的數據存放到List中。
KeyedHandler(name):將結果集中的每一行數據都封裝到一個Map里,再把這些map再存到一個map里,其key為指定的key。
MapHandler:將結果集中的第一行數據封裝到一個Map里,key是列名,value就是對應的值。
MapListHandler:將結果集中的每一行數據都封裝到一個Map里,然后再存放到List
ScalarHandler 將ResultSet的一個列到一個對象中。
使用DbUtils框架對數據庫的CRUD
/* * 使用DbUtils框架對數據庫的CRUD * 批處理 * * */ public class Test { @org.junit.Test public void add() throws SQLException { //創建出QueryRunner對象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "INSERT INTO student (id,name) VALUES(?,?)"; //我們發現query()方法有的需要傳入Connection對象,有的不需要傳入 //區別:你傳入Connection對象是需要你來銷毀該Connection,你不傳入,由程序幫你把Connection放回到連接池中 queryRunner.update(sql, new Object[]{"100", "zhongfucheng"}); } @org.junit.Test public void query()throws SQLException { //創建出QueryRunner對象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT * FROM student"; List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class)); System.out.println(list.size()); } @org.junit.Test public void delete() throws SQLException { //創建出QueryRunner對象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "DELETE FROM student WHERE id="100""; queryRunner.update(sql); } @org.junit.Test public void update() throws SQLException { //創建出QueryRunner對象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "UPDATE student SET name=? WHERE id=?"; queryRunner.update(sql, new Object[]{"zhongfuchengaaa", 1}); } @org.junit.Test public void batch() throws SQLException { //創建出QueryRunner對象 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "INSERT INTO student (name,id) VALUES(?,?)"; Object[][] objects = new Object[10][]; for (int i = 0; i < 10; i++) { objects[i] = new Object[]{"aaa", i + 300}; } queryRunner.batch(sql, objects); } }分頁
分頁技術是非常常見的,在搜索引擎下搜索頁面,不可能把全部數據都顯示在一個頁面里邊。所以我們用到了分頁技術。
Oracle實現分頁/* Oracle分頁語法: @lineSize---每頁顯示數據行數 @currentPage----當前所在頁 */ SELECT *FROM ( SELECT 列名,列名,ROWNUM rn FROM 表名 WHERE ROWNUM<=(currentPage*lineSize)) temp WHERE temp.rn>(currentPage-1)*lineSize;
Oracle分頁原理簡單解釋:
/* Oracle分頁: Oracle的分頁依賴于ROWNUM這個偽列,ROWNUM主要作用就是產生行號。 分頁原理: 1:子查詢查出前n行數據,ROWNUM產生前N行的行號 2:使用子查詢產生ROWNUM的行號,通過外部的篩選出想要的數據 例子: 我現在規定每頁顯示5行數據【lineSize=5】,我要查詢第2頁的數據【currentPage=2】 注:【對照著語法來看】 實現: 1:子查詢查出前10條數據【ROWNUM<=10】 2:外部篩選出后面5條數據【ROWNUM>5】 3:這樣我們就取到了后面5條的數據 */Mysql實現分頁
/* Mysql分頁語法: @start---偏移量,不設置就是從0開始【也就是(currentPage-1)*lineSize】 @length---長度,取多少行數據 */ SELECT * FROM 表名 LIMIT [START], length; /* 例子: 我現在規定每頁顯示5行數據,我要查詢第2頁的數據 分析: 1:第2頁的數據其實就是從第6條數據開始,取5條 實現: 1:start為5【偏移量從0開始】 2:length為5 */
總結:
Mysql從(currentPage-1)*lineSize開始取數據,取lineSize條數據
Oracle先獲取currentPagelineSize條數據,從(currentPage-1)lineSize開始取數據
使用JDBC連接數據庫實現分頁下面是常見的分頁圖片
配合圖片,看下我們的需求是什么:
算出有多少頁的數據,顯示在頁面上
根據頁碼,從數據庫顯示相對應的數據。
分析:
算出有多少頁數據這是非常簡單的【在數據庫中查詢有多少條記錄,你每頁顯示多少條記錄,就可以算出有多少頁數據了】
使用Mysql或Oracle的分頁語法即可
通過上面分析,我們會發現需要用到4個變量
currentPage--當前頁【由用戶決定的】
totalRecord--總數據數【查詢表可知】
lineSize--每頁顯示數據的數量【由我們開發人員決定】
pageCount--頁數【totalRecord和lineSize決定】
//每頁顯示3條數據 int lineSize = 3; //總記錄數 int totalRecord = getTotalRecord(); //假設用戶指定的是第2頁 int currentPage = 2; //一共有多少頁 int pageCount = getPageCount(totalRecord, lineSize); //使用什么數據庫進行分頁,記得要在JdbcUtils中改配置 Listlist = getPageData2(currentPage, lineSize); for (Person person : list) { System.out.println(person); } } //使用JDBC連接Mysql數據庫實現分頁 public static List getPageData(int currentPage, int lineSize) throws SQLException { //從哪個位置開始取數據 int start = (currentPage - 1) * lineSize; QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT name,address FROM person LIMIT ?,?"; List persons = (List ) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize}); return persons; } //使用JDBC連接Oracle數據庫實現分頁 public static List getPageData2(int currentPage, int lineSize) throws SQLException { //從哪個位置開始取數據 int start = (currentPage - 1) * lineSize; //讀取前N條數據 int end = currentPage * lineSize; QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT " + " name, " + " address " + "FROM ( " + " SELECT " + " name, " + " address , " + " ROWNUM rn " + " FROM person " + " WHERE ROWNUM <= ? " + ")temp WHERE temp.rn>?"; List persons = (List ) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start}); return persons; } public static int getPageCount(int totalRecord, int lineSize) { //簡單算法 //return (totalRecord - 1) / lineSize + 1; //此算法比較好理解,把數據代代進去就知道了。 return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1; } public static int getTotalRecord() throws SQLException { //使用DbUtils框架查詢數據庫表中有多少條數據 QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "SELECT COUNT(*) FROM person"; Object o = queryRunner.query(sql, new ScalarHandler()); String ss = o.toString(); int s = Integer.parseInt(ss); return s; }
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68503.html
摘要:前言由于寫的文章已經是有點多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導航。 前言 由于寫的文章已經是有點多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導航。 由于更新比較頻繁,因此隔一段時間才會更新目錄導航哦~想要獲取最新原創的技術文章歡迎關注我的公眾號:Java3y Java3y文章目錄導航 Java基礎 泛型就這么簡單 注解就這么簡單 Druid數據庫連接池...
摘要:不用自己來創建,而是通過池來獲取對象使用完后,調用的方法也不會真的關閉,而是把歸還給池連接池技術可以完成對象的再次利用接口為數據庫連接池提供了公共的接口各個廠商需要讓自己的連接池實現這個接口。 1.DButils工具類的介紹個三個核心類 A: 概述 DBUtils是java編程中的數據庫操作實用工具,小巧簡單實用。 DBUtils封裝了對JDBC的操作,簡化了JDBC操作,可以少...
摘要:不用自己來創建,而是通過池來獲取對象使用完后,調用的方法也不會真的關閉,而是把歸還給池連接池技術可以完成對象的再次利用接口為數據庫連接池提供了公共的接口各個廠商需要讓自己的連接池實現這個接口。 01DButils工具類的介紹個三個核心類 * A: DButils工具類的介紹個三個核心類 * a: 概述 * DBUtils是java編程中的數據庫操作實用工具,小巧...
摘要:一致性一個事務中,事務前后數據的完整性必須保持一致。持久性持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。 一、事務概述1.什么是事務一件事情有n個組成單元 要不這n個組成單元同時成功 要不n個單元就同時失敗就是將n個組成單元放到一個事務中2.mysql的事務默認的事務:一條sql語句就是一個事務 默認就開啟事務并提交事...
閱讀 3627·2023-04-26 02:32
閱讀 3904·2021-11-23 10:05
閱讀 2291·2021-10-08 10:04
閱讀 2711·2021-09-22 16:06
閱讀 3612·2021-09-22 15:27
閱讀 764·2019-08-30 15:54
閱讀 1698·2019-08-30 13:50
閱讀 2704·2019-08-29 13:56