数据库连接池

JDBC概述:
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。

Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。

数据库连接池

实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池。

原理:一开始先在内存中开辟一块空间,在池中存放多个连接对象,后面需要连接的话,直接从池中取,不需要自己创建,用完记得归还(调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池),以确保连接对象能循环使用。

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

先来熟悉一下JDBC吧!

JDBC开发步骤

  1. 注册驱动.
    导入jar包,此处以mysql为例:
    在这里插入图片描述
    JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver
    DriverManager工具类,提供注册驱动的方法 registerDriver(),方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册:
    DriverManager.registerDriver(new com.mysql.jdbc.Driver());
    以上代码不推荐使用,存在两方面不足
    1).硬编码,后期不易于程序扩展和维护
    2.)驱动被注册两次。
    通常开发我们使用Class.forName() 加载一个使用字符串描述的驱动类。
    如果使用Class.forName()将类加载到内存,该类的静态代码将自动执行。
    通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
	static {
		try {
			java.sql.DriverManager.registerDriver(new Driver());
		} catch (SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	      }
……
}
  1. 获得连接.
    获取连接需要方法 DriverManager.getConnection(url,username,password),三个参数分别表示,url 需要连接数据库的位置(网址) user用户名 password 密码
    url比较复杂,下面是mysql的url:
    jdbc:mysql://localhost:3306/mydb
    JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
     第一部分是jdbc,这是固定的;
     第二部分是数据库名称,那么连接mysql数据库,第二部分当然是mysql了;
     第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号(3306),以及DATABASE名称(mydb)组成。

  2. 获得语句执行平台
    String sql = “某SQL语句”;
    获取Statement语句执行平台:Statement stmt = con.createStatement();

  3. 执行sql语句
    常用方法:
     int executeUpdate(String sql); --执行insert update delete语句.
     ResultSet executeQuery(String sql); --执行select语句.
     boolean execute(String sql); --执行select返回true 执行其他的语句返回false.

  4. 处理结果

    执行insert、update、delete,返回int,无需处理

    ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
    rs.next();//指向第一行
    rs.getInt(1);//获取第一行第一列的数据
    常用方法:
     Object getObject(int index) / Object getObject(String name) 获得任意对象
     String getString(int index) / Object getObject(String name) 获得字符串
     int getInt(int index) / Object getObject(String name) 获得整形
     double getDouble(int index) / Object getObject(String name) 获得双精度浮点型

  5. 释放资源
    与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
    rs.close();
    stmt.close();
    con.close();

上代码:

package com.util;

/*
 *  实现JDBC的工具类
 *  定义方法,直接返回数据库的连接对象
 *  
 *  写关闭方法
 */

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCUtils {
	private JDBCUtils(){}
	private static Connection con ;
	
	static{
		try{
			Class.forName("com.mysql.jdbc.Driver");
			String url = "jdbc:mysql://localhost:3306/my";
			String username="root";
			String password="root";
			con = DriverManager.getConnection(url, username, password);
		}catch(Exception ex){
			throw new RuntimeException(ex+"数据库连接失败");//抛这个异常时代码不往下执行
		}
	}
	
	/*
	 * 定义静态方法,返回数据库的连接对象
	 */
	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(rs!=null){
			 try{
				 rs.close();
			 }catch(SQLException ex){}
		 }
		 
		 if(stat!=null){
			 try{
				 stat.close();
			 }catch(SQLException ex){}
		 }
		 
		 if(con!=null){
			 try{
				 con.close();
			 }catch(SQLException ex){}
		 }
		 
	}
}

实际开发中一般会把数据库的配置写在配置文件中,方便后期维护修改,
以database.properties为例,

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/my
username=root
password=root

读database.properties文件:

package com.properties;

import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

/*
 *  加载properties配置文件
 *  IO读取文件,键值对存储到集合
 *  从集合中以键值对方式获取数据库的连接信息,完成数据库的连接
 */
