JDBC连接池&DBUtils
使用连接池改造JDBC的工具类:
1.1.1 需求:
传统JDBC的操作,对连接的对象销毁不是特别好.每次创建和销毁连接都是需要花费时间.可以使用连接池优化的程序.
- 在程序开始的时候,可以创建几个连接,将连接放入到连接池中.用户使用连接的时候,可以从连接池中进行获取.用完之后,可以将连接归还连接池.
1.1.2 分析:
1.1.2.1 技术分析:
【自定义连接池】(了解)
SUN公司提供了一个连接池的接口.(javax.sql.DataSource).
定义一个连接池:实现这个接口.
使用List集合存放多个连接的对象.
【自定义连接池的代码】
public class MyDataSource implements DataSource{
// 创建一个List集合用于存放多个连接对象.
private List<Connection> list = new ArrayList<Connection>();
// 在程序开始的时候,初始化几个连接,将连接存放到list中.
public MyDataSource() {
// 初始化3个连接:
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
@Override
// 获得连接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
// 归还连接的方法:
public void addBack(Connection conn){
list.add(conn);
}
...
}
【自定义连接池中问题及如何解决】
Ø 问题?
1.如果使用自定义连接池,那么需要额外记住自定义连接池中的API.
2.能不能使用面向接口的编程方式.
Ø 解决:
不额外提供API方法,就可以解决上述两个问题!!!
能不能还调用Connection的close方法.能不能增强Connection的close方法,原有的销毁变为归还!!!
Ø 如何增强Connection的close方法:
增强一个Java类中的某个方法有几种方式???
一种方式:继承的方式
能够控制这个类的构造的时候才可以使用继承
二种方式:装饰者模式方式
包装对象和被包装的对象都要实现相同的接口
包装的对象中需要获得到被包装对象的引用
缺点:如果接口的方法比较多增强其中的某个方法其他的功能的方法需要原有调用
三种方式:动态代理的方式
被增强的对象实现接口就可以
【继承和装饰者的案例】
/
* 继承的方式增强一个类中某个方法:
*/
class Man{
public void run(){
System.out.println("跑....");
}
}
class SuperMan extends Man{
public void run(){
// super.run();
System.out.println("飞....");
}
}
/**
* 使用装饰者的方式完成类的方法的增强
*/
interface Waiter{
public void server();
}
class Waiteress implements Waiter{
@Override
public void server() {
System.out.println("服务...");
}
}
class WaiteressWrapper implements Waiter{
private Waiter waiter;
public WaiteressWrapper(Waiter waiter) {
this.waiter = waiter;
}
@Override
public void server() {
System.out.println("微笑...");
// this.waiter.server();
}
}
【使用装饰者模式增强Connection的close方法】
public class MyConnection implements Connection{
private Connection conn;
private List<Connection> list;
public MyConnection(Connection conn,List<Connection> list) {
this.conn = conn;
this.list = list;
}
@Override
public void close() throws SQLException {
list.add(conn);
}
...
}
连接池的getConnection方法: @Override
// 获得连接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
MyConnection myConn = new MyConnection(conn, list);
return myConn;
}
【常见的开源的数据库连接池】:
DBCP:
DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。
C3P0:
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
Tomcat内置连接池:
【DBCP连接池的使用】
第一步:引入DBCP连接池的jar包.
第二步:编写DBCP代码:
手动设置参数:
配置文件设置参数:
【DBCP连接池的使用】
@Test
/**
* 手动方式:
*/
public void demo1(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///web_07");
dataSource.setUsername("root");
dataSource.setPassword("123");
try{
// 获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from category";
// 预编译SQL:
stmt = conn.prepareStatement(sql);
// 执行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
@Test
/**
* 配置文件方式:
*/
public void demo2(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
Properties properties = new Properties();
try{
properties.load(new FileInputStream("src/dbcpconfig.properties"));
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
// 获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from category";
// 预编译SQL:
stmt = conn.prepareStatement(sql);
// 执行SQL:
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt, conn);
}
}
【C3P0连接池的使用】
第一步:引入C3P0连接池的jar包
- 配置文件 c3p0.properties
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- This is default config! -->
<default-config>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!-- This is my config for mysql-->
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF8</property>
<property name="user">root</property>
<property name="password"></property>
<!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
<property name="initialPoolSize">10</property>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
<property name="maxIdleTime">30</property>
<!--连接池中保留的最大连接数。默认值: 15 -->
<property name="maxPoolSize">100</property>
<!-- 连接池中保留的最小连接数,默认为:3-->
<property name="minPoolSize">10</property>
<!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0-->
<property name="maxStatements">200</property>
<!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
<property name="checkoutTimeout" value="3000"/>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
<property name="acquireIncrement" value="2"/>
<!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次-->
<property name="acquireRetryAttempts" value="0"/>
<!--重新尝试的时间间隔,默认为:1000毫秒-->
<property name="acquireRetryDelay" value="1000" />
<!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
<property name="autoCommitOnClose">false</property>
<!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null -->
<property name="automaticTestTable">Test</property>
<!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
<property name="idleConnectionTestPeriod">60</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->
<property name="maxStatementsPerConnection"></property>
</named-config>
<!-- This is my config for oracle -->
<named-config name="oracle">
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="jdbcUrl">jdbc:oracle:thin:@localhost:1521:orcl</property>
<!--mysql:jdbc:mysql://103.249.130.171:3306/mallshop?useUnicode=true&characterEncoding=UTF8-->
<property name="user">scott</property>
<property name="password">liang</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
远程连接jdbc:mysql://103.249.130.171:3306/mallshop?useUnicode=true&characterEncoding=UTF8
第二步:编写代码:
* 手动设置参数:
* 配置文件设置参数:
【C3P0改造工具类】
public class JDBCUtils2 {
private static final ComboPooledDataSource DATA_SOURCE =new ComboPooledDataSource();
/**
* 获得连接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = DATA_SOURCE.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
ResultSetHandler
我们知道在执行select语句之后得到的是ResultSet,然后我们还需要对ResultSet进行转换,得到最终我们想要的数据。你可以希望把ResultSet的数据放到一个List中,也可能想把数据放到一个Map中,或是一个Bean中。
DBUtils提供了一个接口ResultSetHandler,它就是用来ResultSet转换成目标类型的工具。你可以自己去实现这个接口,把ResultSet转换成你想要的类型。
DBUtils提供了很多个ResultSetHandler接口的实现,这些实现已经基本够用了,我们通常不用自己去实现ResultSet接口了。
MapHandler:单行处理器!把结果集转换成Map<String,Object>,其中列名为键
MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;
BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;
BeanListHandler:多行处理器!把结果集转换成List<Bean>;
ColumnListHandler:多行单列处理器!把结果集转换成List<Object>,使用ColumnListHandler时需要指定某一列的名称或编号,例如:new ColumListHandler(“name”)表示把name列的数据放到List中。
ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。
Map处理器
Bean处理器
Column处理器
Scalar处理器
QueryRunner之查询
QueryRunner的查询方法是:
public <T> T query(String sql, ResultSetHandler<T> rh, Object… params)
public <T> T query(Connection con, String sql, ResultSetHandler<T> rh, Object… params)
query()方法会通过sql语句和params查询出ResultSet,然后通过rh把ResultSet转换成对应的类型再返回。
@Test
public void fun1() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
Map<String,Object> map = qr.query(sql, new MapHandler()[[ThinkPad1]](#_msocom_1) , "S_2000");
System.out.println(map);
}
@Test
public void fun2() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Map<String,Object>> list = qr.query(sql, new MapListHandler() );
for(Map<String,Object> map : list) {
System.out.println(map);
}
}
@Test
public void fun3() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student where number=?";
Student stu = qr.query(sql, new BeanHandler<Student>(Student.class);
System.out.println(stu);
}
@Test
public void fun4() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
for(Student stu : list) {
System.out.println(stu);
}
}
@Test
public void fun5() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
List<Object> list = qr.query(sql, new ColumnListHandler("name") );
for(Object s : list) {
System.out.println(s);
}
}
@Test
public void fun6() throws SQLException {
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select count(*) from tab_student";
Number number = (Number)qr.query(sql, new ScalarHandler());
int cnt = number.intValue();
System.out.println(cnt);
}
Druid 阿里数据库连接池
使用方式
- 导入jar包
- 定义配置文件
- 配置文件是properties形式的
- 可以自定义名成可以放到任意目录下
- 获取DataSource
通过工厂类来进行获取 DruidDataSourceFactory - 获取连接 getConnection()
Spring JDBC
由Spring框架提供的对JDBC简单封装,提供了一个JDBCTemplate对象简化JDBC的开发
基本步骤
- 导入jar包
- 创建JdbcTemplate对象,依赖于数据源DataSource
- JdbcTemplate template = new JdbcTemplate(ds);
- 调用JdbcTemplate的方法来完成CRUD操作
- update():执行DML语句。增、删、改语句
- queryForMap():查询结果将结果封装为map集合
- queryForList():查询结果将结果封装为list集合
- query():查询结果,将结果封装为JavaBean对象
- queryForObject:查询结果,将结果封装为Object对象