一、目的:
使用JDBC连接数据库,并提供统一的查询入口,返回指定格式的数据。
二、工具:
1)jdk1.8+(其他版本未测)
2)mysql5.7
3)mysql-connector-java-5.1.40-bin.jar
4)java IDE,可以使用eclipse或者idea等,本文演示使用eclipse-Oxygen.2
三、过程:
java的安装和环境变量配置过程略。
mysql的安装和配置过程略。
在eclipse中新建一个java工程(也可以新建动态web工程,因为本文的目的是编写工具类,所以只建java工程即可),命名为Test。
然后引入JDBC驱动包(驱动包在哪里就去哪里找~_~)。
接下来在com.re.utils包下新建工具类JdbcUtils.java。
JdbcUtils类代码:
package com.re.utils;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;
import org.json.JSONObject;
public class JdbcUtils {
// 驱动名称
private String jdbcDriver = "";
// 数据库地址
private String jdbcUrl = "";
// 数据库用户名
private String jdbcUser = "";
// 数据库密码
private String jdbcPassword = "";
// c3p0配置文件路径
private String c3p0url = "";
// 数据库连接类型
private String connectionType = "jdbc";
// 以mySQL为例,参数格式如下:
// jdbcDriver = "com.mysql.jdbc.Driver";
// jdbcUrl = "jdbc:mysql://localhost:3306/test";
// jdbcUser = "root";
// jdbcPassword = "root";
/**
* 带参构造方法
*
* @param driver
* 驱动名称
* @param url
* 数据库地址
* @param user
* 数据库用户名
* @param password
* 数据库密码
*/
public JdbcUtils(String driver, String url, String user, String password) {
// 设定各项参数,包括数据库连接、驱动类型
this.setJdbcDriver(driver);
this.setJdbcUrl(url);
this.setJdbcUser(user);
this.setJdbcPassword(password);
}
/**
* 带参构造方法(C3P0)
*
* @param c3p0url
* C3P0配置文件路径
*/
public JdbcUtils(String c3p0url) {
this.setC3p0url(c3p0url);
this.setConnectionType("c3p0");
}
public String getJdbcDriver() {
return jdbcDriver;
}
public void setJdbcDriver(String jdbcDriver) {
this.jdbcDriver = jdbcDriver;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public String getJdbcUser() {
return jdbcUser;
}
public void setJdbcUser(String jdbcUser) {
this.jdbcUser = jdbcUser;
}
public String getJdbcPassword() {
return jdbcPassword;
}
public void setJdbcPassword(String jdbcPassword) {
this.jdbcPassword = jdbcPassword;
}
public String getC3p0url() {
return c3p0url;
}
public void setC3p0url(String c3p0url) {
this.c3p0url = c3p0url;
}
public String getConnectionType() {
return connectionType;
}
public void setConnectionType(String connectionType) {
this.connectionType = connectionType;
}
/**
* 获取数据库连接
*
* @return 数据库连接
*/
public Connection getConnection() {
String jdbcDriver = this.getJdbcDriver();
Connection connection = null;
try {
// 反射驱动类,调用DriverManager的getConnection()方法获取连接
Class.forName(jdbcDriver);
connection = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return connection;
}
/**
* 根据C3P0连接池获取数据库连接
*
* @param xmlUrl
* c3p0配置文件路径
* @return 数据库连接
*/
public Connection getConnectionByC3P0() {
C3P0Utils c3p0Utils = new C3P0Utils(c3p0url);
Connection conn = c3p0Utils.getConnection();
return conn;
}
/**
* 根据指定的sql和参数进行查询
*
* @param type
* 0为无参,1为有参
* @param sql
* 指定的sql
* @param params
* 参数列表
* @param showAlias
* 是否显示别名
* @return
* @throws Exception
*/
public ArrayList> execute(int type, String sql, ArrayList params, boolean showAlias)
throws Exception {
// 判断sql类型,并返回标识位
int flag = this.checkSqlType(sql);
Connection connection = null;
// 获取数据库连接
if (this.getC3p0url().isEmpty() && "jdbc".equals(this.getConnectionType())) {
connection = this.getConnection();
} else if ("c3p0".equals(this.getConnectionType())) {
connection = this.getConnectionByC3P0();
}
// 判断数据库是否支持查询结果以别名的方式显示
boolean aliasSupport = this.checkDBIsSupportAlias(connection);
if (aliasSupport == false) {
showAlias = false;
}
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 增删改所对应的结果
int result = 0;
ArrayList> list = new ArrayList>();
try {
preparedStatement = connection.prepareStatement(sql);
// 如果有参数的话,则设置参数
if (type == 1) {
// 检测sql中出现的"?"的次数
int num = this.getMarksNum(sql, "?");
// 当sql中出现的"?"次数和输入的参数列表中的元素数量相等时,则可以进行查询。否则,抛出异常
if (params.size() == num) {
// 循环并设置参数
for (int i = 1; i <= params.size(); i++) {
preparedStatement.setObject(i, params.get(i - 1));
}
} else {
throw new Exception("由于sql中的未知变量与参数列表中元素数量不一致,因此无法查询。");
}
}
// 如果sql为查询
if (flag == 1) {
list = this.listConstractor(preparedStatement, resultSet, showAlias);
}
// 如果sql为增删改
else if (flag == 2) {
result = preparedStatement.executeUpdate();
System.out.println("执行完毕,已影响 " + result + " 行。");
}
// 其他的sql
else {
result = preparedStatement.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭数据库连接等
this.releaseDB(resultSet, preparedStatement, connection);
}
return list;
}
/**
* 增删改数据的方法。如果是新增,则返回新增时生成的自增主键(此方法不完善,建议执行单条新增)
*
* @param type
* 0为无参,1为有参
* @param sql
* 指定的sql
* @param params
* 参数列表
* @return 自增主键的List
* @throws Exception
*/
public ArrayList execute(int type, String sql, ArrayList params) throws Exception {
ArrayList ids = new ArrayList<>();
// 判断sql类型,并返回标识位
int flag = this.checkSqlType(sql);
// 如果不是增删改,则直接返回空,不执行语句(避免报错)
if (flag != 2) {
return ids;
}
Connection connection = null;
// 获取数据库连接
if (this.getC3p0url().isEmpty() && "jdbc".equals(this.getConnectionType())) {
connection = this.getConnection();
} else if ("c3p0".equals(this.getConnectionType())) {
connection = this.getConnectionByC3P0();
}
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
int result = 0;
try {
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 如果有参数的话,则设置参数
if (type == 1) {
// 检测sql中出现的"?"的次数
int num = this.getMarksNum(sql, "?");
// 当sql中出现的"?"次数和输入的参数列表中的元素数量相等时,则可以进行查询。否则,抛出异常
if (params.size() == num) {
// 循环并设置参数
for (int i = 1; i <= params.size(); i++) {
preparedStatement.setObject(i, params.get(i - 1));
}
} else {
throw new Exception("由于sql中的未知变量与参数列表中元素数量不一致,因此无法查询。");
}
}
result = preparedStatement.executeUpdate();
System.out.println("执行完毕,已影响 " + result + " 行。");
resultSet = preparedStatement.getGeneratedKeys();
while (resultSet.next()) {
ids.add((Long) resultSet.getObject(1));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭数据库连接等
this.releaseDB(resultSet, preparedStatement, connection);
}
return ids;
}
/**
* 判断sql是增删改查的哪一种
*
* @param sql
* 需要判断的sql
* @return 如果是查询则返回1;如果是增删改则返回2;否则返回0
*/
public int checkSqlType(String sql) {
int flag = 0;
// 如果SQL以SELECT开头
if (sql.toUpperCase().trim().startsWith("SELECT")) {
flag = 1;
}
// 如果SQL以UPDATE或INSERT或DELETE开头
else if (sql.toUpperCase().trim().startsWith("UPDATE") || sql.toUpperCase().trim().startsWith("INSERT") || sql.toUpperCase().trim().startsWith("DELETE")) {
flag = 2;
}
return flag;
}
/**
* 根据指定的字符串来获取特定元素出现的数量
*
* @param str
* 指定的字符串
* @param mark
* 特定元素的内容
* @return 特定元素出现的次数
*/
public int getMarksNum(String str, String mark) {
// 计数
int num = 0;
while (str.indexOf(mark) >= 0) {
// 当特定元素出现时,计数+1
num++;
// 然后将此次出现的特定元素去掉并形成新的字符串来进行下一次循环
str = str.substring(str.indexOf(mark) + 1);
}
return num;
}
/**
* 将查询出来的ArrayList转成JSONObject
*
* @param list
* @return
*/
public JSONObject ArrayListToJSONObject(String key,ArrayList> list) {
JSONObject result = new JSONObject();
result.put(key, this.ArrayListToJSONArray(list));
return result;
}
/**
* 将查询出来的ArrayList转成JSONArray(用于easyUI)
*
* @param list
* @return
*/
public JSONArray ArrayListToJSONArray(ArrayList> list) {
JSONArray result = new JSONArray();
for(HashMap map:list) {
result.put(map);
}
return result;
}
/**
* 将查询结果转换为List
*
* @param preparedStatement
* 预编译语句
* @param resultSet
* 查询结果
* @param showAlias
* 是否显示别名
* @return
*/
public ArrayList> listConstractor(PreparedStatement preparedStatement, ResultSet resultSet,
boolean showAlias) {
ArrayList> list = new ArrayList>();
// 生成结果集
try {
resultSet = preparedStatement.executeQuery();
// 获取结果集属性
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// 获取结果集(属性)中的列数
int columnCount = resultSetMetaData.getColumnCount();
while (resultSet.next()) {
// 每行都需要新建一个map
HashMap map = new HashMap();
for (int i = 1; i <= columnCount; i++) {
// 如果支持别名,则使用别名显示,否则使用列名显示
if (showAlias) {
// 向map中加入键值对,键的内容为别名,值的内容为结果集中的对应的内容
map.put(resultSetMetaData.getColumnLabel(i), resultSet.getObject(i));
} else {
// 如果不支持别名则使用别名显示
map.put(resultSetMetaData.getColumnName(i), resultSet.getObject(i));
}
}
// 将map加入list中
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
/**
* 确认数据库是否支持别名显示
*
* @param connection
* @return
*/
private boolean checkDBIsSupportAlias(Connection connection) {
boolean flag = true;
boolean aliasSupport = false;
DatabaseMetaData dbMeta = null;
try {
dbMeta = connection.getMetaData();
aliasSupport = dbMeta.supportsColumnAliasing();
if (!aliasSupport) {
flag = false;
}
} catch (SQLException e) {
e.printStackTrace();
}
return flag;
}
/**
* 关闭数据库连接
*
* @param resultSet
* 结果集
* @param preparedStatement
* 预处理指令
* @param connection
* 数据库连接
*/
private void releaseDB(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
四、使用 方式:
使用带参构造方法创建JdbcUtils实例(需要传入驱动名、数据库url、数据库用户名和数据库密码四个参数),然
后使用该实例
调用execute方法(需要传入sql是否有参、查询sql、参数列表、是否显示别名四个参数)。
如果是查询语句(select)则返回ArrayList>类型的返回值;如果是增删改语句,则
返回null并在
控制台打出操作数据的条数(很抱歉,这里还无法做到完全智能,所以还需要调用时注意一下)。