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