public class PropertiesDemo {
	public static void main(String[] args) throws Exception{
		/*FileInputStream fis = new FileInputStream("src/database.properties");
		System.out.println(fis);*/
		//发布给用户的是bin文件,所以会报空指针异常
		//所以
		//使用类的加载器
		InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("database.properties");
		//ClassLoader getClassLoader() 返回类的类加载器。  
		//InputStream getResourceAsStream(String name) 返回用于读取指定资源的输入流。  
		System.out.println(in);
		Properties pro = new Properties();
		//导入输入流。
		pro.load(in);
		//获取集合中的键值对
		String driverClass=pro.getProperty("driverClass");
		String url = pro.getProperty("url");
		String username = pro.getProperty("username");
		String password = pro.getProperty("password");
		Class.forName(driverClass);
		Connection con = DriverManager.getConnection(url, username, password);
		System.out.println(con);
		
	}
}

写一个工具类就是:

package com.util;


import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

/*
 *  编写数据库连接的工具类,JDBC工具类
 *  获取连接对象采用读取配置文件方式
 *  读取文件获取连接,执行一次,static{}
 */
public class JDBCUtilsConfig {
	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("数据库连接失败");
		}
	}
	
	private static void readConfig()throws Exception{
		InputStream in = JDBCUtilsConfig.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;
	}
	
}

insertdemo:

package com.mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
JDBC操作数据库的步骤
1.注册驱动
    告知JVM使用的是哪一个数据库的驱动
2.获得连接
   使用JDBC中的类,完成对MySQL数据库的连接
3.获得语句执行平台
  通过连接对象获取对SQL语句的执行者对象
4.执行sql语句
  使用执行者对象,向数据库执行SQL语句
  获取到数据库的执行后的结果
5.处理结果
6.释放资源  一堆close()
*/
public class InsertTest {
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		//1.注册驱动 反射技术,将驱动类加入到内存
		Class.forName("com.mysql.jdbc.Driver");
		//2.获得数据库连接  DriverManager类中静态方法
		//static Connection getConnection(String url, String user, String password) 
		String url = "jdbc:mysql://localhost:3306/my";
		String username="root";
		String password="root";
		Connection con = DriverManager.getConnection(url, username, password);
		//3.获得语句执行平台, 通过数据库连接对象,获取到SQL语句的执行者对象
		// con对象调用方法   Statement createStatement() 获取Statement对象,将SQL语句发送到数据库
		Statement stat = con.createStatement();
		//	4.执行sql语句
		// 通过执行者对象调用方法执行SQL语句,获取结果
		int row = stat.executeUpdate
				("INSERT INTO jackson(username,password) VALUES('momo','123')");
		System.out.println(row);
		
		//6.释放资源  一堆close()
		stat.close();
		con.close();
	}
	
}

selectdemo:

package com.mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class SelectTest {
	public static void main(String[] args) throws Exception {
		//1. 注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		//2. 获取连接对象
		String url = "jdbc:mysql://localhost:3306/my";
		String username="root";
		String password="root";
		Connection con = DriverManager.getConnection(url, username, password);
		//3 .获取执行SQL 语句对象
		Statement stat = con.createStatement();
		// 拼写查询的SQL
		String sql = "SELECT * FROM jackson";
		//4. 调用执行者对象方法,执行SQL语句获取结果集
		// ResultSet executeQuery(String sql)  执行SQL语句中的select查询
		// 返回值ResultSet接口的实现类对象,实现类在mysql驱动中
		ResultSet rs = stat.executeQuery(sql);
		//5 .处理结果集
		// ResultSet接口方法 boolean next() 返回true,有结果集,返回false没有结果集
		while(rs.next()){
			//获取每列数据,使用是ResultSet接口的方法 getXX方法参数中,建议写String列名
			System.out.println(rs.getString("username")+"   "+rs.getString("password")+
					"   "+rs.getString("sex")+"   "+rs.getString("age"));
		}
		rs.close();
		stat.close();
		con.close();
	}

}

如果用Statement ,会存在注入攻击的情况,大概意思就是防止有的人登录时输入密码后,加上"or" 1=1,这样就能查出所有结果集啦,这时就有了子接口PreparedStatement预编译sql,参数用?(占位符)
上代码:

package com.mysql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

