JDBC-数据库连接池/操作
1.数据库连接池的必要性
不使用数据库连接池:
在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:
在主程序(如servlet、beans、DAO)中建立数据库连接。
进行sql操作
断开数据库连接。
这种模式开发,存在的问题:
普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。
这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
2.数据库连接池的基本思想
数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,
只需从“缓冲池”中取出一个,使用完毕之后再放回去。数据库连接池负责分配、管理和释放数据库连接,
它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些
数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的
最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
3.数据库连接池技术的优点
资源重用:
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
更快的系统反应速度:
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
新的资源分配手段:
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源
统一的连接管理,避免数据库连接泄露:
在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
4.两种开源的数据库连接池
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
DBCP 数据库连接池
C3P0 数据库连接池
DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
DBCP数据源
DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool. 如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
注意:
数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
C3P0数据源
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
c3p0与dbcp区别
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
Apache—DBUtils简介
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils
能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
DbUtils类
DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL 情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
public static void commitAndClose(Connection conn)throws SQLException 用来提交连接的事务,然后关闭连接
public static void commitAndCloseQuietly(Connection conn): 用来提交连接的事务,然后关闭连接,并且在关闭连接时不抛出SQL异常。
public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
JDBC版
package com.jdbc.dbutils;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import org.apache.commons.dbutils.DbUtils;
public class JDBCTools {
private static String driver;
private static String url;
private static String user;
private static String password;
static{
//加载jdbc.properties资源配置文件
Properties pro = new Properties();
try {
pro.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));
//初始化参数
driver = pro.getProperty("driver");
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
DbUtils.loadDriver(driver);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接
* @return Connection 数据库连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
Connection conn = null;
conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放数据库连接资源
* @param rs ResultSet 查询数据库的结果集
* @param st Statement 执行SQL语句的Statement
* @param conn Connection 数据库连接
*/
public static void free(ResultSet rs,Statement st,Connection conn){
DbUtils.closeQuietly(rs);
DbUtils.closeQuietly(st);
DbUtils.closeQuietly(conn);
}
public static void free(ResultSet rs){
try {
DbUtils.close(rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void free(Statement st){
try {
DbUtils.close(st);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void free(Connection conn){
try {
DbUtils.close(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void freeCommit(Connection conn){
DbUtils.commitAndCloseQuietly(conn);
}
}
QueryRunner类
该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理、考虑了事务处理需要共用Connection。
该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryRunner类提供了两个构造方法:
QueryRunner():默认的构造方法
QueryRunner(DataSource ds):需要一个 javax.sql.DataSource 来作参数的构造方法。
使用QueryRunner类实现更新
public int update(Connection conn, String sql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
public int update(Connection conn, String sql) throws SQLException:用来执行一个不需要置换参数的更新操作。
@Test
public void testQueryRunnerUpdate() {
// 1.创建QueryRunner的实例
QueryRunner qr = new QueryRunner();
Connection conn = null;
try {
// 2.获取连接
conn = JDBCTools.getConnection();
String sql = "insert into t_stu values(null,?,?,?,?)";
// 3、使用update方法
qr.update(conn, sql, "陈乔恩", "女", "H5", "170102班");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.free(null, null, conn);
}
}
//要考虑事务的话,连接不关闭
public static int update(Connection conn, String sql, Object[] params) throws SQLException{
// 1.创建QueryRunner的实例
QueryRunner qr = new QueryRunner();
return qr.update(conn, sql, params);
}
使用QueryRunner类实现查询
public Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
public Object query(String sql, ResultSetHandler rsh, Object... params) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource 方法中重新获得 Connection。
public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
public Object query( String sql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
ResultSetHandler接口
该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。
ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet rs)
该方法的返回值将作为QueryRunner类的query()方法的返回值。
@Test
public void testResultSetHandler() {
// 1.创建QueryRunner的实例
QueryRunner qr = new QueryRunner();
Connection conn = null;
try {
// 2.获取连接
conn = JDBCTools.getConnection();
class MyResultSetHandler implements ResultSetHandler{
@Override
public Object handle(ResultSet rs) throws SQLException {
Student stu = new Student();
if (rs.next()) {
stu.setId(rs.getInt(1));
stu.setSname(rs.getString(2));
stu.setSex(rs.getString(3));
stu.setMajor(rs.getString(4));
stu.setClasses(rs.getString(5));
}
return stu;
}
}
String sql = "select sno,sname,sex,major,classes from t_stu where sno =?";
// 3、使用query方法
// QueryRunner 的 query 方法的返回值取决于其 ResultSetHandler 参数的handle 方法的返回值
Object obj = qr.query(conn, sql, new MyResultSetHandler(),1);
System.out.println(obj);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.free(null, null, conn);
}
}
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ColumnListHandler:将结果集中某一列的数据存放到List中。
KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
BeanHandler实现类
BeanHandler: 把结果集的第一条记录转为创建 BeanHandler 对象时传入的 Class参数对应的对象.
当JavaBean的属性名与字段名不一致时,可以通过指定别名告知属性名
BeanListHandler实现类
BeanListHandler: 把结果集转为一个 List, 该 List 不为 null, 但可能为空集合(size() 方法返回 0) 若
SQL 语句的确能够查询到记录, List 中存放创建 BeanListHandler 传入的 Class对象对应的对象.
MapHandler实现类
MapListHandler实现类
ScalarHandler实现类(可以用来统计)
ScalarHandler: 把结果集转为一个数值(可以是任意基本数据类型和字符串, Date 等)返回
ScalarHandler()只取第一行第一列
ScalarHandler(int columnIndex):取第一行的第columnIndex列
ScalarHandler(String columnName):取第一行的列名为columnName列的值
QueryLoader类
QueryLoader类是一个从一个文件加载查询到一个Map的简单的类。然后,当需要的时候,你从 Map 中选择一些查询。
即把sql语句也配置到文件中
@Test
public void testQueryLoader(){
try {
// /表示类路径的根目录
Map<String,String> sqls = QueryLoader.instance().load("/sql.properties");
String sql = sqls.get("insert");
System.out.println(sql);
} catch (IOException e) {
e.printStackTrace();
}
}
还没有评论,来说两句吧...