前言
只有光头才能变强。
文本已收录至我的GitHub仓库,欢迎Star:/ZhongFuCheng3y/3y
1.数据库连接池
什么是数据库连接池
简单来说:数据库连接池就是提供连接的。。。
为什么我们要使用数据库连接池
编写连接池
编写连接池需实现java.sql.DataSource接口
创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
调用Connection.close()方法,Connction返回给LinkedList
//获取连接只需要一次就够了,所以用static代码块 static{ //读取文件配置 InputStreaminputStream=Demo1.class.getClassLoader().getResourceAsStream("db.properties"); Propertiesproperties=newProperties(); try{ properties.load(inputStream); Stringurl=properties.getProperty("url"); Stringusername=properties.getProperty("username"); Stringdriver=properties.getProperty("driver"); Stringpassword=properties.getProperty("password"); //加载驱动 Class.forName(driver); //获取多个连接,保存在LinkedList集合中 for(inti=0;i<10;i++){ Connectionconnection=DriverManager.getConnection(url,username,password); list.add(connection); } }catch(IOExceptione){ e.printStackTrace(); }catch(ClassNotFoundExceptione){ e.printStackTrace(); }catch(SQLExceptione){ e.printStackTrace(); } } //重写Connection方法,用户获取连接应该从LinkedList中给他 @Override publicConnectiongetConnection()throwsSQLException{ System.out.println(list.size()); System.out.println(list); //先判断LinkedList是否存在连接 returnlist.size()>0?list.removeFirst():null; }privatestaticLinkedList<Connection>list=newLinkedList<>();//获取连接只需要一次就够了,所以用static代码块static{//读取文件配置InputStreaminputStream=Demo1.class.getClassLoader().getResourceAsStream("db.properties");Propertiesproperties=newProperties();try{properties.load(inputStream);Stringurl=properties.getProperty("url");Stringusername=properties.getProperty("username");Stringdriver=properties.getProperty("driver");Stringpassword=properties.getProperty("password");//加载驱动Class.forName(driver);//获取多个连接,保存在LinkedList集合中for(inti=0;i<10;i++){Connectionconnection=DriverManager.getConnection(url,username,password);list.add(connection);}}catch(IOExceptione){e.printStackTrace();}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(SQLExceptione){e.printStackTrace();}}//重写Connection方法,用户获取连接应该从LinkedList中给他@OverridepublicConnectiongetConnection()throwsSQLException{System.out.println(list.size());System.out.println(list);//先判断LinkedList是否存在连接returnlist.size()>0?list.removeFirst():null;}staticLinkedList<Connection>list=newLinkedList<>();
我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的
解决思路:
写一个Connection子类,覆盖close()方法
写一个Connection包装类,增强close()方法
用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强
分析第一个思路:
分析第二个思路:
写一个类,实现与被增强对象的相同接口【Connection接口】
定义一个变量,指向被增强的对象
定义构造方法,接收被增强对象
覆盖想增强的方法
对于不想增强的方法,直接调用被增强对象的方法
这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法
分析第三个思路代码实现:
publicConnectiongetConnection()throwsSQLException{ if(list.size()>0){ finalConnectionconnection=list.removeFirst(); //看看池的大小 System.out.println(list.size()); //返回一个动态代理对象 return(Connection)Proxy.newProxyInstance(Demo1.class.getClassLoader(),connection.getClass().getInterfaces(),newInvocationHandler(){ @Override publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ //如果不是调用close方法,就按照正常的来调用 if(!method.getName().equals("close")){ method.invoke(connection,args); }else{ //进到这里来,说明调用的是close方法 list.add(connection); //再看看池的大小 System.out.println(list.size()); } returnnull; } }); } returnnull; }@OverridepublicConnectiongetConnection()throwsSQLException{if(list.size()>0){finalConnectionconnection=list.removeFirst();//看看池的大小System.out.println(list.size());//返回一个动态代理对象return(Connection)Proxy.newProxyInstance(Demo1.class.getClassLoader(),connection.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//如果不是调用close方法,就按照正常的来调用if(!method.getName().equals("close")){method.invoke(connection,args);}else{//进到这里来,说明调用的是close方法list.add(connection);//再看看池的大小System.out.println(list.size());}returnnull;}});}returnnull;}
我们上面已经能够简单编写一个线程池了。下面我们来使用一下开源数据库连接池
DBCP
使用DBCP数据源的步骤:
导入两个jar包【Commons-dbcp.jar和Commons-pool.jar】
读取配置文件
获取BasicDataSourceFactory对象
创建DataSource对象
static{ try{ //读取配置文件 InputStreaminputStream=Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Propertiesproperties=newProperties(); properties.load(inputStream); //获取工厂对象 BasicDataSourceFactorybasicDataSourceFactory=newBasicDataSourceFactory(); dataSource=basicDataSourceFactory.createDataSource(properties); }catch(IOExceptione){ e.printStackTrace(); }catch(Exceptione){ e.printStackTrace(); } } publicstaticConnectiongetConnection()throwsSQLException{ returndataSource.getConnection(); } //这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】 publicstaticvoidrelease(Connectionconn,Statementst,ResultSetrs){ if(rs!=null){ try{ rs.close(); }catch(Exceptione){ e.printStackTrace(); } rs=null; } if(st!=null){ try{ st.close(); }catch(Exceptione){ e.printStackTrace(); } } if(conn!=null){ try{ conn.close(); }catch(Exceptione){ e.printStackTrace(); } } }privatestaticDataSourcedataSource=null;static{try{//读取配置文件InputStreaminputStream=Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");Propertiesproperties=newProperties();properties.load(inputStream);//获取工厂对象BasicDataSourceFactorybasicDataSourceFactory=newBasicDataSourceFactory();dataSource=basicDataSourceFactory.createDataSource(properties);}catch(IOExceptione){e.printStackTrace();}catch(Exceptione){e.printStackTrace();}}publicstaticConnectiongetConnection()throwsSQLException{returndataSource.getConnection();}//这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】publicstaticvoidrelease(Connectionconn,Statementst,ResultSetrs){if(rs!=null){try{rs.close();}catch(Exceptione){e.printStackTrace();}rs=null;}if(st!=null){try{st.close();}catch(Exceptione){e.printStackTrace();}}if(conn!=null){try{conn.close();}catch(Exceptione){e.printStackTrace();}}}staticDataSourcedataSource=null;
C3P0
C3P0数据源的性能更胜一筹,并且它可以使用XML配置文件配置信息!
步骤:
导入开发包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】
导入XML配置文件【可以在程序中自己一个一个配,C3P0的doc中的Configuration有XML文件的事例】
new出ComboPooledDataSource对象
static{ //如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的 comboPooledDataSource=newComboPooledDataSource("oracle"); } publicstaticConnectiongetConnection()throwsSQLException{ returncomboPooledDataSource.getConnection(); }privatestaticComboPooledDataSourcecomboPooledDataSource=null;static{//如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的comboPooledDataSource=newComboPooledDataSource("oracle");}publicstaticConnectiongetConnection()throwsSQLException{returncomboPooledDataSource.getConnection();}staticComboPooledDataSourcecomboPooledDataSource=null;
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文件的配置:
<Resourcename="jdbc/EmployeeDB" auth="Container" type="javax.sql.DataSource" username="root" password="root" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/zhongfucheng" maxActive="8" maxIdle="4"/> </Context><Context><Resourcename="jdbc/EmployeeDB"auth="Container"type="javax.sql.DataSource"username="root"password="root"driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/zhongfucheng"maxActive="8"maxIdle="4"/></Context>
//初始化JNDI容器 ContextinitCtx=newInitialContext(); //获取到JNDI容器 ContextenvCtx=(Context)initCtx.lookup("java:comp/env"); //扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池 DataSourceds=(DataSource) envCtx.lookup("jdbc/EmployeeDB"); Connectionconn=ds.getConnection(); System.out.println(conn); }try{//初始化JNDI容器ContextinitCtx=newInitialContext();//获取到JNDI容器ContextenvCtx=(Context)initCtx.lookup("java:comp/env");//扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池DataSourceds=(DataSource)envCtx.lookup("jdbc/EmployeeDB");Connectionconn=ds.getConnection();System.out.println(conn);}
使用dbutils框架
dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量
DbUtils类
提供了关闭连接,装载JDBC驱动,回滚提交事务等方法的工具类【比较少使用,因为我们学了连接池,就应该使用连接池连接数据库】
QueryRunner类
该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量
ResultSetHandler接口
该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。
使用DbUtils框架对数据库的CRUD
publicclassTest{ @org.junit.Test publicvoidadd()throwsSQLException{ //创建出QueryRunner对象 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="INSERTINTOstudent(id,name)VALUES(?,?)"; //我们发现query()方法有的需要传入Connection对象,有的不需要传入 //区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中 queryRunner.update(sql,newObject[]{"100","zhongfucheng"}); } @org.junit.Test publicvoidquery()throwsSQLException{ //创建出QueryRunner对象 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="SELECT*FROMstudent"; Listlist=(List)queryRunner.query(sql,newBeanListHandler(Student.class)); System.out.println(list.size()); } @org.junit.Test publicvoiddelete()throwsSQLException{ //创建出QueryRunner对象 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="DELETEFROMstudentWHEREid='100'"; queryRunner.update(sql); } @org.junit.Test publicvoidupdate()throwsSQLException{ //创建出QueryRunner对象 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="UPDATEstudentSETname=?WHEREid=?"; queryRunner.update(sql,newObject[]{"zhongfuchengaaa",1}); } @org.junit.Test publicvoidbatch()throwsSQLException{ //创建出QueryRunner对象 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="INSERTINTOstudent(name,id)VALUES(?,?)"; Object[][]objects=newObject[10][]; for(inti=0;i<10;i++){ objects[i]=newObject[]{"aaa",i+300}; } queryRunner.batch(sql,objects); } }/**使用DbUtils框架对数据库的CRUD*批处理***/publicclassTest{@org.junit.Testpublicvoidadd()throwsSQLException{//创建出QueryRunner对象QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="INSERTINTOstudent(id,name)VALUES(?,?)";//我们发现query()方法有的需要传入Connection对象,有的不需要传入//区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中queryRunner.update(sql,newObject[]{"100","zhongfucheng"});}@org.junit.Testpublicvoidquery()throwsSQLException{//创建出QueryRunner对象QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="SELECT*FROMstudent";Listlist=(List)queryRunner.query(sql,newBeanListHandler(Student.class));System.out.println(list.size());}@org.junit.Testpublicvoiddelete()throwsSQLException{//创建出QueryRunner对象QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="DELETEFROMstudentWHEREid='100'";queryRunner.update(sql);}@org.junit.Testpublicvoidupdate()throwsSQLException{//创建出QueryRunner对象QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="UPDATEstudentSETname=?WHEREid=?";queryRunner.update(sql,newObject[]{"zhongfuchengaaa",1});}@org.junit.Testpublicvoidbatch()throwsSQLException{//创建出QueryRunner对象QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="INSERTINTOstudent(name,id)VALUES(?,?)";Object[][]objects=newObject[10][];for(inti=0;i<10;i++){objects[i]=newObject[]{"aaa",i+300};}queryRunner.batch(sql,objects);}}
分页
分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。
Oracle实现分页
SELECT*FROM( SELECT列名,列名,ROWNUMrn FROM表名 WHEREROWNUM<=(currentPage*lineSize))temp WHEREtemp.rn>(currentPage-1)*lineSize;/*Oracle分页语法:@lineSize---每页显示数据行数@currentPage----当前所在页*/SELECT*FROM(SELECT列名,列名,ROWNUMrnFROM表名WHEREROWNUM<=(currentPage*lineSize))tempWHEREtemp.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实现分页
SELECT* FROM表名 LIMIT[START],length; /* 例子: 我现在规定每页显示5行数据,我要查询第2页的数据 分析: 1:第2页的数据其实就是从第6条数据开始,取5条 实现: 1:start为5【偏移量从0开始】 2:length为5 *//*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*/
总结:
使用JDBC连接数据库实现分页
下面是常见的分页图片
配合图片,看下我们的需求是什么:
算出有多少页的数据,显示在页面上
根据页码,从数据库显示相对应的数据。
分析:
算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】
使用Mysql或Oracle的分页语法即可
通过上面分析,我们会发现需要用到4个变量
intlineSize=3; //总记录数 inttotalRecord=getTotalRecord(); //假设用户指定的是第2页 intcurrentPage=2; //一共有多少页 intpageCount=getPageCount(totalRecord,lineSize); //使用什么数据库进行分页,记得要在JdbcUtils中改配置 List<Person>list=getPageData2(currentPage,lineSize); for(Personperson:list){ System.out.println(person); } //使用JDBC连接Mysql数据库实现分页 publicstaticList<Person>getPageData(intcurrentPage,intlineSize)throwsSQLException{ //从哪个位置开始取数据 intstart=(currentPage-1)*lineSize; QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="SELECTname,addressFROMpersonLIMIT?,?"; List<Person>persons=(List<Person>)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{start,lineSize}); returnpersons; } //使用JDBC连接Oracle数据库实现分页 publicstaticList<Person>getPageData2(intcurrentPage,intlineSize)throwsSQLException{ //从哪个位置开始取数据 intstart=(currentPage-1)*lineSize; //读取前N条数据 intend=currentPage*lineSize; QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="SELECT"+ "name,"+ "address"+ "FROM("+ "SELECT"+ "name,"+ "address,"+ "ROWNUMrn"+ "FROMperson"+ "WHEREROWNUM<=?"+ ")tempWHEREtemp.rn>?"; List<Person>persons=(List<Person>)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{end,start}); returnpersons; } publicstaticintgetPageCount(inttotalRecord,intlineSize){ //简单算法 //return(totalRecord-1)/lineSize+1; //此算法比较好理解,把数据代代进去就知道了。 returntotalRecord%lineSize==0?(totalRecord/lineSize):(totalRecord/lineSize)+1; } publicstaticintgetTotalRecord()throwsSQLException{ //使用DbUtils框架查询数据库表中有多少条数据 QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource()); Stringsql="SELECTCOUNT(*)FROMperson"; Objecto=queryRunner.query(sql,newScalarHandler()); Stringss=o.toString(); ints=Integer.parseInt(ss); returns; }//每页显示3条数据intlineSize=3;//总记录数inttotalRecord=getTotalRecord();//假设用户指定的是第2页intcurrentPage=2;//一共有多少页intpageCount=getPageCount(totalRecord,lineSize);//使用什么数据库进行分页,记得要在JdbcUtils中改配置List<Person>list=getPageData2(currentPage,lineSize);for(Personperson:list){System.out.println(person);}//使用JDBC连接Mysql数据库实现分页publicstaticList<Person>getPageData(intcurrentPage,intlineSize)throwsSQLException{//从哪个位置开始取数据intstart=(currentPage-1)*lineSize;QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="SELECTname,addressFROMpersonLIMIT?,?";List<Person>persons=(List<Person>)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{start,lineSize});returnpersons;}//使用JDBC连接Oracle数据库实现分页publicstaticList<Person>getPageData2(intcurrentPage,intlineSize)throwsSQLException{//从哪个位置开始取数据intstart=(currentPage-1)*lineSize;//读取前N条数据intend=currentPage*lineSize;QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="SELECT"+"name,"+"address"+"FROM("+"SELECT"+"name,"+"address,"+"ROWNUMrn"+"FROMperson"+"WHEREROWNUM<=?"+")tempWHEREtemp.rn>?";List<Person>persons=(List<Person>)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{end,start});returnpersons;}publicstaticintgetPageCount(inttotalRecord,intlineSize){//简单算法//return(totalRecord-1)/lineSize+1;//此算法比较好理解,把数据代代进去就知道了。returntotalRecord%lineSize==0?(totalRecord/lineSize):(totalRecord/lineSize)+1;}publicstaticintgetTotalRecord()throwsSQLException{//使用DbUtils框架查询数据库表中有多少条数据QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());Stringsql="SELECTCOUNT(*)FROMperson";Objecto=queryRunner.query(sql,newScalarHandler());Stringss=o.toString();ints=Integer.parseInt(ss);returns;}
最后
有帮助?好看!转发!乐于输出干货的Java技术公众号:Java3y。公众号内有200多篇原创技术文章、海量视频资源、精美脑图,不妨来关注一下!