1.数据库连接池什么是数据库连接池简单来说:数据库连接池就是提供连接的。。。为什么我们要使用数据库连接池
数据库的连接的建立和关闭是非常消耗资源的
频繁地打开、关闭连接造成系统性能低下
编写连接池
编写连接池需实现java.sql.DataSource接口
创建批量的Connection用LinkedList保存【既然是个池,当然用集合保存、、LinkedList底层是链表,对增删性能较好】
实现getConnetion(),让getConnection()每次调用,都是在LinkedList中取一个Connection返回给用户
调用Connection.close()方法,Connction返回给LinkedList
privatestaticLinkedListlist=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=;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()>?list.removeFirst():null;
}
我们已经完成前三步了,现在问题来了。我们调用Conncetion.close()方法,是把数据库的物理连接关掉,而不是返回给LinkedList的解决思路:
写一个Connection子类,覆盖close()方法
写一个Connection包装类,增强close()方法
用动态代理,返回一个代理对象出去,拦截close()方法的调用,对close()增强
分析第一个思路:
Connection是通过数据库驱动加载的,保存了数据的信息。写一个子类Connection,new出对象,子类的Connction无法直接继承父类的数据信息,也就是说子类的Connection是无法连接数据库的,更别谈覆盖close()方法了。
分析第二个思路:
写一个Connection包装类。
写一个类,实现与被增强对象的相同接口【Connection接口】
定义一个变量,指向被增强的对象
定义构造方法,接收被增强对象
覆盖想增强的方法
对于不想增强的方法,直接调用被增强对象的方法
这个思路本身是没什么毛病的,就是实现接口时,方法太多了!,所以我们也不使用此方法
分析第三个思路代码实现:
@Override
publicConnectiongetConnection()throwsSQLException{
if(list.size()>){
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();
}
}
}
C3P0C3P0数据源的性能更胜一筹,并且它可以使用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();
}
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文件的配置:
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"/>
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接口的实现类即可。
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
* 批处理
*
* */
publicclassTest{
@org.junit.Test
publicvoidadd()throwsSQLException{
//创建出QueryRunner对象
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="INSERT INTO student (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 * FROM student";
Listlist=(List)queryRunner.query(sql,newBeanListHandler(Student.class));
System.out.println(list.size());
}
@org.junit.Test
publicvoiddelete()throwsSQLException{
//创建出QueryRunner对象
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="DELETE FROM student WHERE id='100'";
queryRunner.update(sql);
}
@org.junit.Test
publicvoidupdate()throwsSQLException{
//创建出QueryRunner对象
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="UPDATE student SET name=? WHERE id=?";
queryRunner.update(sql,newObject[]{"zhongfuchengaaa",1});
}
@org.junit.Test
publicvoidbatch()throwsSQLException{
//创建出QueryRunner对象
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="INSERT INTO student (name,id) VALUES(?,?)";
Object[][]objects=newObject[10][];
for(inti=;i
objects[i]=newObject[]{"aaa",i+300};
}
queryRunner.batch(sql,objects);
}
}
分页分页技术是非常常见的,在搜索引擎下搜索页面,不可能把全部数据都显示在一个页面里边。所以我们用到了分页技术。Oracle实现分页
/*
Oracle分页语法:
@lineSize---每页显示数据行数
@currentPage----当前所在页
*/
SELECT*FROM(
SELECT列名,列名,ROWNUMrn
FROM表名
WHEREROWNUM
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
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先获取currentPage*lineSize条数据,从(currentPage-1)*lineSize开始取数据
使用JDBC连接数据库实现分页下面是常见的分页图片配合图片,看下我们的需求是什么:
算出有多少页的数据,显示在页面上
根据页码,从数据库显示相对应的数据。
分析:
算出有多少页数据这是非常简单的【在数据库中查询有多少条记录,你每页显示多少条记录,就可以算出有多少页数据了】
使用Mysql或Oracle的分页语法即可
通过上面分析,我们会发现需要用到4个变量
currentPage--当前页【由用户决定的】
totalRecord--总数据数【查询表可知】
lineSize--每页显示数据的数量【由我们开发人员决定】
pageCount--页数【totalRecord和lineSize决定】
//每页显示3条数据
intlineSize=3;
//总记录数
inttotalRecord=getTotalRecord();
//假设用户指定的是第2页
intcurrentPage=2;
//一共有多少页
intpageCount=getPageCount(totalRecord,lineSize);
//使用什么数据库进行分页,记得要在JdbcUtils中改配置
Listlist=getPageData2(currentPage,lineSize);
for(Personperson:list){
System.out.println(person);
}
}
//使用JDBC连接Mysql数据库实现分页
publicstaticListgetPageData(intcurrentPage,intlineSize)throwsSQLException{
//从哪个位置开始取数据
intstart=(currentPage-1)*lineSize;
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="SELECT name,address FROM person LIMIT ?,?";
Listpersons=(List)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{start,lineSize});
returnpersons;
}
//使用JDBC连接Oracle数据库实现分页
publicstaticListgetPageData2(intcurrentPage,intlineSize)throwsSQLException{
//从哪个位置开始取数据
intstart=(currentPage-1)*lineSize;
//读取前N条数据
intend=currentPage*lineSize;
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="SELECT "+
" name, "+
" address "+
"FROM ( "+
" SELECT "+
" name, "+
" address , "+
" ROWNUM rn "+
" FROM person "+
" WHERE ROWNUM
")temp WHERE temp.rn>?";
Listpersons=(List)queryRunner.query(sql,newBeanListHandler(Person.class),newObject[]{end,start});
returnpersons;
}
publicstaticintgetPageCount(inttotalRecord,intlineSize){
//简单算法
//return (totalRecord - 1) / lineSize + 1;
//此算法比较好理解,把数据代代进去就知道了。
returntotalRecord%lineSize==?(totalRecord/lineSize):(totalRecord/lineSize)+1;
}
publicstaticintgetTotalRecord()throwsSQLException{
//使用DbUtils框架查询数据库表中有多少条数据
QueryRunnerqueryRunner=newQueryRunner(JdbcUtils.getDataSource());
Stringsql="SELECT COUNT(*) FROM person";
Objecto=queryRunner.query(sql,newScalarHandler());
Stringss=o.toString();
ints=Integer.parseInt(ss);
returns;
}
如果文章有错的地方欢迎指正,大家互相交流。
领取专属 10元无门槛券
私享最新 技术干货