目录

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