为何要对jdbc进行封装
JDBC操作常见的六个步骤中存在大量的重复操作,对于这种程序冗余的问题,我们我已通过封装处理来解决。jdbc的封装操作可以起到减少重复代码量、防止因大量重复以至代码不小心编写错误的问题。
JDBC封装1.0
通过以下封装可以实现对其中三个步骤完成统一封装处理:
- 加载驱动
- 获取连接
- 获取执行sql语句的对象
- 执行
- 处理结果
- 关闭资源
代码
package com.softeem.jdbc03.utils;
import java.sql.*;
import java.util.Objects;
/**
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* .' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* 佛祖保佑 永无BUG
*/
public class DBUtils {
/**驱动类路径*/
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
/**URL地址*/
private static final String URL = "jdbc:mysql://localhost:3306/test";
/**登录数据库服务器的账号*/
private static final String USER = "root";
/**登录数据库服务器的密码*/
private static final String PASSWORD = "123456";
static {
try {
//1.加载驱动
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 返回数据库连接对象
* @return
*/
public static Connection getConn(){
try {
return DriverManager.getConnection(URL,USER,PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
* @param rs 结果集
* @param stat 处理sql的执行对象
* @param conn 数据库连接
*/
public static void close(ResultSet rs, Statement stat,Connection conn){
//ctrl+alt+t
try {
if (rs != null){
rs.close();
}
if (stat != null){
stat.close();
}
if (conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 实现通用更新操作的方法,连接是需要外部提供的,事务可以在外部操作
* 注意事项:该方法中出现异常必须抛出不能捕获,否则一旦出现异常,在当前方法就已经被catch,
* 从而导致下一级调用者无法发现该异常,而导致事务无法回滚
* @param conn
* @param sql
* @param params
* @return
* @throws SQLException
*/
public static boolean exeUpdate(Connection conn,String sql,Object...params) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
//当传入的参数不为null时执行预处理
if (Objects.nonNull(params)){
for (int i = 0; i < params.length ; i++) {
ps.setObject(i + 1,params[i]);
}
}
//执行更新
return ps.executeUpdate() > 0;
} finally {
DBUtils.close(null,ps,null);
}
}
}
1.0概括
将一些固定不变的字符串常量定义为private static final(私有,静态,唯一)的属性;
在static(游离块)中编写加载驱动的方法,并用try…catch语句处理可能出现的异常;
将获取连接对象,关闭资源,数据的更新操作定义为静态方法,以便于通过调用类来直接使用该方法;
在数据的更新操作中引入事务的概念(注意事项:该方法中出现异常必须抛出不能捕获,否则一旦出现异常,在当前方法就已经被catch,从而导致下一级调用者无法发现该异常,而导致事务无法回滚);
实现通用更新操作的方法,连接是需要外部提供的,事务可以在外部操作,确保使用的是同一个连接.;
SQL语句(对应预处理时需要指定的参数随之变化),因此以上所有的增删改(insert,update,delte)功能可以通过一个封装方法统一完成:
JDBC封装2.0
package com.softeem.jdbc03.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.softeem.jdbc02.uis.enity.User;
import java.sql.*;
import java.util.List;
import java.util.Objects;
/**
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* .' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* 佛祖保佑 永无BUG
*/
public class DBUtils2 {
/**驱动类路径*/
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
/**URL地址*/
private static final String URL = "jdbc:mysql://localhost:3306/test";
/**登录数据库服务器的账号*/
private static final String USER = "root";
/**登录数据库服务器的密码*/
private static final String PASSWORD = "123456";
/**登录数据库服务器的密码*/
private static final int INIT_SIZE = 1;
/**初始连接数*/
private static final int MAX_ACTIVE = 5;
/**最长等待连接获取时间*/
private static final long MAX_WAIT = 5000;
/**最小闲置连接数*/
private static final int MIN_IDLE = 1;
/**声明数据源*/
private static DruidDataSource dataSource;
static {
dataSource = createDataSource();
}
/**
* 创建连接池并返回
* @return
*/
private static DruidDataSource createDataSource(){
//创建连接池对象
dataSource = new DruidDataSource();
//设置连接数据库的基本字符串
dataSource.setUrl(URL);
dataSource.setUsername(USER);
dataSource.setPassword(PASSWORD);
//设置连接池的初始参数
dataSource.setInitialSize(INIT_SIZE);
dataSource.setMaxActive(MAX_ACTIVE);
dataSource.setMaxWait(MAX_WAIT);
dataSource.setMinIdle(MIN_IDLE);
return dataSource;
}
/**
* 返回数据库连接对象
* @return
*/
public static Connection getConn(){
try {
//当连接池对象为null或者连接池对象被关闭时重新创建连接池
if (dataSource == null || dataSource.isClosed()){
dataSource = createDataSource();
}
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
* @param rs 结果集
* @param stat 处理sql的执行对象
* @param conn 数据库连接
*/
public static void close(ResultSet rs, Statement stat,Connection conn){
//ctrl+alt+t
try {
if (rs != null){
rs.close();
}
if (stat != null){
stat.close();
}
if (conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
*
* @param conn
* @param sql
* @param params
* @return
* @throws SQLException
*/
public static boolean exeUpdate(Connection conn,String sql,Object...params) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
//当传入的参数不为null时执行预处理
if (Objects.nonNull(params)){
for (int i = 0; i < params.length ; i++) {
ps.setObject(i + 1,params[i]);
}
}
//执行更新
return ps.executeUpdate() > 0;
} finally {
DBUtils2.close(null,ps,null);
}
}
/**
* 查询单行数据
* @param sql
* @param call
* @param params
* @return
*/
public static Object queryOne(String sql,Callback call,Object... params){
Connection conn = getConn();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
if (Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1,params[i]);
}
rs = ps.executeQuery();
//回调机制,将结果集转换为Objec返回
return call.toObject(rs);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.close(rs,ps,conn);
}
return null;
}
/**
* 封装查询多条记录
* @param sql
* @param call
* @param params
* @return
*/
public static List queryList(String sql,Callback call,Object... params){
Connection conn = getConn();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
if (Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1,params[i]);
}
rs = ps.executeQuery();
//回调机制,将结果集转换为Objec返回
return call.toList(rs);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.close(rs,ps,conn);
}
return null;
}
//内部接口
public interface Callback{
//回调函数(钩子函数)
/**
* 将结果集转换为Object 为查询单条数据准备
* @param rs
* @return
*/
default Object toObject(ResultSet rs){
return null;
}
/**
* 将结果集转换为List集合 为查询多条数据准备
* @param rs
* @return
*/
default List toList(ResultSet rs){
return null;
}
}
}
2.0概括
在1.0的基础引入了连接池的概念,让我们在获取连接时更加快捷,但是项目加载过程中需要耗费一定的时间搭建连接池;
连接池的原理即,在程序启动时,事先开启并通过容器维护一定量的数据连接,从而为后续的数据持久化功能节省连接获取的时间开销,因为一旦dao需要使用连接时,只需要从连接池中直接获取即可(类似从一个集合中取一个元素),并且使用完毕之后只需将连接归还到连接池中即可,从程序的访问效率上得到极大提升,因此,连接池技术,是服务端技术的重要组成部分。
创建内部接口,使 queryList函数能够进行回调,及把需要引入的集合中元素类型问题抛给用户解决后然后再完成该方法的实现。
JDBC封装3.0
package com.softeem.jdbc03.utils;
import com.alibaba.druid.pool.DruidDataSource;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
/**
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* .' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* 佛祖保佑 永无BUG
*/
public class DBUtils3 {
/**
* 驱动类路径
*/
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
/**
* URL地址
*/
private static final String URL = "jdbc:mysql://localhost:3306/test";
/**
* 登录数据库服务器的账号
*/
private static final String USER = "root";
/**
* 登录数据库服务器的密码
*/
private static final String PASSWORD = "123456";
/**
* 登录数据库服务器的密码
*/
private static final int INIT_SIZE = 1;
/**
* 初始连接数
*/
private static final int MAX_ACTIVE = 5;
/**
* 最长等待连接获取时间
*/
private static final long MAX_WAIT = 5000;
/**
* 最小闲置连接数
*/
private static final int MIN_IDLE = 1;
/**
* 声明数据源
*/
private static DruidDataSource dataSource;
static {
dataSource = createDataSource();
}
/**
* 创建连接池并返回
*
* @return
*/
private static DruidDataSource createDataSource() {
//创建连接池对象
dataSource = new DruidDataSource();
//设置连接数据库的基本字符串
dataSource.setUrl(URL);
dataSource.setUsername(USER);
dataSource.setPassword(PASSWORD);
//设置连接池的初始参数
dataSource.setInitialSize(INIT_SIZE);
dataSource.setMaxActive(MAX_ACTIVE);
dataSource.setMaxWait(MAX_WAIT);
dataSource.setMinIdle(MIN_IDLE);
return dataSource;
}
/**
* 返回数据库连接对象
*
* @return
*/
public static Connection getConn() {
try {
//当连接池对象为null或者连接池对象被关闭时重新创建连接池
if (dataSource == null || dataSource.isClosed()) {
dataSource = createDataSource();
}
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
*
* @param rs 结果集
* @param stat 处理sql的执行对象
* @param conn 数据库连接
*/
public static void close(ResultSet rs, Statement stat, Connection conn) {
//ctrl+alt+t
try {
if (rs != null) {
rs.close();
}
if (stat != null) {
stat.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* @param conn
* @param sql
* @param params
* @return
* @throws SQLException
*/
public static boolean exeUpdate(Connection conn, String sql, Object... params) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
//当传入的参数不为null时执行预处理
if (Objects.nonNull(params)) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
//执行更新
return ps.executeUpdate() > 0;
} finally {
DBUtils3.close(null, ps, null);
}
}
/**
* Object -> Map
* 将查询的结果存储到一个List集合中,集合中每一个元素即一个Map对象
* (属性:属性值)
*
* @return
*/
public static List<Map<String, Object>> queryToMap(String sql,Object...params) {
//声明List集合存储所有查询的数据(内部的每一条数据使用一个Map对象表示)
List<Map<String, Object>> list = new ArrayList<>();
PreparedStatement ps = null;
ResultSet rs = null;
Connection conn = DBUtils2.getConn();
try {
ps = conn.prepareStatement(sql);
//执行预处理(如果存在占位符,则为占位符填充具体的值)
if (Objects.nonNull(params)){
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1,params[i]);
}
}
//执行查询
rs = ps.executeQuery();
//获取结果集元数据对象
ResultSetMetaData rsmd = rs.getMetaData();
//获取总查询列数
int columnCount = rsmd.getColumnCount();
//遍历结果集
while (rs.next()) {
//每一次循环循环创建一个Map(查询到一条数据)
Map<String, Object> map = new HashMap<>();
//获取每一列的列标签
for (int i = 1; i <= columnCount; i++) {
//获取指定的列标签
String cname = rsmd.getColumnName(i);
//获取列类型
int type = rsmd.getColumnType(i);
//根据列标签获取列值
Object value = rs.getObject(i);
//将一个查询列装入map
map.put(cname, value);
}
//将map集合(javabean)存储到List
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs,ps,conn);
}
return list;
}
/**
* 封装通用查询单条数据的方法
*
* JDBC,反射,集合框架,lambda表达式,新增Objects类
*
* @param <T>
* @param t
* @param sql
* @param params
* @return
*/
public static <T> T queryOne(Class<T> t, String sql, Object... params) {
// 获取查询到到数据集合
List<Map<String, Object>> list = queryToMap(sql, params);
if (list.size() > 0) {
// 获取一个Map对象
Map<String, Object> map = list.get(0);
// 将map集合转换为Javabean并返回
return mapToBean(map, t);
}
return null;
}
/**
* 封装通用查询多条数据的方法
*
* @param <T>
* @param t
* @param sql
* @param params
* @return
*/
public static <T> List<T> queryList(Class<T> t, String sql, Object... params) {
List<T> list = new ArrayList<T>();
// 获取所有查询的到的数据
List<Map<String, Object>> maps = queryToMap(sql, params);
// 遍历集合中每一条数据(map)
maps.forEach(m -> {
// 将map转换为Javabean
T obj = mapToBean(m, t);
// 将Javabean装入list
list.add(obj);
});
return list;
}
/**
* 将Map集合转换为一个确定的类型
*
* @param <T>
* @param map
* @param t
* @return
*/
private static <T> T mapToBean(Map<String, Object> map, Class<T> t) {
try {
// 根据提供的Class对象创建对应类型的Object
T obj = t.newInstance();
map.forEach((k, v) -> {
try {
// 根据Field名称获取字段对象
Field field = t.getDeclaredField(k);
// 设置字段的可访问性
field.setAccessible(true);
// 为字段设置值
field.set(obj, v);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return obj;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 返回对象
return null;
}
}
3.0概括
在2.0基础上引入反射概念,让程序更简单有效。
总结
正如同jdbc封装步骤的一步步简洁而言,我们编程的工作也正是这样循序渐进、任重而道远的过程。随着我们掌握知识的增加,也会不断推翻以前的代码,让自己编程能力以及编写的代码不断完善。