数据库连接池:
- 数据库的连接对象创建工作,比较消耗性能。
- 一开始在内存中开辟一块空间(集合),一开始往池子里面放置多个连接对象。后面需要连接的话,直接从池子里面取,不需要自己去创建连接了。使用完毕,要记得归还连接。确保连接对象 能循环利用。
数据库连接池简单搭建:
这是一个数据库连接池,一开始先往池子里面放10个连接:
1.开始创建10个连接
2.来的程序通过getConnection获得连接
3.用完之后,使用addBack归还连接
4.扩容
package com.util;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
//这是一个数据库连接池,一开始先往池子里面放10个连接
//1.开始创建10个连接
//2.来的程序通过getConnection获得连接
//3.用完之后,使用addBack归还连接
//4.扩容
public class MyDataSource implements DataSource{
List<Connection> list = new ArrayList<Connection>();
public MyDataSource(){
for(int i = 0; i < 10; i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
//该连接池对外公布的获取连接的方法
@Override(覆写了父类DataSource的方法)
public Connection getConnection() throws SQLException {
//来拿连接的时候,先看看池子里面还有没有。
if(list.size() == 0){
for(int i = 0; i < 5; i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
//remove(0) ---->移除的第一个,移除的是集合中的第一个(永远是第一个)
Connection conn = list.remove(0);
return conn;
}
// 用完之后记得归还
public void addBack(Connection conn){
list.add(conn);
}
连接池简单使用:
ackage com.util;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import java.sql.PreparedStatement;
public class TestPool {
@Test
public void testPool(){
Connection conn = null;
PreparedStatement ps = null;
MyDataSource dataSource = new MyDataSource();
try {
conn = dataSource.getConnection();
String sql = "insert into account values (3,'lili',500)";
ps = conn.prepareStatement(sql);
ps.executeUpdate(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//归还连接
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
数据库连接池代码实现中出现的问题:
-
需要额外记住addBack的方法
-
单例(不断的New)
-
无法面向接口编程
UserDao dao = new UserDaoImpl();
dao.insert();DataSource dataSource = new MyDataSource();
因为接口里面没有定义addBack的方法 -
怎么解决?以addBack为切入点
解决自定义数据库连接池出现的问题:
由于多了一个addBack方法,所以使用这个连接池的地方,需要额外记住这个方法,并且还不能面向接口编程
我们打算修改接口中的那个close方法 原来的Connection对象的close方法,是真的关闭连接。
打算修改这个close方法,以后再调用close,并不是真的关闭,而是归还连接对象
如何扩展某一个方法?
原有的方法逻辑,不是我们想要的,想修改自己的逻辑
1.直接改源码? ------无法实现
2.继承? -------必须得知道这个接口的具体实现是谁
3.使用装饰者模式
自定义数据库连接池(装饰者模式)
例:
- 创建一个接口
- 创建一个类,继承上述接口
- 创建一个装饰类
- 测试类
一个接口:
package com.test;
public interface Waiter {
void service();
}
继承上述接口的类:
package com.test;
public class Waitress implements Waiter {
@Override
public void service() {
// TODO Auto-generated method stub
System.out.println("在服务...");
}
}
装饰类(也要实现上述接口):
package com.test;
public class WaitressWrap implements Waiter{
Waiter waiter = null;
public WaitressWrap(Waiter waiter){
this.waiter = waiter;
}
@Override
public void service() {
System.out.println("微笑:::");
waiter.service();
}
}
测试类:
package com.test;
public class MainTest {
public static void main(String[] args) {
WaitressWrap waitressWrap = new WaitressWrap(new Waitress());
waitressWrap.service();
}
}
运行测试类,控制台输出:
分析:
面向接口编程:
面向接口案例:
1. 新建数据库连接池工具类并properties文件:
package com.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtils {
private JDBCUtils(){};
private static Connection con;
private static String driverClass;
private static String url;
private static String username;
private static String password;
static{
try{
readConfig();
Class.forName(driverClass);
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException(ex+"数据库连接失败");
}
}
private static void readConfig() throws Exception{
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
pro.load(in);
driverClass = pro.getProperty("driverClass");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
}
public static Connection getConnection(){
return con;
}
public static void close(Connection con,Statement stat){
if(stat != null){
try{
stat.close();
}catch(SQLException ex){};
}
if(con != null){
try{
con.close();
}catch(SQLException ex){};
}
}
public static void close(Connection con,Statement stat,ResultSet rs){
if(stat != null){
try{
stat.close();
}catch(SQLException ex){};
}
if(con != null){
try{
con.close();
}catch(SQLException ex){};
}
if(rs != null){
try{
rs.close();
}catch(SQLException ex){};
}
}
}
2. 新建一个类,继承DataSource类,并覆写其方法,面向一个接口对象
package com.util;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
public class MyDataSource implements DataSource{
List<Connection> list = new ArrayList<Connection>();
public MyDataSource(){
for(int i = 0; i < 10; i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
//该连接池对外公布的获取连接的方法
@Override
public Connection getConnection() throws SQLException {
//来拿连接的时候,先看看池子里面还有没有。
if(list.size() == 0){
for(int i = 0; i < 5; i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
//remove(0) ---->移除的第一个,移除的是集合中的第一个(永远是第一个)
Connection conn = list.remove(0);
//把这个对象抛出去的时候,对这个对象进行包装(这一步就是面向接口)
Connection connection = new ConnectionWrap(conn,list);
return connection;
}
// 用完之后记得归还
public void addBack(Connection conn){
list.add(conn);
}
3. 装饰类(接口对象)
package com.util;
public class ConnectionWrap implements Connection {
Connection connection = null;
List<Connection> list;
public ConnectionWrap(Connection connection,List<Connection> list) {
super();
this.connection = connection;
this.list = list;
}
@Override
public void close() throws SQLException {
// connection.close();
System.out.println("有人来归还连接对象了,归还之前,池子里面是:"+list.size());
list.add(connection);
System.out.println("有人来归还连接对象了,归还之后....池子里面是:"+list.size());
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
4. 测试类:
package com.util;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import java.sql.PreparedStatement;
public class TestPool {
@Test
public void testPool(){
Connection conn = null;
PreparedStatement ps = null;
MyDataSource dataSource = new MyDataSource();
try {
conn = dataSource.getConnection();
String sql = "insert into account values (3,'wangli',500)";
ps = conn.prepareStatement(sql);
ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
//归还连接
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JDBCUtils.close(conn, ps);
}
}
}
数据库连接池DBCP代码实现案例:
- 建数据库bank,其中建表account,设置主键id,name,money
- 在项目src下导入配置文件(dbcpconfig.properties)
- 测试类
导入配置文件:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bank
username=root
password=1234
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
测试类:
package com.dbcp;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import com.util.JDBCUtils;
public class DBCPDemo02 {
@Test
public void testDBCP02(){
Connection conn = null;
PreparedStatement ps = null;
try {
BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties properties = new Properties();
InputStream is = new FileInputStream("src//dbcpconfig.properties");
properties.load(is);
DataSource dataSource = factory.createDataSource(properties);
conn = dataSource.getConnection();
String sql = "insert into account values(4,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "liuquan");
ps.setString(2, "2000");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtils.close(conn, ps);
}
}
}
数据库连接池C3p0的使用案例(重点掌握):
- 导入jar包
- 配置文件
- 测试类
- 数据库部署(在此案例中,数据库名称为bank,表为account)
2 配置文件:c3p0-config.xml(这个文件名字不能改,因为底层代码通过这个名字解析的)
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost/bank</property>
<property name="user">root</property>
<property name="password">1234</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 测试类
package com.c3p0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.util.JDBCUtils;
public class C3P0Demo02 {
@Test
public void testC3P0(){
Connection conn = null;
PreparedStatement ps = null;
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
conn = dataSource.getConnection();
String sql = "insert into account values(5,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "sunmeili");
ps.setInt(2, 2000);
ps.executeUpdate();
} catch (Exception e) {
System.out.println(e);
}finally{
JDBCUtils.close(conn, ps);
}
}
}
C3p0工具类(以后用到直接调用方法就行)
package com.c3p0;
import java.io.InputStream;
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 com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtils2 {
static ComboPooledDataSource dataSource =null;
static{
dataSource = new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
public static void close(Connection con,Statement stat){
if(stat != null){
try{
stat.close();
}catch(SQLException ex){};
}
if(con != null){
try{
con.close();
}catch(SQLException ex){};
}
}
public static void close(Connection con,Statement stat,ResultSet rs){
if(stat != null){
try{
stat.close();
}catch(SQLException ex){};
}
if(con != null){
try{
con.close();
}catch(SQLException ex){};
}
if(rs != null){
try{
rs.close();
}catch(SQLException ex){};
}
}
}
DBUtils的增删改功能:
一定要导入DBUtils的jar包!!!
package com.dbutils;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class TestDBUtils {
@Test
public void testInsert() throws SQLException{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
QueryRunner querry = new QueryRunner(dataSource);
//增加
querry.update("insert into account values(8,?,?)", "zhangjun",500);
//删除
querry.update("delete from account where id = ?",3);
//修改
querry.update("update account set money = ? where id = ?",400,8);
}
}
DBUtils的查询功能(直接new接口的匿名实现类):
- 数据库准备:数据库名称为bank,表为account
- 导入DBUtils的jar包
- 建一个对象类account,为数据库中的表account的属性实现其set、get方法,toString
- 测试类
package com.dbutils;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Test;
import com.domain.Account;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class TestDBUtils2 {
@Test
public void testInsert() throws SQLException{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
QueryRunner querry = new QueryRunner(dataSource);
//去执行查询,查询到的数据还是在那个result里面,然后调用下面的handle方法,由用户手动去封装
Account account = querry.query("select * from account where id = ?", new ResultSetHandler<Account>(){
@Override
public Account handle(ResultSet rs) throws SQLException {
Account account = new Account();
while(rs.next()){
String name = rs.getString("name");
int money = rs.getInt("money");
account.setName(name);
account.setMoney(money);
}
return account;
}
}, 2);
System.out.println(account.toString());
}
}
执行操作后,控制台输出:
DBUtils查询的简化写法(直接使用框架已经写好的实现类):
public class TestDBUtils2 {
@Test
public void testInsert() throws SQLException{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
QueryRunner querry = new QueryRunner(dataSource);
//单个数据查询
Account account = querry.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), 5);
System.out.println(account);
//多个数据查询
java.util.List<Account> list = querry.query("select * from account", new BeanListHandler<Account>(Account.class));
for(Account account1 : list){
System.out.println(account);
}
}
}
注释:上述代码中的(Account.class):
通过字节码得到该类的实例
例如:Account account = new Account();
Account a1 = Account.class.newInstance();
控制台输出: