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