/*
 *  Java程序实现用户登录,用户名和密码,数据库检查
 *  防止注入攻击
 *  Statement接口实现类,作用执行SQL语句,返回结果集
 *  有一个子接口PreparedStatement  (SQL预编译存储,多次高效的执行SQL) 
 *  PreparedStatement的实现类数据库的驱动中,如何获取接口的实现类
 *  
 *  是Connection数据库连接对象的方法
 *  PreparedStatement prepareStatement(String sql) 
          
 */
public class PrepareStatementTest {
	public static void main(String[] args) throws Exception {
		Class.forName("com.mysql.jdbc.Driver");
		String url = "jdbc:mysql://localhost:3306/my";
		String username = "root";
		String password = "root";
		Connection con = DriverManager.getConnection(url, username, password);
		Scanner sc = new Scanner(System.in);
		String user = sc.nextLine();
		String pass = sc.nextLine();
		
		//执行SQL语句,数据表,查询用户名和密码,如果存在,登录成功,不存在登录失败
		String sql = "SELECT * FROM jackson WHERE username=? AND PASSWORD=?";
		//调用Connection接口的方法prepareStatement,获取PrepareStatement接口的实现类
		//方法中参数,SQL语句中的参数全部采用问号占位符
		PreparedStatement pst =  con.prepareStatement(sql);
		System.out.println(pst);
		//调用pst对象set方法,设置问号占位符上的参数
		pst.setObject(1, user);
		pst.setObject(2, pass);
		
		//调用方法,执行SQL,获取结果集
		ResultSet rs = pst.executeQuery();
		while(rs.next()){
			System.out.println(rs.getString("username")+"   "+rs.getString("password"));
		}
		
		rs.close();
		pst.close();
		con.close();
	}
}

啰嗦了一大堆,赶紧步入正题啦,不要忘记本文名称数据库连接池

先搭建一个简单的数据库连接池熟悉一下概念:
连接池工具类要实现DataSource接口,

package com.connectionpool;

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;

import com.util.JDBCUtils;

public class MyDataSource implements DataSource{
//一开始先往池子里面放10个连接	
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 {
		// TODO Auto-generated method stub
		//来拿连接的时候,先看看,池子里面还有没有,没有要扩容。
		if(list.size() == 0 ){
			for (int i = 0; i < 5; i++) {
				Connection conn = JDBCUtils.getConnection();
				list.add(conn);
			}
		}
		
		Connection conn = list.remove(0);
		return conn;
	}
	
	//用完之后,记得归还
	public void addBack(Connection conn){
		list.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;
	}

}

常见的数据库连接池:
dbcp、c3p0

dbcp:

第一步:导包
除了之前的mysql-connector-java-5.1.37-bin.jar
另导入
在这里插入图片描述
第二步:创建DataSource接口的实现类对象
实现类, org.apache.commons.dbcp

BasicDataSource dataSource = new BasicDataSource();

第三步:设置数据库连接基本信息,通过对象方法setXXX设置

dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/my");
dataSource.setUsername("root");
dataSource.setPassword("root");

第四步:获得连接

Connection con = dataSource.getConnection();

一个简单的dbcp连接池就出来了:

package com.dbcp;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.dbcp.BasicDataSource;

import com.util.JDBCUtils;

/*
 *  连接池jar包中,定义好一个类 BasicDataSource
 *  实现类数据源的规范接口 javax.sql.DataSource
 */
public class DataSoruceDemo {
	public static void main(String[] args) {
		Connection con = null;
		PreparedStatement  pst = null;
		
		//1.创建DataSource接口的实现类对象
		//实现类, org.apache.commons.dbcp
		BasicDataSource dataSource = new BasicDataSource();
		//2.连接数据库的4个最基本信息,通过对象方法setXXX设置进来
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://localhost:3306/my");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		
		try{
		//3.调用对象方法getConnection获取数据库的连接
		    con = dataSource.getConnection();
			System.out.println(con);
		}catch(SQLException ex){
//			System.out.println(ex);
			throw new RuntimeException("数据库连接失败");
		}finally{
			//BasicDataSource做了处理,这里的关闭是把连接还回去
			JDBCUtils.close(con, pst);
		}
	}
}

