目录

Java 操作数据库(五) JDBC连接池封装-C3P0

本节目标

  • 学会完整封装使用 JDBC 连接池

本示例以 MySQL 数据库作为演示库

引入驱动包

<dependencies>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.16</version>
	</dependency>
	<dependency>
		<groupId>com.mchange</groupId>
		<artifactId>c3p0</artifactId>
		<version>0.9.5.4</version>
	</dependency>
	<dependency>
		<groupId>commons-beanutils</groupId>
		<artifactId>commons-beanutils</artifactId>
		<version>1.9.2</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency>
</dependencies>

封装工具类 C3p0Util

package com.bqteam.learn.jdbc;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.beanutils.BeanUtils;

import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class C3p0Util {

	private static ComboPooledDataSource dataSource;

	public static boolean init(String driverName, String dsn, String user, String pwd) {
		if (dataSource == null) {
			dataSource = new ComboPooledDataSource();
			try {
				dataSource.setDriverClass(driverName);
			} catch (PropertyVetoException e) {
				e.printStackTrace();
				return false;
			}
			dataSource.setUser(user);
			dataSource.setPassword(pwd);
			dataSource.setJdbcUrl(dsn);

			// 设置初始连接池的大小
			dataSource.setInitialPoolSize(2);

			// 设置连接池的最小值
			dataSource.setMinPoolSize(1);

			// 设置连接池的最大值
			dataSource.setMaxPoolSize(10);

			// 设置连接池中的最大Statements数量
			dataSource.setMaxStatements(50);

			// 设置连接池的最大空闲时间
			dataSource.setMaxIdleTime(60);
		}
		return true;
	}

	/**
     * 从连接池获得数据库连接
     *
     * @return Connection
     */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
     * 关闭 Connection
     *
     * @param connection 连接池对象
     */
	private static void close(Connection connection) {
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
     * 关闭 Statement
     *
     * @param statement
     */
	private static void close(Statement statement) {
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
     * 关闭 ResultSet
     *
     * @param resultSet
     */
	private static void close(ResultSet resultSet) {
		if (resultSet != null) {
			try {
				resultSet.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
     * 关闭 Connection 以及 Statement
     *
     * @param connection
     * @param statement
     */
	private static void close(Connection connection, Statement statement) {
		close(connection);
		close(statement);
	}

	/**
     * 关闭 Connection,Statement 以及 ResultSet
     *
     * @param connection
     * @param statement
     * @param resultSet
     */
	private static void close(Connection connection, Statement statement, ResultSet resultSet) {
		close(connection, statement);
		close(resultSet);
	}

	/**
     * 设置参数
     *
     * @param preparedStatement Statement对象
     * @param param             参数列表
     * @return
     * @throws SQLException
     */
	private static boolean settingParams(PreparedStatement preparedStatement, Object[] param) throws SQLException {

		if (param != null && param.length > 0) {
			// 获取ParameterMetaData
			ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
			// 获得SQL中占位符个数
			int paramCount = parameterMetaData.getParameterCount();

			// 占位符个数与参数个数不一致,返回false表示出错
			if (paramCount != param.length) {
				return false;
			}
			// 设置对应的参数信息
			for (int i = 0; i < paramCount; i++) {
				preparedStatement.setObject(i + 1, param[i]);
			}
		}
		return true;
	}

	/**
     * 更新操作
     *
     * @param sql   执行的 SQL 语句
     * @param param 对应的参数列表
     * @return true 更新成功, false 更新失败
     */
	public static boolean update(String sql, Object[] param) {

		PreparedStatement preparedStatement = null;
		Connection connection = getConnection();
		if (connection == null) {
			return false;
		}
		try {
			preparedStatement = connection.prepareStatement(sql);

			if (!settingParams(preparedStatement, param)) {
				return false;
			}

			int result = preparedStatement.executeUpdate();
			return result > 0;

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(connection, preparedStatement);
		}
		return false;
	}

	/**
     * 获取单条数据
     *
     * @param sql   执行 SQL 语句
     * @param param 对应的参数列表
     * @param clazz 所要获取的对象的类型
     * @param <T>   对象的类型
     * @return
     */
	public static <T> T queryOne(String sql, Object[] param, Class<T> clazz) {

		Connection connection = getConnection();
		if (connection == null) {
			return null;
		}
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {

			preparedStatement = connection.prepareStatement(sql);

			if (!settingParams(preparedStatement, param)) {
				return null;
			}

			resultSet = preparedStatement.executeQuery();
			if (resultSet == null) {
				return null;
			}

			if (resultSet.next()) {
				// 利用反射机制创建对象
				T data = clazz.newInstance();
				// 获得 ResultSetMetaData
				ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
				// 获得列的数量
				int columnCount = resultSetMetaData.getColumnCount();
				for (int i = 0; i < columnCount; i++) {
					// 获得对应的列的名称
					String name = resultSetMetaData.getColumnName(i + 1);
					// 获得对应的列的值
					Object rsData = resultSet.getObject(name);
					// 使用 BeanUtils 工具对属性进行注入
					BeanUtils.copyProperty(data, name, rsData);
				}
				return data;

			} else {
				return null;
			}

		} catch (InstantiationException | SQLException | IllegalAccessException | InvocationTargetException e) {
			e.printStackTrace();
		} finally {
			close(connection, preparedStatement, resultSet);
		}
		return null;
	}

	/**
     * 获取 Bean 并且封装成 List
     *
     * @param sql   执行 SQL 语句
     * @param param 对应的参数列表
     * @param clazz 所要获取的对象的类型
     * @param <T>   对象的类型
     * @return list
     */
	public static <T> List<T> queryList(String sql, Object[] param, Class<T> clazz) {

		Connection connection = getConnection();
		if (connection == null) {
			return null;
		}
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {

			preparedStatement = connection.prepareStatement(sql);

			if (!settingParams(preparedStatement, param)) {
				return null;
			}

			resultSet = preparedStatement.executeQuery();
			if (resultSet == null) {
				return null;
			}
			List<T> results = new ArrayList<>();

			while (resultSet.next()) {
				// 创建对象
				T data = clazz.newInstance();
				// 获得ResultSetMetaData
				ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
				// 获得列的数量
				int columnCount = resultSetMetaData.getColumnCount();
				for (int i = 0; i < columnCount; i++) {
					// 获得对应的列的名称
					String name = resultSetMetaData.getColumnName(i + 1);
					// 获得对应的列的值
					Object rData = resultSet.getObject(name);
					// 使用BeanUtils工具对属性进行注入
					BeanUtils.copyProperty(data, name, rData);
				}
				results.add(data);

			}
			return results;

		} catch (InstantiationException | SQLException | IllegalAccessException | InvocationTargetException e) {
			e.printStackTrace();
		} finally {
			close(connection, preparedStatement, resultSet);
		}
		return null;
	}
}

使用 JUint 进行测试

package com.bqteam.learn.jdbc;

import com.bqteam.learn.bean.LphHomework;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertNotNull;

public class TestC3p0Util {
	@BeforeClass
	public static void init() {
		String driverName = "com.mysql.cj.jdbc.Driver";
		String dsn = "jdbc:mysql://host:port/database?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false";
		String user = "user";
		String pwd = "password";
		C3p0Util.init(driverName, dsn, user, pwd);
	}

	@Test
	public void testQueryList() {
		final String SQL = "select * from lph_homework limit ?";
		Object[] param = {10};
		List<LphHomework> list = C3p0Util.queryList(SQL, param, LphHomework.class);
		assertNotNull(list);
		for (LphHomework item : list) {
			System.out.printf("作业ID为 %d,名称为 %s,教学组ID为 %d\n", item.getId(), item.getName(), item.getMath_cls_group_id());
		}
	}
}

测试输出结果如下

作业ID为 92名称为 作业12-3-20171128教学组ID为 2
作业ID为 41名称为 作业7-2-20171110教学组ID为 1
作业ID为 47名称为 作业8-1-20171110教学组ID为 1
作业ID为 164名称为 作业16-3-20180313教学组ID为 6
作业ID为 104名称为 作业13-2-20180307教学组ID为 5
作业ID为 147名称为 作业14-1-20180312教学组ID为 5
作业ID为 53名称为 作业8-3-20171110教学组ID为 1
作业ID为 9名称为 作业3-3-20171031教学组ID为 3
作业ID为 15名称为 作业4-1-20171031教学组ID为 2
作业ID为 120名称为 作业19-2-20180312教学组ID为 4