数据库连接池

数据库连接池:

  1. 数据库的连接对象创建工作,比较消耗性能。
  2. 一开始在内存中开辟一块空间(集合),一开始往池子里面放置多个连接对象。后面需要连接的话,直接从池子里面取,不需要自己去创建连接了。使用完毕,要记得归还连接。确保连接对象 能循环利用。

在这里插入图片描述

数据库连接池简单搭建:

这是一个数据库连接池,一开始先往池子里面放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();
		}
	}
}

数据库连接池代码实现中出现的问题:

  1. 需要额外记住addBack的方法

  2. 单例(不断的New)

  3. 无法面向接口编程
    UserDao dao = new UserDaoImpl();
    dao.insert();

    DataSource dataSource = new MyDataSource();
    因为接口里面没有定义addBack的方法

  4. 怎么解决?以addBack为切入点

解决自定义数据库连接池出现的问题:
由于多了一个addBack方法,所以使用这个连接池的地方,需要额外记住这个方法,并且还不能面向接口编程
我们打算修改接口中的那个close方法 原来的Connection对象的close方法,是真的关闭连接。
打算修改这个close方法,以后再调用close,并不是真的关闭,而是归还连接对象

如何扩展某一个方法?
原有的方法逻辑,不是我们想要的,想修改自己的逻辑
1.直接改源码? ------无法实现
2.继承? -------必须得知道这个接口的具体实现是谁
3.使用装饰者模式

自定义数据库连接池(装饰者模式)

例:

  1. 创建一个接口
  2. 创建一个类,继承上述接口
  3. 创建一个装饰类
  4. 测试类

在这里插入图片描述

一个接口:

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代码实现案例:

  1. 建数据库bank,其中建表account,设置主键id,name,money
  2. 在项目src下导入配置文件(dbcpconfig.properties)
  3. 测试类

在这里插入图片描述

导入配置文件:

#连接设置

    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的使用案例(重点掌握):

  1. 导入jar包
  2. 配置文件
  3. 测试类
  4. 数据库部署(在此案例中,数据库名称为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接口的匿名实现类):

  1. 数据库准备:数据库名称为bank,表为account
  2. 导入DBUtils的jar包
  3. 建一个对象类account,为数据库中的表account的属性实现其set、get方法,toString
  4. 测试类

在这里插入图片描述

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();

控制台输出:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值