补充下通过读配置文件获取连接的,这里需要BasicDataSourceFactory类的createDataSource(Properties properties)方法:
dbcpconfig.properties:

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/my
username=root
password=root

#<!-- 初始化连接 -->
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.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

import com.util.JDBCUtils;

public class DbcpByReadConfig {
	
	@Test
	public void testDBCP(){
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			BasicDataSourceFactory factory = new BasicDataSourceFactory();
			Properties properties = new Properties();
			InputStream is = getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
			properties.load(is);
			DataSource dataSource = factory.createDataSource(properties);
			
			conn = dataSource.getConnection();
			String sql = "insert into jackson(username,password) values( ? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "yyqx");
			ps.setString(2, "123456");
			
			ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.close(conn, ps);
		}
		
	}

}

c3po:
c3p0更常用
首先导包:
在这里插入图片描述

上代码,先来不读配置文件的:

package com.c3p0;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.util.JDBCUtil;

public class C3P0Demo {
	@Test
	public void testC3P0 (){
		
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1. 创建datasource
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			
			//2. 设置连接数据的信息
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			dataSource.setJdbcUrl("jdbc:mysql://localhost/mydb");
			dataSource.setUser("root");
			dataSource.setPassword("root");
			
			//2. 得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into user(uid,username,password) values(?,? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "001");
			ps.setString(2, "admi234n");
			ps.setString(3, "123");
			
			ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps);
		}
	}
}

再来通过读配置文件的:
这里需要特别注意配置文件名字只能叫c3p0-config.xml
因为ComboPooledDataSource类内部去扫这个文件了

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<!-- default-config 默认的配置,  -->
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost/mydb</property>
    <property name="user">root</property>
    <property name="password">root</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>
  
   <!-- This app is massive! -->
  <named-config name="oracle"> 
    <property name="acquireIncrement">50</property>
    <property name="initialPoolSize">100</property>
    <property name="minPoolSize">50</property>
    <property name="maxPoolSize">1000</property>

    <!-- intergalactoApp adopts a different approach to configuring statement caching -->
    <property name="maxStatements">0</property> 
    <property name="maxStatementsPerConnection">5</property>

    <!-- he's important, but there's only one of him -->
    <user-overrides user="master-of-the-universe"> 
      <property name="acquireIncrement">1</property>
      <property name="initialPoolSize">1</property>
      <property name="minPoolSize">1</property>
      <property name="maxPoolSize">5</property>
      <property name="maxStatementsPerConnection">50</property>
    </user-overrides>
  </named-config>

 
</c3p0-config>
	

默认的是进行mysql配置,也可指定oracle等数据库的配置

package com.c3p0;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.util.JDBCUtil;

public class C3P0ByConfig {
	
	@Test
	public void testC3P0(){
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			
			//默认会找 xml 中的 default-config 分支。
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			//若需要oracle,则
			//ComboPooledDataSource dataSourceora = new ComboPooledDataSource("oracle");
			
			//2. 得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into user(uid,username,password) values(?,? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "002");
			ps.setString(2, "my");
			ps.setString(3, "123345");
			
			ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps);
		}
	}

}

整合成一个C3P0Util:(不要忘记c3p0-config.xml),后期用c3p0连接池需要这两就行

package com.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
	
	private static DataSource dataSource = new ComboPooledDataSource();

	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

	// 直接可以获取一个连接池
	public static DataSource getDataSource() {
		return dataSource;
	}

	// 获取连接对象
	public static Connection getConnection() throws SQLException {

		Connection con = tl.get();
		if (con == null) {
			con = dataSource.getConnection();
			tl.set(con);
		}
		return con;
	}

	// 开启事务
	public static void startTransaction() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.setAutoCommit(false);
		}
	}

	// 事务回滚
	public static void rollback() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.rollback();
		}
	}

	// 提交并且 关闭资源及从ThreadLocall中释放
	public static void commitAndRelease() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.commit(); // 事务提交
			con.close();// 关闭资源
			tl.remove();// 从线程绑定中移除
		}
	}

	// 关闭资源方法
	public static void closeConnection() throws SQLException {
		Connection con = getConnection();
		if (con != null) {
			con.close();
		}
	}

	public static void closeStatement(Statement st) throws SQLException {
		if (st != null) {
			st.close();
		}
	}

	public static void closeResultSet(ResultSet rs) throws SQLException {
		if (rs != null) {
			rs.close();
		}
	}

}

