|
|
|
@ -3,61 +3,85 @@ package cn.edu.hust.jdbc;
|
|
|
|
|
import cn.edu.hust.conf.ConfigurationManager;
|
|
|
|
|
import cn.edu.hust.constant.Constants;
|
|
|
|
|
|
|
|
|
|
import java.sql.*;
|
|
|
|
|
import java.sql.Connection;
|
|
|
|
|
import java.sql.DriverManager;
|
|
|
|
|
import java.sql.PreparedStatement;
|
|
|
|
|
import java.sql.ResultSet;
|
|
|
|
|
import java.sql.SQLException;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.concurrent.ArrayBlockingQueue;
|
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
|
|
|
|
|
|
// JDBCHelper类是一个用于管理数据库连接以及执行数据库相关操作的工具类,
|
|
|
|
|
// 它结合了单例模式和连接池的思想,方便在整个应用程序中统一获取数据库连接并执行SQL语句等操作。
|
|
|
|
|
public class JDBCHelper {
|
|
|
|
|
private static JDBCHelper instance=new JDBCHelper();
|
|
|
|
|
//使用阻塞队列
|
|
|
|
|
private LinkedBlockingQueue<Connection> queue=new LinkedBlockingQueue<Connection>();
|
|
|
|
|
static{
|
|
|
|
|
|
|
|
|
|
// 采用单例模式,保证整个应用程序中只有一个JDBCHelper实例存在,instance为该类的唯一实例对象。
|
|
|
|
|
private static JDBCHelper instance = new JDBCHelper();
|
|
|
|
|
|
|
|
|
|
// 使用LinkedBlockingQueue作为阻塞队列来存放数据库连接对象,实现连接池的功能。
|
|
|
|
|
// 连接池中的连接可以被多个地方获取并使用,使用完后再归还到队列中供后续使用。
|
|
|
|
|
private LinkedBlockingQueue<Connection> queue = new LinkedBlockingQueue<Connection>();
|
|
|
|
|
|
|
|
|
|
// 静态代码块,在类加载时执行,用于加载数据库驱动。
|
|
|
|
|
// 通过从配置管理器(ConfigurationManager)中获取数据库驱动的配置信息(Constants.JDBC_DRIVER),
|
|
|
|
|
// 调用Class.forName方法加载驱动,这样后续才能通过DriverManager建立数据库连接。
|
|
|
|
|
static {
|
|
|
|
|
try {
|
|
|
|
|
Class.forName(ConfigurationManager.getProperty(Constants.JDBC_DRIVER));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 在构造函数创建数据库连接池
|
|
|
|
|
* 结合单例模式,确保数据库连接池单例
|
|
|
|
|
* 私有构造函数,遵循单例模式的设计原则,防止外部直接通过构造函数创建多个实例。
|
|
|
|
|
* 在构造函数内部,创建数据库连接池,根据配置文件中获取的数据源大小(Constants.JDBC_ACTIVE),
|
|
|
|
|
* 循环创建指定数量的数据库连接,并将这些连接放入到阻塞队列(queue)中。
|
|
|
|
|
* 每个连接通过DriverManager.getConnection方法创建,使用从配置管理器获取的数据库连接URL、用户名和密码等信息。
|
|
|
|
|
*/
|
|
|
|
|
private JDBCHelper(){
|
|
|
|
|
int dataSourceSize=ConfigurationManager.getInteger(Constants.JDBC_ACTIVE);
|
|
|
|
|
String url=ConfigurationManager.getProperty(Constants.JDBC_URL);
|
|
|
|
|
String username=ConfigurationManager.getProperty(Constants.JDBC_USERNAME);
|
|
|
|
|
String passward=ConfigurationManager.getProperty(Constants.JDBC_PSSWORD);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
for(int i=0;i<dataSourceSize;i++)
|
|
|
|
|
{
|
|
|
|
|
Connection connection=DriverManager.getConnection(url,username,passward);
|
|
|
|
|
private JDBCHelper() {
|
|
|
|
|
// 获取配置文件中定义的数据源大小,即要创建的数据库连接数量
|
|
|
|
|
int dataSourceSize = ConfigurationManager.getInteger(Constants.JDBC_ACTIVE);
|
|
|
|
|
|
|
|
|
|
// 获取配置文件中定义的数据库连接URL
|
|
|
|
|
String url = ConfigurationManager.getProperty(Constants.JDBC_URL);
|
|
|
|
|
|
|
|
|
|
// 获取配置文件中定义的数据库用户名
|
|
|
|
|
String username = ConfigurationManager.getProperty(Constants.JDBC_USERNAME);
|
|
|
|
|
|
|
|
|
|
// 获取配置文件中定义的数据库密码,此处变量名存在拼写错误,正确应为Constants.JDBC_PASSWORD
|
|
|
|
|
String passward = ConfigurationManager.getProperty(Constants.JDBC_PSSWORD);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 循环创建指定数量的数据库连接
|
|
|
|
|
for (int i = 0; i < dataSourceSize; i++) {
|
|
|
|
|
// 通过DriverManager创建数据库连接,传入URL、用户名和密码
|
|
|
|
|
Connection connection = DriverManager.getConnection(url, username, passward);
|
|
|
|
|
// 将创建好的连接放入阻塞队列中,供后续获取使用
|
|
|
|
|
queue.put(connection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public static JDBCHelper getInstance()
|
|
|
|
|
{
|
|
|
|
|
// 获取JDBCHelper类的唯一实例对象的静态方法,外部代码通过调用此方法获取该工具类的实例,进而使用其提供的数据库操作功能。
|
|
|
|
|
public static JDBCHelper getInstance() {
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取数据库连接
|
|
|
|
|
* 使用阻塞队列
|
|
|
|
|
* @return
|
|
|
|
|
* 获取数据库连接的方法,从阻塞队列(queue)中取出一个数据库连接对象返回给调用者使用。
|
|
|
|
|
* 如果队列中当前没有可用连接,该方法会阻塞,直到有连接可用(因为LinkedBlockingQueue的take方法的特性)。
|
|
|
|
|
* 如果在获取连接过程中发生中断异常(InterruptedException),会打印栈追踪信息,并返回null。
|
|
|
|
|
*
|
|
|
|
|
* @return 可用的数据库连接对象,如果获取过程出现问题则返回null。
|
|
|
|
|
*/
|
|
|
|
|
public Connection getConnection()
|
|
|
|
|
{
|
|
|
|
|
public Connection getConnection() {
|
|
|
|
|
try {
|
|
|
|
|
// 从阻塞队列中取出一个连接,若队列为空则阻塞等待
|
|
|
|
|
return queue.take();
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
@ -66,77 +90,90 @@ public class JDBCHelper {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 更新数据
|
|
|
|
|
* @param sql
|
|
|
|
|
* @param params
|
|
|
|
|
* @return
|
|
|
|
|
* 用于执行更新数据的SQL语句(如INSERT、UPDATE、DELETE等操作)的方法。
|
|
|
|
|
*
|
|
|
|
|
* @param sql 要执行的SQL语句,包含占位符(例如:INSERT INTO table_name (column1, column2) VALUES (?,?))。
|
|
|
|
|
* @param params SQL语句中占位符对应的参数值数组,按顺序依次对应SQL语句中的占位符。
|
|
|
|
|
* @return 受影响的行数,即执行该更新语句后数据库中实际被修改的行数,如果执行过程出现异常则返回0。
|
|
|
|
|
*/
|
|
|
|
|
public int excuteUpdate(String sql,Object[] params)
|
|
|
|
|
{
|
|
|
|
|
int re=0;
|
|
|
|
|
Connection conn=null;
|
|
|
|
|
PreparedStatement statement=null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
conn=getConnection();
|
|
|
|
|
statement=conn.prepareStatement(sql);
|
|
|
|
|
for (int i = 0; i < params.length; i++) {
|
|
|
|
|
statement.setObject(i+1,params[i]);
|
|
|
|
|
}
|
|
|
|
|
re=statement.executeUpdate();
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
|
|
|
|
|
if(conn!=null)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
queue.put(conn);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
public int excuteUpdate(String sql, Object[] params) {
|
|
|
|
|
int re = 0;
|
|
|
|
|
|
|
|
|
|
Connection conn = null;
|
|
|
|
|
PreparedStatement statement = null;
|
|
|
|
|
|
|
|
|
|
public static interface QueryCallBack
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
// 从连接池中获取一个数据库连接
|
|
|
|
|
conn = getConnection();
|
|
|
|
|
|
|
|
|
|
// 使用获取到的连接创建一个PreparedStatement对象,用于执行带参数的SQL语句
|
|
|
|
|
statement = conn.prepareStatement(sql);
|
|
|
|
|
|
|
|
|
|
// 循环设置PreparedStatement中的参数,将params数组中的值依次赋给SQL语句中的占位符
|
|
|
|
|
for (int i = 0; i < params.length; i++) {
|
|
|
|
|
statement.setObject(i + 1, params[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行更新操作,并获取受影响的行数
|
|
|
|
|
re = statement.executeUpdate();
|
|
|
|
|
|
|
|
|
|
return re;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
} finally {
|
|
|
|
|
// 无论是否执行成功,都需要将使用完的数据库连接归还到连接池中,
|
|
|
|
|
// 如果连接对象不为空,尝试将其放回阻塞队列(queue),如果放回过程出现中断异常则打印栈追踪信息。
|
|
|
|
|
if (conn!= null) {
|
|
|
|
|
try {
|
|
|
|
|
queue.put(conn);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定义一个内部接口QueryCallBack,该接口中定义了一个方法process,用于处理查询结果集(ResultSet)。
|
|
|
|
|
// 具体的处理逻辑由实现该接口的类来定义,实现了一种回调机制,方便根据不同需求灵活处理查询结果。
|
|
|
|
|
public static interface QueryCallBack {
|
|
|
|
|
void process(ResultSet rs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询数据的处理
|
|
|
|
|
* 使用接口回掉,根据用户的自定义接口来进行处理
|
|
|
|
|
* @param sql
|
|
|
|
|
* @param params
|
|
|
|
|
* @param queryCallBack
|
|
|
|
|
* 用于执行查询数据的SQL语句的方法,通过回调接口(QueryCallBack)让调用者自定义如何处理查询得到的结果集。
|
|
|
|
|
*
|
|
|
|
|
* @param sql 要执行的查询SQL语句,例如:SELECT * FROM table_name WHERE condition。
|
|
|
|
|
* @param params SQL语句中占位符对应的参数值数组,按顺序依次对应SQL语句中的占位符(如果有占位符的话)。
|
|
|
|
|
* @param queryCallBack 实现了QueryCallBack接口的对象,用于定义具体如何处理查询得到的结果集(ResultSet)。
|
|
|
|
|
*/
|
|
|
|
|
public void excuteQuery(String sql,Object[] params,QueryCallBack queryCallBack)
|
|
|
|
|
{
|
|
|
|
|
Connection conn=null;
|
|
|
|
|
PreparedStatement statement=null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
conn=getConnection();
|
|
|
|
|
statement=conn.prepareStatement(sql);
|
|
|
|
|
public void excuteQuery(String sql, Object[] params, QueryCallBack queryCallBack) {
|
|
|
|
|
Connection conn = null;
|
|
|
|
|
PreparedStatement statement = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 从连接池中获取一个数据库连接
|
|
|
|
|
conn = getConnection();
|
|
|
|
|
|
|
|
|
|
// 使用获取到的连接创建一个PreparedStatement对象,用于执行带参数的SQL语句
|
|
|
|
|
statement = conn.prepareStatement(sql);
|
|
|
|
|
|
|
|
|
|
// 循环设置PreparedStatement中的参数,将params数组中的值依次赋给SQL语句中的占位符
|
|
|
|
|
for (int i = 0; i < params.length; i++) {
|
|
|
|
|
statement.setObject(i+1,params[i]);
|
|
|
|
|
statement.setObject(i + 1, params[i]);
|
|
|
|
|
}
|
|
|
|
|
ResultSet rs=statement.executeQuery();
|
|
|
|
|
|
|
|
|
|
// 执行查询操作,获取结果集(ResultSet)
|
|
|
|
|
ResultSet rs = statement.executeQuery();
|
|
|
|
|
|
|
|
|
|
// 调用传入的回调接口对象的process方法,将结果集传递进去,由具体实现该接口的类来处理结果集数据。
|
|
|
|
|
queryCallBack.process(rs);
|
|
|
|
|
}catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
if(conn!=null)
|
|
|
|
|
{
|
|
|
|
|
} finally {
|
|
|
|
|
// 无论是否执行成功,都需要将使用完的数据库连接归还到连接池中,
|
|
|
|
|
// 如果连接对象不为空,尝试将其放回阻塞队列(queue),如果放回过程出现中断异常则打印栈追踪信息。
|
|
|
|
|
if (conn!= null) {
|
|
|
|
|
try {
|
|
|
|
|
queue.put(conn);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
@ -147,42 +184,49 @@ public class JDBCHelper {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 批量执行sql语句
|
|
|
|
|
* @param sql
|
|
|
|
|
* @param params
|
|
|
|
|
* @return
|
|
|
|
|
* 用于批量执行SQL语句的方法,适合一次性执行多条结构相似的SQL语句(例如批量插入数据等操作)。
|
|
|
|
|
*
|
|
|
|
|
* @param sql 要批量执行的SQL语句模板,包含占位符,所有批量执行的语句结构相同,只是参数值不同。
|
|
|
|
|
* @param params 包含多条SQL语句参数值的列表,每个元素是一个Object数组,对应一条SQL语句的参数值,顺序与sql中的占位符对应。
|
|
|
|
|
* @return 一个整数数组,表示每条SQL语句执行后受影响的行数,如果执行过程出现异常则返回null。
|
|
|
|
|
*/
|
|
|
|
|
public int[] excuteBatch(String sql,List<Object[]> params)
|
|
|
|
|
{
|
|
|
|
|
Connection connection=null;
|
|
|
|
|
PreparedStatement statement=null;
|
|
|
|
|
int[] res=null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
connection=getConnection();
|
|
|
|
|
statement=connection.prepareStatement(sql);
|
|
|
|
|
//1.取消自动提交
|
|
|
|
|
public int[] excuteBatch(String sql, List<Object[]> params) {
|
|
|
|
|
Connection connection = null;
|
|
|
|
|
PreparedStatement statement = null;
|
|
|
|
|
int[] res = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 从连接池中获取一个数据库连接
|
|
|
|
|
connection = getConnection();
|
|
|
|
|
|
|
|
|
|
// 使用获取到的连接创建一个PreparedStatement对象,用于执行带参数的SQL语句
|
|
|
|
|
statement = connection.prepareStatement(sql);
|
|
|
|
|
|
|
|
|
|
// 1. 取消自动提交模式,这样可以将多条SQL语句作为一个事务来统一提交或回滚,提高数据一致性。
|
|
|
|
|
connection.setAutoCommit(false);
|
|
|
|
|
//2.设置参数
|
|
|
|
|
for (Object[] param:
|
|
|
|
|
params) {
|
|
|
|
|
|
|
|
|
|
// 2. 循环设置每条SQL语句的参数,并将其添加到批处理中,通过addBatch方法将每条语句添加到待执行的批处理队列中。
|
|
|
|
|
for (Object[] param : params) {
|
|
|
|
|
for (int i = 0; i < param.length; i++) {
|
|
|
|
|
statement.setObject(i+1,param[i]);
|
|
|
|
|
statement.setObject(i + 1, param[i]);
|
|
|
|
|
}
|
|
|
|
|
statement.addBatch();
|
|
|
|
|
}
|
|
|
|
|
//3.批量执行
|
|
|
|
|
res=statement.executeBatch();
|
|
|
|
|
//4.最后一步提交
|
|
|
|
|
|
|
|
|
|
// 3. 批量执行所有添加到批处理队列中的SQL语句,返回一个整数数组表示每条语句受影响的行数。
|
|
|
|
|
res = statement.executeBatch();
|
|
|
|
|
|
|
|
|
|
// 4. 最后提交事务,将所有批量执行的SQL语句对数据库的修改统一提交,如果执行过程中出现异常则事务会回滚。
|
|
|
|
|
connection.commit();
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
if(connection!=null)
|
|
|
|
|
{
|
|
|
|
|
} finally {
|
|
|
|
|
// 无论是否执行成功,都需要将使用完的数据库连接归还到连接池中,
|
|
|
|
|
// 如果连接对象不为空,尝试将其放回阻塞队列(queue),如果放回过程出现中断异常则打印栈追踪信息。
|
|
|
|
|
if (connection!= null) {
|
|
|
|
|
try {
|
|
|
|
|
queue.put(connection);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
@ -192,4 +236,4 @@ public class JDBCHelper {
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|