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的编写,大大提高了开发的效率。

 

posted @ 2023-03-10 15:38  无虑的小猪  阅读(376)  评论(0编辑  收藏  举报