下面补充Hikari连接池,有缘后期来详讲!
导包:
在这里插入图片描述

package com.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class HikariUtil{
	
	private static HikariDataSource datasource;
	public static synchronized Connection getCon() throws SQLException{
		if(datasource == null){
		HikariConfig config = new HikariConfig();
//		config.setDriverClassName("oracle.jdbc.driver.OracleDriver");
//		config.setJdbcUrl("jdbc:oracle:thin:@ip:port:orcl");
//		config.setUsername("username");
//		config.setPassword("password");
		config.setDriverClassName("com.mysql.jdbc.Driver");
		config.setJdbcUrl("jdbc:mysql://localhost/mydb");
		config.setUsername("root");
		config.setPassword("root");
		config.setMaximumPoolSize(100);
		config.addDataSourceProperty("cachePrepStmts", "true");
		config.addDataSourceProperty("prepStmtCacheSize", "250");
		config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
		config.setConnectionTimeout(20*1000);
		datasource = new HikariDataSource(config);
		}
		return datasource.getConnection();
	}
	public static void close(Connection con,PreparedStatement pst){
		
		try {
			if(con!=null){
				con.close();
			}
			if(pst!=null){
				pst.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


}

遇到的坑:

在这里插入图片描述
大概意思就是mysql驱动版本低了!
解决:换版本
其实这里就是想记录下下载驱动的步骤emmmmm
mysql官网:https://www.mysql.com/
在这里插入图片描述
选择Downloads ,往下滑找到:
在这里插入图片描述
接下来找到下图打开:
在这里插入图片描述
Select Operating System:
在这里插入图片描述
点download后选择下图就可以了
在这里插入图片描述

又有坑:
在这里插入图片描述

解决:url换成

config.setJdbcUrl("jdbc:mysql://localhost/mydb?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");

数据库连接池只是获取了数据库的连接操作,并不对从数据库中取到的数据进行处理,下面介绍DBUtils来对返回的数据进行处理

DBUtils核心类:
QueryRunner:提供对sql语句操作的API.
ResultSetHandler接口:用于定义select操作后,怎样封装结果集.
增删改都用update(),查询用query()

QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); 
//增删改
	public static void update(){
		String sql = "INSERT INTO sort (sname,sprice,sdesc)VALUES(?,?,?)";
		Object[] params = {"水果",100.12,"刚刚上市的核桃"};
		try{
			int row = qr.update(sql, params);
			System.out.println(row);
		}catch(SQLException ex){
			throw new RuntimeException("数据添加失败");
		}
	}
//查询,ResultSetHandler为接口,直接new接口的匿名实现类
	public static void select() throws SQLException{
		Sort  sort =  qr.query("select * from sort where sid = ?", new ResultSetHandler<Sort>(){

			@Override
			public Sort handle(ResultSet rs) throws SQLException {
				Sort sort  =  new Sort();
				while(rs.next()){
					String sname = rs.getString("sname");
					Double sprice = rs.getDouble("sprice");
					String sdesc = rs.getString("sdesc");
					
					sort.setSname(sname);
					sort.setSprice(sprice);
					sort.setSdesc(sdesc);
				}
				return sort;
			}
			 
		 }, 6);
		System.out.println(sort.toString());
	}

以上是直接new接口的匿名实现类,也可new接口的实现类
常见的ResultSetHandler接口实现类:

实现类描述
BeanHandler将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
ArrayHandler将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。
MapHandler将结果集第一行封装到Map集合中,Key 列名, Value 该列数据
MapListHandler将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合
ScalarHandler它是用于单数据。例如select count(*) from 表操作。
ColumnListHandler将结果集中指定的列的字段值,封装到一个List集合中

上代码:

package com.dbutils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
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.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com.entity.Sort;
import com.util.C3P0Utils;
import com.util.JDBCUtilsConfig;

/*
 * QueryRunner数据查询操作:
 *   调用QueryRunner类方法query(Connection con,String sql,ResultSetHandler r, Object..params)
 *   ResultSetHandler r 结果集的处理方式,传递ResultSetHandler接口实现类
 *   Object..params SQL语句中的?占位符
 *   
 *   注意: query方法返回值,返回的是T 泛型, 具体返回值类型,跟随结果集处理方式变化
 */
public class QueryRunnerSelectDemo {
	private static Connection con = C3P0Utils.getConnection();
	
	public static void main(String[] args) throws SQLException{
//		arrayHandler();
//		arrayListHandler();
//		beanHandler();
		beanListHander();
//		columnListHandler();
//		scalarHandler();
//		mapHandler();
//		mapListHandler();
	}
	/*
	 *  结果集第八种处理方法,MapListHandler
	 *  将结果集每一行存储到Map集合,键:列名,值:数据
	 *  Map集合过多,存储到List集合
	 */
	public static void mapListHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT  * FROM sort";
		//调用方法query,传递结果集实现类MapListHandler
		//返回值List集合, 存储的是Map集合
		List<Map<String,Object>> list = qr.query(con, sql, new MapListHandler());
		//遍历集合list
		for( Map<String,Object> map : list ){
			for(String key : map.keySet()){
				System.out.print(key+"..."+map.get(key));
			}
			System.out.println();
		}
		
	}
	
	/*
	 *  结果集第七种处理方法,MapHandler
	 *  将结果集第一行数据,封装到Map集合中
	 *  Map<键,值> 键:列名  值:这列的数据
	 */
	public static void mapHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT  * FROM sort";
		//调用方法query,传递结果集实现类MapHandler
		//返回值: Map集合,Map接口实现类, 泛型
		Map<String,Object> map = qr.query(con, sql, new MapHandler());
		//遍历Map集合
		for(String key : map.keySet()){
			System.out.println(key+".."+map.get(key));
		}
	}
	
	
	/*
	 *  结果集第六种处理方法,ScalarHandler
	 *  对于查询后,只有1个结果(一行一列,聚合函数)
	 */
	public static void scalarHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT COUNT(*) FROM sort";
		//调用方法query,传递结果集处理实现类ScalarHandler
		long count = qr.query(con, sql, new ScalarHandler<Long>());
		System.out.println(count);
	}
	
	/*
	 *  结果集第五种处理方法,ColumnListHandler
	 *  结果集,指定列的数据,存储到List集合
	 *  List<Object> 每个列数据类型不同
	 */
	public static void columnListHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT * FROM sort ";		
		//调用方法 query,传递结果集实现类ColumnListHandler
		//实现类构造方法中,使用字符串的列名
		List<Object> list = qr.query(con, sql, new ColumnListHandler<Object>("sname"));
		for(Object obj : list){
			System.out.println(obj);
		}
	}
	
	/*
	 *  结果集第四种处理方法, BeanListHandler
	 *  结果集每一行数据,封装JavaBean对象
	 *  多个JavaBean对象,存储到List集合
	 */
	public static void beanListHander()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT * FROM sort ";
		//调用方法query,传递结果集处理实现类BeanListHandler
		List<Sort> list = qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
		for(Sort s : list){
			System.out.println(s);
		}
	}
	
	/*
	 *  结果集第三种处理方法,BeanHandler
	 *  将结果集的第一行数据,封装成JavaBean对象
	 *  注意: 被封装成数据到JavaBean对象, Sort类必须有空参数构造
	 */
	public static void beanHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT * FROM sort ";
		//调用方法,传递结果集实现类BeanHandler
		//BeanHandler(Class<T> type) 
		Sort s = qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
		System.out.println(s);
	}
	
	/*
	 *  结果集第二种处理方法,ArrayListHandler
	 *  将结果集的每一行,封装到对象数组中, 出现很多对象数组
	 *  对象数组存储到List集合
	 */
	public static void arrayListHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT * FROM sort";		
		//调用query方法,结果集处理的参数上,传递实现类ArrayListHandler
		//方法返回值 每行是一个对象数组,存储到List
		List<Object[]> result=  qr.query(con, sql, new ArrayListHandler());
		
		//集合的遍历
		for( Object[] objs  : result){
			//遍历对象数组
			for(Object obj : objs){
				System.out.print(obj+"  ");
			}
			System.out.println();
		}
	}
	
	/*
	 *  结果集第一种处理方法, ArrayHandler
	 *  将结果集的第一行存储到对象数组中  Object[]
	 */
	public static void arrayHandler()throws SQLException{
		QueryRunner qr = new QueryRunner();
		String sql = "SELECT * FROM sort";
		//调用方法query执行查询,传递连接对象,SQL语句,结果集处理方式的实现类
		//返回对象数组
		Object[] result = qr.query(con, sql, new ArrayHandler());
		for(Object obj : result){
			System.out.print(obj);
		}
	}
	
	
	
	
}

