Java 操作数据库(四) JDBC封装进阶
目录
本节目标
- 学会完整封装使用 JDBC
本示例以 MySQL 数据库作为演示库
引入驱动包
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</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>
封装代码 JDBCUtil
package com.bqteam.learn.jdbc;
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class JDBCUtil {
private static String driverName = "";
private static String dsn = "";
private static String user = "";
private static String pwd = "";
public static void init(String driverName, String dsn, String user, String pwd) {
JDBCUtil.driverName = driverName;
JDBCUtil.dsn = dsn;
JDBCUtil.user = user;
JDBCUtil.pwd = pwd;
}
/**
* 获取连接
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
// 加载驱动
Class.forName(driverName);
conn = DriverManager.getConnection(dsn, user, pwd);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭 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();
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();
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();
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;
}
}
使用 JUnit 测试
创建跟数据库字段对应的 Bean:LphHomework
,上面 JDBC 类无法进行数据下划线字段转 Bean 驼峰映射,因此演示 Bean 所有成员属性都是使用下划线命名,实际开发中应使用驼峰法命名。
package com.bqteam.learn.bean;
import java.io.Serializable;
public class LphHomework implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* name
*/
private String name;
/**
* math_cls_group_id
*/
private Long math_cls_group_id;
public LphHomework() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getMath_cls_group_id() {
return math_cls_group_id;
}
public void setMath_cls_group_id(Long math_cls_group_id) {
this.math_cls_group_id = math_cls_group_id;
}
}
测试代码如下
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 TestJDBCUtil {
@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";
JDBCUtil.init(driverName, dsn, user, pwd);
}
@Test
public void testQueryList() {
final String SQL = "select * from lph_homework limit ?";
Object[] param = {10};
List<LphHomework> list = JDBCUtil.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