JDBC笔记(二):JDBC的封装
1、原生JDBC的缺陷
在 JDBC 笔记(一):JDBC的开发步骤 中,提到原生JDBC查询数据库的开发步骤,不难看出用原生的JDBC查询数据库有以下缺点:
1、代码重复
原生JDBC,获取数据库连接、创建Statement对象步骤,每次查询数据库都需要创建并获取1.
2、资源管理
数据库连接资源需要手动关闭。
3、结果集处理
添加数据库的结果集需要映射到实体对象中的逻辑处理。
4、SQL耦合
SQL硬编码在代码逻辑中。
2、JDBC封装优化
针对上述缺陷,对原生JDBC做简单封装。JDBC封装是基于JDBC 笔记(一):JDBC的开发步骤中的原生JDBC的优化。
2.1、代码重复、资源管理的优化
创建jdbc工具类,封装获取连接,关闭资源的方法。
1 import java.sql.*; 2 import java.util.Objects; 3 4 public class JdbcUtils { 5 6 private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"; 7 private static final String USERNAME = "root"; 8 private static final String PASSWORD = "root"; 9 public static Connection conn = null; 10 public static PreparedStatement ps = null; 11 12 /** 13 * 数据库连接 14 * @return 15 */ 16 public static Connection getConnection() { 17 if (conn == null) { 18 try { 19 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); 20 } catch (SQLException e) { 21 e.printStackTrace(); 22 } 23 } 24 return conn; 25 } 26 27 /** 28 * 关闭资源 29 * @param conn 30 * @param sta 31 * @param rs 32 */ 33 public static void close(Connection conn, Statement sta, ResultSet rs) { 34 try { 35 // 关闭ResultSet 36 if (Objects.nonNull(rs)) { 37 rs.close(); 38 } 39 // 关闭Statement 40 if (Objects.nonNull(sta)) { 41 sta.close(); 42 } 43 // 关闭Connection 44 if (Objects.nonNull(conn)) { 45 sta.close(); 46 } 47 } catch (SQLException e) { 48 e.printStackTrace(); 49 } 50 } 51 }
JDBC操作可简化如下:
1 public void getUserInnfo() throws Exception { 2 // 获取连接 3 Connection conn = JdbcUtils.getConnection(); 4 5 String querySql = " select * from user where id = ?"; 6 PreparedStatement ps = conn.prepareStatement(querySql); 7 ps = conn.prepareStatement(querySql); 8 ps.setInt(1, 101); 9 ResultSet rs = ps.executeQuery(); 10 while(rs.next()) { 11 System.out.println("用户名称:" + rs.getString("name")); 12 } 13 14 // 关闭资源 15 JdbcUtils.close(conn, ps, rs); 16 }
2.2、SQL耦合的优化
对SQL耦合的优化,可以通过封装PreparedStatement的对SQL的操作处理来完成,在工具类中封装操作SQL的方法:
1 import java.sql.*; 2 import java.util.Objects; 3 4 /** 5 * @Description: JDBC工具类 6 * @author: snails 7 * @since: 2023/1/12 11:26 8 */ 9 public class JdbcUtils { 10 11 private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"; 12 private static final String USERNAME = "root"; 13 private static final String PASSWORD = "root"; 14 15 public static Connection conn = null; 16 public static PreparedStatement ps = null; 17 18 /** 19 * 数据库连接 20 * @return 21 */ 22 public static Connection getConnection() { 23 if (conn == null) { 24 try { 25 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); 26 } catch (SQLException e) { 27 e.printStackTrace(); 28 } 29 } 30 return conn; 31 } 32 33 /** 34 * 关闭资源 35 * @param conn 36 * @param sta 37 * @param rs 38 */ 39 public static void close(Connection conn, Statement sta, ResultSet rs) { 40 try { 41 // 关闭ResultSet 42 if (Objects.nonNull(rs)) { 43 rs.close(); 44 } 45 // 关闭Statement 46 if (Objects.nonNull(sta)) { 47 sta.close(); 48 } 49 // 关闭Connection 50 if (Objects.nonNull(conn)) { 51 sta.close(); 52 } 53 } catch (SQLException e) { 54 e.printStackTrace(); 55 } 56 } 57 58 /** 59 * 执行查询 60 * @param sql 61 * @param parameters 62 * @return 63 */ 64 public static ResultSet executeQuery(String sql, Object...parameters) throws SQLException { 65 ResultSet rs = null; 66 // 获取数据库库连接 67 conn = getConnection(); 68 // 创建Statement对象 69 ps = conn.prepareStatement(sql); 70 // 设置sql中的参数 71 if (Objects.nonNull(parameters) && parameters.length > 0) { 72 for (int i = 0; i < parameters.length; i++) { 73 ps.setObject(i+1, parameters[i]); 74 } 75 } 76 77 // 返回结果集 78 return ps.executeQuery(); 79 } 80 81 }
查询逻辑的简化:
1 public void getUserInnfo() throws Exception { 2 // 查询 3 ResultSet rs = JdbcUtils.executeQuery("select * from user where id = ?", 101); 4 // 结果集的处理 5 while(rs.next()) { 6 System.out.println("用户名称:" + rs.getString("name")); 7 } 8 // 关闭资源 9 JdbcUtils.close(JdbcUtils.conn, JdbcUtils.ps, rs); 10 }
2.3、ResultSet结果集的优化
对于ResultSet结果集的优化,利用反射、元数据进行处理。
1 /** 2 * 数据库列与实体对象映射处理 3 * @param sql 4 * @param clazz 5 * @param parameters 6 * @param <T> 7 * @return 8 * @throws Exception 9 */ 10 public static <T> List<T> executeQuery(String sql, Class clazz, Object...parameters) throws Exception { 11 // 获取连接 12 conn = getConnection(); 13 // 执行SQL获取结果集 14 ResultSet rs = executeQuery(sql, parameters); 15 // 获取表的元数据 16 ResultSetMetaData metaData = ps.getMetaData(); 17 18 List<T> resultList = new ArrayList<>(); 19 20 // 结果集映射成对象实体 21 while(rs.next()) { 22 // 获取表字段总数 23 int columnCount = metaData.getColumnCount(); 24 // 通过反射实例化对象 25 Object obj = clazz.newInstance(); 26 // 遍历表字段 27 for (int i = 1; i <= columnCount; i++) { 28 // 列名 29 String columnName = metaData.getColumnName(i); 30 // 列值 31 Object columnValue = rs.getObject(i); 32 // 数据库字段与数据库值的映射 33 setFieldValueForColumn(obj, columnName, columnValue); 34 } 35 resultList.add((T) obj); 36 } 37 // 关闭资源 38 JdbcUtils.close(JdbcUtils.conn, JdbcUtils.ps, rs); 39 40 return resultList; 41 } 42 43 /** 44 * 根据字段名称设置对象属性 45 * @param o 46 * @param columnName 47 * @param columnValue 48 */ 49 private static void setFieldValueForColumn(Object o, String columnName, Object columnValue) { 50 Class<?> aClass = o.getClass(); 51 try { 52 Field field = aClass.getDeclaredField(columnName); 53 field.setAccessible(true); 54 field.set(o, columnValue); 55 field.setAccessible(false); 56 } catch (Exception e) { 57 // 驼峰模式的处理 58 if (columnName.contains("_")) { 59 // \w -> 元字符,相当于 [a-zA-Z0-9] 60 Pattern pattern = Pattern.compile("_(\\w)"); 61 columnName = columnName.toLowerCase(); 62 Matcher matcher = pattern.matcher(columnName); 63 StringBuffer sb = new StringBuffer(); 64 65 if (matcher.find()) { 66 // matcher.group(1) 指的是第一个括号里的东西 \w 67 // 替换掉_,并将_的相邻下一个字母转为大写 68 matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); 69 } 70 matcher.appendTail(sb); 71 // 再次调用复制操作 72 setFieldValueForColumn(o,sb.toString(),columnValue); 73 } 74 } 75 }
查询逻辑的简化:
1 public void getUserInnfo() throws Exception { 2 // 查询 3 List<User> userList = JdbcUtils.executeQuery("select * from user where id = ?", User.class, 101); 4 // 结果集的处理 5 System.out.println("userList = " + userList); 6 }
通过上述的优化,我们只需要关注SQL的编写,大大提高了开发的效率。