补充通用的CRUD方法:

package com.commoncrud;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.Test;

import com.entity.Sort;
import com.util.C3P0Utils;
import com.util.JDBCUtils;

public class CommonCRUDUtils {
	
	@Test
	public void testUpdate(){
		//update("update sort set sprice = ? where sid = ?" , 999 ,1);
		//update02("update sort set sprice = ? where sid = ?" , 999 ,2);
	}
	
	//这种update是根据参数个数
	public void update(String sql ,Object ... args) {
		Connection conn = null;
		PreparedStatement ps=null;
		
		try {
			conn = C3P0Utils.getConnection();
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				//因为不知道是什么类型的数据,所以都使用setObject来对待。
				ps.setObject(i+1, args[i]);
			}
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.close(ps, conn);
		}
	}
	
	//这种update是根据问号个数
	public void update02(String sql ,Object ... args) {
		Connection conn = null;
		PreparedStatement ps=null;
		try {
			conn = C3P0Utils.getConnection();
			ps = conn.prepareStatement(sql);
			//元数据
			/*数据库元数据  DatabaseMetaData
			参数元数据  ParameterMetaData
			结果集元数据  ResultSetMetaData*/
			//获取到有几个问号,占位符
			ParameterMetaData metaData  = ps.getParameterMetaData();
			int count = metaData.getParameterCount();
			
			for (int i = 0; i < count; i++) {
				//因为不知道是什么类型的数据,所以都使用setObject来对待。
				ps.setObject(i+1, args[i]);
			}
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.close(ps, conn);
		}
	}
	
	
	@Test
	public void testQuery(){
		Sort sort = query("select * from sort where sid = ?" ,new ResultSetHandler<Sort>(){

			@Override
			public Sort handle(ResultSet rs) {
				// TODO Auto-generated method stub
				try {
					Sort sort  = new Sort();
					if(rs.next()){
						String sname = rs.getString("sname");
						int sprice = rs.getInt("sprice");
						
						sort.setSname(sname);
						sort.setSprice(sprice);
					}
					return sort;
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
				return null;
			}
			
		} , 3);
		
		System.out.println(sort);
	}
	
	public <T> T query(String sql , ResultSetHandler<T> handler, Object ...args ){
		Connection conn = null;
		PreparedStatement ps=null;
		try {
			conn = C3P0Utils.getConnection();
			ps = conn.prepareStatement(sql);
			//获取到有几个问号,占位符
			ParameterMetaData metaData  = ps.getParameterMetaData();
			int count = metaData.getParameterCount();
			for (int i = 0; i < count; i++) {
				//因为不知道是什么类型的数据,所以都使用setObject来对待。
				ps.setObject(i+1, args[i]);
			}
			
			//执行查询工作, 然后得到结果集
			ResultSet rs = ps.executeQuery();
			
			//把结果集丢给调用者,让它去封装数据 ,返回封装数据
			T t =  (T) handler.handle(rs);
			 
			return t;
			//问题一: 这里的数据获取,以及封装成上面对象返回。不知道。 因为调用的地方需要的数据不同。
			/*while(rs.next()){
				rs.getInt("id");
				rs.getString("name");
//				。。。
			}*/
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.close(ps, conn);
		}
		return null;
	}

}

其中ResultSetHandler接口是自定义接口,

package com.commoncrud;

import java.sql.ResultSet;

public interface ResultSetHandler<T> {
	
	T handle(ResultSet rs);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值