1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > JDBC第四篇【数据库连接池 DbUtils框架 分页】(修订版)

JDBC第四篇【数据库连接池 DbUtils框架 分页】(修订版)

时间:2022-11-05 01:10:55

相关推荐

JDBC第四篇【数据库连接池 DbUtils框架 分页】(修订版)

前言

只有光头才能变强。

文本已收录至我的GitHub仓库,欢迎Star:/ZhongFuCheng3y/3y

1.数据库连接池

什么是数据库连接池

简单来说:数据库连接池就是提供连接的。。。

为什么我们要使用数据库连接池

编写连接池

编写连接池需实现java.sql.DataSource接口

创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】

实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户

调用Connection.close()方法,Connction返回给LinkedList

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<>();

//获取连接只需要一次就够了,所以用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;

}

我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的

解决思路:

写一个Connection子类,覆盖close()方法

写一个Connection包装类,增强close()方法

用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强

分析第一个思路:

分析第二个思路:

写一个类,实现与被增强对象的相同接口【Connection接口】

定义一个变量,指向被增强的对象

定义构造方法,接收被增强对象

覆盖想增强的方法

对于不想增强的方法,直接调用被增强对象的方法

这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法

分析第三个思路代码实现:

@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;}

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;

}

我们上面已经能够简单编写一个线程池了。下面我们来使用一下开源数据库连接池

DBCP

使用DBCP数据源的步骤:

导入两个jar包【Commons-dbcp.jar和Commons-pool.jar】

读取配置文件

获取BasicDataSourceFactory对象

创建DataSource对象

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;

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();

}

}

}

C3P0

C3P0数据源的性能更胜一筹,并且它可以使用XML配置文件配置信息!

步骤:

导入开发包【c3p0-0.9.2-pre1.jar】和【mchange-commons-0.2.jar】

导入XML配置文件【可以在程序中自己一个一个配,C3P0的doc中的Configuration有XML文件的事例】

new出ComboPooledDataSource对象

privatestaticComboPooledDataSourcecomboPooledDataSource=null;static{//如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的comboPooledDataSource=newComboPooledDataSource("oracle");}publicstaticConnectiongetConnection()throwsSQLException{returncomboPooledDataSource.getConnection();}staticComboPooledDataSourcecomboPooledDataSource=null;

static{

//如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的

comboPooledDataSource=newComboPooledDataSource("oracle");

}

publicstaticConnectiongetConnection()throwsSQLException{

returncomboPooledDataSource.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文件的配置:

<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>

<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>

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);}

//初始化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

/**使用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);}}

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);

}

}

分页

分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。

Oracle实现分页

/*Oracle分页语法:@lineSize---每页显示数据行数@currentPage----当前所在页*/SELECT*FROM(SELECT列名,列名,ROWNUMrnFROM表名WHEREROWNUM<=(currentPage*lineSize))tempWHEREtemp.rn>(currentPage-1)*lineSize;

SELECT*FROM(

SELECT列名,列名,ROWNUMrn

FROM表名

WHEREROWNUM<=(currentPage*lineSize))temp

WHEREtemp.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*/

SELECT*

FROM表名

LIMIT[START],length;

/*

例子:

我现在规定每页显示5行数据,我要查询第2页的数据

分析:

1:第2页的数据其实就是从第6条数据开始,取5条

实现:

1:start为5【偏移量从0开始】

2:length为5

*/

总结:

使用JDBC连接数据库实现分页

下面是常见的分页图片

配合图片,看下我们的需求是什么:

算出有多少页的数据,显示在页面上

根据页码,从数据库显示相对应的数据。

分析:

算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】

使用Mysql或Oracle的分页语法即可

通过上面分析,我们会发现需要用到4个变量

//每页显示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;}

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多篇原创技术文章、海量视频资源、精美脑图,不妨来关注一下!

有帮助?好看!转发!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。