本节内容:
- JDBC连接池
- DBUtils
一、JDBC连接池
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采取连接池技术,来共享连接Connection。在程序开始的时候,可以创建几个连接,将连接放入到连接池中。用户使用连接的时候,可以从连接池中进行获取。用完之后,可以将连接归还连接池。
连接池主要解决的是性能问题。
1. 连接池概述
(1)概念
用池来管理Connection,这样可以重复使用Connection。有了池,我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。
(2)规范
Java为数据库连接池提供了公共的接口,javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便地切换不同厂商的连接池。
常见的连接池:DBCP、C3P0。还有tomcat自带的JNDI,JNDI使用的概率极低。市场上使用率最高的是C3P0。
2. 自定义连接池(了解)
定义一个连接池实现javax.sql.DataSource,使用List集合存放多个连接对象。如果我们需要编写自定义连接池,需要完成以下步骤:
- 创建连接池(作为数据源),需要实现接口javax.sql.DataDource。因为我们只使用该接口中getConnection()方法,简化本案例,我们只实现javax.sql.DataDource中的getConnection()方法,而没有实现javax.sql.DataDource中的其他接口
- 提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList
- 本案例在静态代码块中,为连接池初始化5个连接(本案例中初始化连接使用上一篇文章中实现的工具类)
- 之后程序如果需要连接,调用实现类的getConnection(),该方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除。
- 当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中。
MyDataSource.java
package cn.itheima.jdbc.DataSource; import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger; import javax.sql.DataSource; import cn.itheima.jdbc.utils.JDBCUtils_V3; public class MyDataSource implements DataSource{
//1.创建1个容器用于存储Connection对象
private static LinkedList<Connection> pool = new LinkedList<Connection>(); //2.创建5个连接放到容器中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils_V3.getConnection();
pool.add(conn);
}
} /**
* 重写获取连接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判断
if(pool.size()==0){
//4.池子里面没有,我们再创建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils_V3.getConnection();
pool.add(conn);
}
}
//5.从池子里面获取一个连接对象Connection
conn = pool.remove(0);
return conn;
} /**
* 归还连接对象到连接池中去
*/
public void backConnection(Connection conn){
pool.add(conn);
} @Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub } @Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub } @Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
} @Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
} @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
} }
TestMyDataSource.java
package cn.itheima.jdbc.test; import java.sql.Connection;
import java.sql.PreparedStatement; import javax.sql.DataSource; import org.junit.Test; import cn.itheima.jdbc.DataSource.MyDataSource;
import cn.itheima.jdbc.DataSource.MyDataSource1;
import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestMyDataSource {
/**
* 添加用户
* 使用未改造过的connection
*/
@Test
public void testAddUser() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.创建自定义连接池对象
MyDataSource dataSource = new MyDataSource();
try {
// 2.从池子中获取连接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "吕布");
pstmt.setString(2, "貂蝉");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
dataSource.backConnection(conn);
}
} }
JDBCUtils_V3.java
package cn.itheima.jdbc.utils; import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle; /**
* 提供获取连接和释放资源的 方法
*
* @author Never Say Never
* @date 2017年10月29日
* @version V1.0
*/
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password; /**
* 静态代码块加载配置文件信息
*/
static {
try {
// 1.通过当前类获取类加载器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通过类加载器的方法获得一个输入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.创建一个properties对象
Properties props = new Properties();
// 4.加载输入流
props.load(is);
// 5.获取相关参数的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
} } /**
* 获取连接方法
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
} /**
* 释放资源方法
*
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
}
3. 自定义连接池(方法增强)
上面的自定义连接池示例中存在严重问题,用户调用getConnection()获得连接后,必须使用backConnection(conn)方法进行连接的归还,如果用户调用conn.close()将连接真正的释放,连接池中将出现无连接可用。
我们希望用户即使调用了close()方法,连接仍归还给连接池。我们需要对close()方法进行增强,从而实现将连接归还给连接池的功能。
(1)方法增强的方式
- 继承:子类继承父类,将父类的方法进行重写,从而进行增强
- 使用前提:必须有父类,且存在继承关系。
- 装饰者设计模式,此设计模式专门用于增强方法
- 使用前提:必须有接口
- 缺点:需要将接口的所有方法都实现
- 动态代理:在运行时动态的创建代理类,完成增强操作,与装饰者类似。
- 使用前提:必须有接口
- 难点:需要反射技术
- 字节码增强,运行时创建目标类子类,从而进行增强
- 常见第三方框架:cglib、javassist等
(2)装饰者设计模式
设计模式:专门为解决某一类问题,而编写的固定格式的代码。
装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B
- 创建类B,并实现接口A
- 提供类B的构造方法,参数类型A,用于接收A接口的其他实现类(C)
- 给类B添加类型为A成员变量,用于存放A接口的其他实现类
- 增强需要的方法
- 实现不需要增强的方法,方法里调用成员变量存放的其他实现类对应的方法
MyConnection.java
package cn.itheima.jdbc.DataSource; import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor; //1.实现同一个接口Connection
public class MyConnection implements Connection {
//3.定义一个变量
private Connection conn; private LinkedList<Connection> pool; // 2.编写一个构造方法(参数使用了面相对象的多态特性)
public MyConnection(Connection conn,LinkedList<Connection> pool) {
this.conn=conn;
this.pool=pool;
} //4.书写需要增强的方法
@Override
public void close() throws SQLException {
pool.add(conn);
} /**
* 此方法必须覆盖!否则会出现空指针异常!!!
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
} @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public Statement createStatement() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public CallableStatement prepareCall(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public String nativeSQL(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
// TODO Auto-generated method stub } @Override
public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public void commit() throws SQLException {
// TODO Auto-generated method stub } @Override
public void rollback() throws SQLException {
// TODO Auto-generated method stub } @Override
public boolean isClosed() throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public DatabaseMetaData getMetaData() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setReadOnly(boolean readOnly) throws SQLException {
// TODO Auto-generated method stub } @Override
public boolean isReadOnly() throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public void setCatalog(String catalog) throws SQLException {
// TODO Auto-generated method stub } @Override
public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setTransactionIsolation(int level) throws SQLException {
// TODO Auto-generated method stub } @Override
public int getTransactionIsolation() throws SQLException {
// TODO Auto-generated method stub
return 0;
} @Override
public SQLWarning getWarnings() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void clearWarnings() throws SQLException {
// TODO Auto-generated method stub } @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
// TODO Auto-generated method stub } @Override
public void setHoldability(int holdability) throws SQLException {
// TODO Auto-generated method stub } @Override
public int getHoldability() throws SQLException {
// TODO Auto-generated method stub
return 0;
} @Override
public Savepoint setSavepoint() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Savepoint setSavepoint(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void rollback(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub } @Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub } @Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Clob createClob() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Blob createBlob() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public NClob createNClob() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public SQLXML createSQLXML() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public boolean isValid(int timeout) throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
// TODO Auto-generated method stub } @Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
// TODO Auto-generated method stub } @Override
public String getClientInfo(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Properties getClientInfo() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setSchema(String schema) throws SQLException {
// TODO Auto-generated method stub } @Override
public String getSchema() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void abort(Executor executor) throws SQLException {
// TODO Auto-generated method stub } @Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
// TODO Auto-generated method stub } @Override
public int getNetworkTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
} }
MyDataSource1.java
package cn.itheima.jdbc.DataSource; import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger; import javax.sql.DataSource; import cn.itheima.jdbc.utils.JDBCUtils_V3; public class MyDataSource1 implements DataSource{
//1.创建1个容器用于存储Connection对象
private static LinkedList<Connection> pool = new LinkedList<Connection>(); //2.创建5个连接放到容器中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils_V3.getConnection();
//放入池子中connection对象已经经过改造了
MyConnection myconn = new MyConnection(conn, pool);
pool.add(myconn);
}
} /**
* 重写获取连接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判断
if(pool.size()==0){
//4.池子里面没有,我们再创建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils_V3.getConnection();
//放入池子中connection对象已经经过改造了
MyConnection myconn = new MyConnection(conn, pool);
pool.add(myconn);
}
}
//5.从池子里面获取一个连接对象Connection
conn = pool.remove(0);
return conn;
} /**
* 归还连接对象到连接池中去
*/
public void backConnection(Connection conn){
pool.add(conn);
} @Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub } @Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub } @Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
} @Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
} @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
} @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
} @Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
} }
TestMyDataSource.java
package cn.itheima.jdbc.test; import java.sql.Connection;
import java.sql.PreparedStatement; import javax.sql.DataSource; import org.junit.Test; import cn.itheima.jdbc.DataSource.MyDataSource;
import cn.itheima.jdbc.DataSource.MyDataSource1;
import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestMyDataSource {
/**
* 添加用户
* 使用未改造过的connection
*/
@Test
public void testAddUser() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.创建自定义连接池对象
MyDataSource dataSource = new MyDataSource();
try {
// 2.从池子中获取连接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "吕布");
pstmt.setString(2, "貂蝉");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
dataSource.backConnection(conn);
}
} /**
* 添加用户
* 使用改造过的connection
*/
@Test
public void testAddUser1() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.创建自定义连接池对象
DataSource dataSource = new MyDataSource1();
try {
// 2.从池子中获取连接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
//3.必须在自定义的connection类中重写prepareStatement(sql)方法
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "吕布1");
pstmt.setString(2, "貂蝉1");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
}
}
JDBCUtils_V3.java
package cn.itheima.jdbc.utils; import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle; /**
* 提供获取连接和释放资源的 方法
*
* @author Never Say Never
* @date 2017年10月29日
* @version V1.0
*/
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password; /**
* 静态代码块加载配置文件信息
*/
static {
try {
// 1.通过当前类获取类加载器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通过类加载器的方法获得一个输入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.创建一个properties对象
Properties props = new Properties();
// 4.加载输入流
props.load(is);
// 5.获取相关参数的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
} } /**
* 获取连接方法
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
} /**
* 释放资源方法
*
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
}
4. C3P0连接池
C3P0开源免费的连接池。目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时可以添加配置文件c3p0-config.xml(或者c3p0.properties,大部分使用c3p0-config.xml),也可以不添加配置文件。它有两种使用方式,但是多数情况下都是用需要配置文件的那种方式。
(1)导入jar包
如果使用的是0.9.2版本,需要导入两个jar包。
如果使用的是0.9.1版本,只需要导入c3p0-0.9.1.2.jar。
(2)配置文件
- 配置文件名称:c3p0-config.xml(固定)
- 配置文件位置:src(类路径)
- 配置文件内容:命名配置
<c3p0-config> <named-config name="itheima">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web08</property>
<property name="user">root</property>
<property name="password">123456</property>
</named-config> </c3p0-config>
命名配置
- 配置文件内容:默认配置
<c3p0-config>
<default-config>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</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> </default-config>
</c3p0-config>
默认配置
(3)示例
c3p0提供核心工具类:ComboPooledDataSource,如果要使用连接池,必须创建该类的实例对象。
- new ComboPooledDataSource("itcast"); 使用配置文件“命名配置”
- <name-config name="itcast">
- new ComboPooledDataSource(); 使用配置文件“默认配置”
- <default-config>
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config> <default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web08</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config> <named-config name="itheima">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web08</property>
<property name="user">root</property>
<property name="password">123456</property>
</named-config> </c3p0-config>
c3p0-config.xml
import java.sql.Connection;
import java.sql.PreparedStatement; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestC3P0 {
@Test
public void testAddUser() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.创建自定义连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();// 加载默认的配置
// ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");//加载有名称的配置 try {
// 2.从池子中获取连接
conn = dataSource.getConnection();
String sql = "insert into tbl_user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "吕布2");
pstmt.setString(2, "貂蝉2");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
}
}
TestC3P0.java
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle; /**
* 提供获取连接和释放资源的 方法
*
* @author Never Say Never
* @date 2017年10月29日
* @version V1.0
*/
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password; /**
* 静态代码块加载配置文件信息
*/
static {
try {
// 1.通过当前类获取类加载器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通过类加载器的方法获得一个输入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.创建一个properties对象
Properties props = new Properties();
// 4.加载输入流
props.load(is);
// 5.获取相关参数的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
} } /**
* 获取连接方法
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
} /**
* 释放资源方法
*
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
}
JDBCUtils_V3.java
可以写一个工具类,修改下相关代码:
import java.sql.Connection;
import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Utils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima"); public static DataSource getDataSource() {
return dataSource;
} public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
C3P0Utils.java
import java.sql.Connection;
import java.sql.PreparedStatement; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; import cn.itheima.jdbc.utils.C3P0Utils;
import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestC3P0 {
@Test
public void testAddUser1() {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 2.从池子中获取连接
conn = C3P0Utils.getConnection();
String sql = "insert into tbl_user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "吕布3");
pstmt.setString(2, "貂蝉3");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
} }
TestC3P0.java
5. DBCP连接池
DBCP也是一个开源的连接池,是Apache上的一个java连接池项目,在企业开发中也比较常见,tomcat内置的连接池。
(1)导入jar包
commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
(2)配置文件
- 配置文件名称:*.properties
- 配置文件位置:任意,建议src(classpath/类路径)
- 配置文件内容:properties不能写中文,不支持在STS(Eclipse的一个变种)中修改,必须使用记事本修改内容,否则中文注释就乱码了
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=utf8
username=root
password=123456
db.properties
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class DBCPUtils {
private static DataSource dataSource;
static{
try {
//1.加载找properties文件输入流
InputStream is = DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
//2.加载输入流
Properties props = new Properties();
props.load(is);
//3.创建数据源
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
throw new RuntimeException(e);
}
} public static DataSource getDataSource(){
return dataSource;
} public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
DBCPUtils.java
import java.sql.Connection;
import java.sql.PreparedStatement; import org.junit.Test; import cn.itheima.jdbc.utils.DBCPUtils; public class TestDBCP { @Test
public void testUpdateUserById(){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DBCPUtils.getConnection();
String sql ="update tbl_user set upassword=? where uid=?";
pstmt= conn.prepareStatement(sql);
pstmt.setString(1, "柳岩");
pstmt.setInt(2, 20);
int rows = pstmt.executeUpdate();
if(rows>0){
System.out.println("更新成功!");
}else{
System.out.println("更新失败!");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
TestDBCP.java
二、DBUtils
如果在某些项目中,不使用框架,只使用JDBC开发,我们会发现冗余代码过多:
为了简化JDBC开发,我们采用apache commons组件的一个成员:DBUtils。
DBUtils就是JDBC的简化开发工具包。需要使用的技术:连接池(只用到里面的获得连接),SQL语句并没有变少以及设置sql语句中的参数。
1. JavaBean组件
JavaBean就是一个类,在开发中常用语封装数据。具有如下特性:
- 需要实现接口:java.io.Serializable,通常偷懒省略了。
- 提供私有字段:private 类型 字段名;
- 提供getter/setter方法
- 提供无参构造
一般将JavaBean类放到domain包或model包里。
2. DBUtils介绍
(1)概述
DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。在不使用框架的情况下,使用DBUtils的概率非常大。
DBUtils三个核心功能介绍:
- QueryRunner类,提供对sql语句操作的API
- ResultSetHandler接口,用于定义select操作后,怎样封装结果集
- DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
(2)QueryRunner核心类
- QueryRunner(Database ds),提供了数据源(连接池),DBUtils底层自动维护connection
- update(String sql, Object... params),执行更新数据。(增、删、改)
- query(String sql, ResultSetHandler<T> rsh, Object... params),执行查询
(3)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。
ArrayHandler | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
ArrayListHangler | 将结果集中的第一条记录封装到一个Object[]数组中,将这些数组在封装到List集合中 |
BeanHandler | 将结果集中的每一条记录封装到一个指定的javaBean中 |
BeanListHandler | 将结果集中每一条记录封装到指定的javaBean中,将这些JavaBean在封装到List集合中 |
ColumnListHandler | 将结果集中指定的列的字段值,封装到一个List集合中 |
KeyedHandler | 将结果集中每一条记录封装到Map<String,Object>,在将这个map结婚作为另一个Map的value,另一个Map集合的key是指定的字段的名称 |
MapHandler | 将结果集中第一条记录封装到Map<String,Object>集合中,key就是字段名称,value就是字段值 |
MapListHandler | 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中 |
ScalarHandler | 它用于单数据。例如select count(*) from table 操作 |
Map处理器:
每一条记录作为一个Map,Map的key是字段名称。
Bean处理器 :
必须搞一个javaBean。
Column处理器:
Scalar处理器:
(4)DbUtils工具类
- closeQuietly(Connection conn) 关闭连接,如果有异常try后不抛
- commitAndCloseQuietly(Connection conn) 提交并关闭连接
- rollbackAndCloseQuietly(Connection conn) 回滚并关闭连接
3. 示例:使用DBUtils完成增删改查操作
public class User {
private int uid;
private String uname;
private String upassword; public User() {
} public int getUid() {
return uid;
} public void setUid(int uid) {
this.uid = uid;
} public String getUname() {
return uname;
} public void setUname(String uname) {
this.uname = uname;
} public String getUpassword() {
return upassword;
} public void setUpassword(String upassword) {
this.upassword = upassword;
} }
User.java
import java.sql.Connection;
import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Utils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima"); public static DataSource getDataSource() {
return dataSource;
} public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
C3P0Utils.java
import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test; import cn.itheima.jdbc.utils.C3P0Utils; /**
* 测试DBUtils工具类的增删改操作
*
* @author Never Say Never
* @date 2017年10月31日
* @version V1.0
*/
public class TestDBUtils1 { /**
* 添加所有用户方法
*/
@Test
public void testAddUser() {
try {
// 1.创建核心类QueryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写SQL语句
String sql = "insert into user values(null,?,?)";
// 3.为占位符设置值
Object[] params = { "余淮", "耿耿" };
// 4.执行添加操作
int rows = qr.update(sql, params);
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} /**
* 根据id修改用户方法
*
*/
@Test
public void testUpdateUserById() {
try {
// 1.创建核心类QueryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写SQL语句
String sql = "update user set upassword=? where uid=?";
// 3.为站位符设置值
Object[] params = { "xxx", 21 };
// 4.执行添加操作
int rows = qr.update(sql, params);
if (rows > 0) {
System.out.println("修改成功!");
} else {
System.out.println("修改失败!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} /**
* 根据id删除用户方法
*/
@Test
public void testDeleteUserById() {
try {
// 1.创建核心类QueryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写SQL语句
String sql = "delete from user where uid=?";
// 3.为站位符设置值
Object[] params = {19};
// 4.执行添加操作
int rows = qr.update(sql, params);
if (rows > 0) {
System.out.println("删除成功!");
} else {
System.out.println("删除失败!");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
TestDBUtils1.java --添加、删除、更新
import java.sql.SQLException;
import java.util.List;
import java.util.Map; import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test; import cn.itheima.domain.User;
import cn.itheima.jdbc.utils.C3P0Utils; /**
* 测试DBUtils查询操作
*
* @author Never Say Never
* @date 2017年10月31日
* @version V1.0
*/
public class TestDBUtils2 { /*
* 查询所有用户方法
*/
@Test
public void testQueryAll() {
try {
// 1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql = "select * from user";
// 3.执行查询操作
List<User> users = qr.query(sql, new BeanListHandler<User>(User.class));
// 4.对结果集集合进行遍历
for (User user : users) {
System.out.println(user.getUname() + " : " + user.getUpassword());
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
} /*
* 根据id查询用户方法
*/
@Test
public void testQueryUserById() {
try {
// 1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql = "select * from user where uid=?";
//3.为占位符设置值
Object[] params = {21};
// 4.执行查询操作
User user = qr.query(sql, new BeanHandler<User>(User.class), params);
System.out.println(user.getUname() + " : " + user.getUpassword());
} catch (SQLException e) {
throw new RuntimeException(e);
}
} /*
* 根据所有用户的总个数
*/
@Test
public void testQueryCount() {
try {
// 1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql = "select count(*) from tbl_user";
// 4.执行查询操作
Long count = (Long) qr.query(sql, new ScalarHandler());
System.out.println(count);
} catch (SQLException e) {
throw new RuntimeException(e);
}
} /*
* 查询所有用户方法
*/
@Test
public void testQueryAll1() {
try {
// 1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql = "select * from user";
// 3.执行查询操作
List<Map<String, Object>> list = qr.query(sql, new MapListHandler());
// 4.对结果集集合进行遍历
for (Map<String, Object> map : list) {
System.out.println(map);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
} /*
* 查询所有用户方法
*/
@Test
public void testQueryAll2() {
try {
// 1.获取核心类queryRunner
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql = "select * from user";
// 3.执行查询操作
List<Object> list = qr.query(sql, new ColumnListHandler("uname"));
// 4.对结果集集合进行遍历
for (Object object : list) {
System.out.println(object);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
TestDBUtils2.java --查询操作
【注意】:DBUtils在创建QueryRunner时传入dataSource对象,每次在执行完之后都会自动关闭Connection连接对象。(就相当于把conn交给dbutils管理了,他会帮我们关掉)
如果没有传入dataSource的话,需要手动关闭。