【Java】JDBC Part2 工具类封装实现
JDBC 工具类封装实现
- 注册和配置都放在静态代码块完成
- 静态方法获取连接,和释放资源
- 本类不产生实例
- 5版本 + 已经可以实现无驱动注册,所以驱动部分注释了
package cn.dai.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * @author ArkD42 * @file Jdbc * @create 2020 - 04 - 23 - 19:25 */ public class JdbcUtil { // 不可生成实例 private JdbcUtil(){} // 连接对象,扩大作用域 private static Connection connection; static { InputStream inputStream = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); try { properties.load(inputStream); // String driverClass = properties.getProperty("driverClass"); String url = properties.getProperty("url"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); // Class.forName(driverClass); connection = DriverManager.getConnection(url, user, password); // System.out.println(connection); 打印检查 } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 * @return 返回连接对象 */ public static Connection getConnection(){ return connection; } /** * 释放资源关闭连接 * @param connection 连接对象 * @param preparedStatement 预编译SQL对象 */ public static void closeResource(Connection connection, PreparedStatement preparedStatement){ try { if (preparedStatement != null) preparedStatement.close(); if (connection != null) connection.close(); }catch (SQLException e){ e.printStackTrace(); } } }
数据库脚本
/* SQLyog Ultimate v12.5.0 (64 bit) MySQL - 8.0.19 : Database - jdbc_db ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; CREATE DATABASE /*!32312 IF NOT EXISTS*/`jdbc_db` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */; USE `jdbc_db`; /*Table structure for table `user` */ DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `user_id` int NOT NULL AUTO_INCREMENT, `user_name` varchar(40) DEFAULT NULL, `user_password` varchar(40) DEFAULT NULL, `user_createTime` date DEFAULT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; /*Data for the table `user` */ insert into `user`(`user_id`,`user_name`,`user_password`,`user_createTime`) values (1,'阿伟','123456',NULL), (2,'杰哥','234567',NULL), (3,'吉良吉影','345678','1997-08-06'), (4,'东方定助','345678','1997-09-06'), (5,'路易十八','567891','1987-09-01'); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
增删改是同一个操作,可以直接封装成update方法执行
/** * 实现增删改的封装,上面的方法直接封装在这个更新方法里面一并调用了 * @param sql * @param args */ public static void update(String sql,Object[] args) { Connection connection = null; PreparedStatement preparedStatement = null; try { connection = JdbcUtil.getConnection(); // 利用我们自己封装的方法获取对象 preparedStatement = connection.prepareStatement(sql); // SQL语句预编译注入 for (int i = 0; i < args.length; i++) { // 参数注入 preparedStatement.setObject(i+1,args[i]); // 注意索引 } int i = preparedStatement.executeUpdate(); System.out.println("执行结果:" + i); } catch (SQLException e) { e.printStackTrace(); } JdbcUtil.closeResource(connection,preparedStatement); // 释放连接 } }
测试类
@Test public void ptst2() throws ParseException { String sql = "insert into user(user_name,user_password,user_createTime) values(?,?,?);"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); java.util.Date date = simpleDateFormat.parse("1987-09-01"); Object[] args = {"路易十八","567891",new java.sql.Date(date.getTime())}; JdbcUtil.update(sql,args); }
查询方法 结果集对象还不能释放,所以只能单独写
@Test public void ptst3() throws ParseException, SQLException { String sql = "select * from user;"; Connection connection = JdbcUtil.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ int user_id = resultSet.getInt(1); String user_name = resultSet.getString(2); String user_password = resultSet.getString(3); Date date = resultSet.getDate(4); // 改用实体类toString // System.out.println(user_id + "|" + user_name + "|" + user_password + "|" + date); User user = new User(user_id, user_name, user_password, date); System.out.println(user); } resultSet.close(); preparedStatement.close(); connection.close(); }
ORM实体类映射
package cn.dai.pojo; import java.sql.Date; /** * @author ArkD42 * @file Jdbc * @create 2020 - 04 - 23 - 20:29 */ public class User { private int user_id; private String user_name; private String user_password; private java.sql.Date user_createTime; public User() { } public User(int user_id, String user_name, String user_password, Date user_createTime) { this.user_id = user_id; this.user_name = user_name; this.user_password = user_password; this.user_createTime = user_createTime; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getUser_name() { return user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } public String getUser_password() { return user_password; } public void setUser_password(String user_password) { this.user_password = user_password; } public Date getUser_createTime() { return user_createTime; } public void setUser_createTime(Date user_createTime) { this.user_createTime = user_createTime; } @Override public String toString() { return "User{" + "user_id=" + user_id + ", user_name='" + user_name + '\'' + ", user_password='" + user_password + '\'' + ", user_createTime=" + user_createTime + '}'; } }
但是,可以使用Map+List方式实现对结果集的封装
/** * 通用查询操作 * @param sql 查询SQL语句 * @param args SQL参数,没有参数直接写null即可 * @return 返回一个List集合,每一个元素是一个Map,存储了每一列字段的数据 */ public static List<Map<Integer, Object>> query(String sql, Object[] args){ List<Map<Integer,Object>> list = new ArrayList<Map<Integer,Object>>(); // 初始化集合容器 try{ Connection connection = JdbcUtil.getConnection(); // 连接对象 PreparedStatement preparedStatement = connection.prepareStatement(sql); // 获取SQL预编译对象 if (args != null){ // 对参数数组的判断,如果为null表明不需要参数注入 for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i+1,args[i]); } } ResultSet resultSet = preparedStatement.executeQuery(); // 执行SQL ResultSetMetaData metaData = resultSet.getMetaData(); //获取结果集的元数据对象 int columnCount = metaData.getColumnCount(); // 元数据对象获取总共的记录的列数 while (resultSet.next()){ // 判断是否还有下一个记录行 Map<Integer,Object> map = new HashMap<Integer, Object>(); //作为每一行的记录 for (int i = 0; i < columnCount ; i++) { // 通过遍历记录列数,获取每个列的值 Object object = resultSet.getObject(i + 1); map.put(i,object); // 装入每个字段的值 } list.add(map); // 遍历多少,装载多少个记录 } resultSet.close(); // 资源释放 JdbcUtil.closeResource(connection,preparedStatement); } catch (Exception e){ e.printStackTrace(); } return list; // 返回结果 }
测试类
@Test public void queryTest(){ String sql1 = "select * from user;"; List<Map<Integer, Object>> maps = JdbcUtil.query(sql1, null); for (Map<Integer, Object> map:maps) { System.out.println(map); } }
结果
ORM+ 反射
package cn.dai.util; import java.io.InputStream; import java.lang.reflect.Field; import java.sql.*; import java.util.*; /** * @author ArkD42 * @file Jdbc * @create 2020 - 04 - 23 - 19:25 */ public class JdbcUtil { // 不可生成实例 private JdbcUtil(){} // 连接对象,扩大作用域 private static Connection connection; static { InputStream inputStream = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); try { properties.load(inputStream); // String driverClass = properties.getProperty("driverClass"); String url = properties.getProperty("url"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); // Class.forName(driverClass); connection = DriverManager.getConnection(url, user, password); // System.out.println(connection); 打印检查 } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 * @return 返回连接对象 */ public static Connection getConnection(){ return connection; } /** * 释放资源关闭连接 * @param connection 连接对象 * @param preparedStatement 预编译SQL对象 */ public static void closeResource(Connection connection, PreparedStatement preparedStatement){ try { if (preparedStatement != null) preparedStatement.close(); if (connection != null) connection.close(); }catch (SQLException e){ e.printStackTrace(); } } /** * 实现增删改的封装,上面的方法直接封装在这个更新方法里面一并调用了 * @param sql * @param args */ public static void update(String sql,Object[] args) { Connection connection = null; PreparedStatement preparedStatement = null; try { connection = JdbcUtil.getConnection(); // 利用我们自己封装的方法获取对象 preparedStatement = connection.prepareStatement(sql); // SQL语句预编译注入 for (int i = 0; i < args.length; i++) { // 参数注入 preparedStatement.setObject(i+1,args[i]); // 注意索引 } int i = preparedStatement.executeUpdate(); System.out.println("执行结果:" + i); } catch (SQLException e) { e.printStackTrace(); } JdbcUtil.closeResource(connection,preparedStatement); // 释放连接 } /** * 通用查询操作 * @param sql 查询SQL语句 * @param args SQL参数,没有参数直接写null即可 * @return 返回一个List集合,每一个元素是一个Map,存储了每一列字段的数据 */ public static List<Map<Integer, Object>> query(String sql, Object[] args){ List<Map<Integer,Object>> list = new ArrayList<Map<Integer,Object>>(); // 初始化集合容器 try{ Connection connection = JdbcUtil.getConnection(); // 连接对象 PreparedStatement preparedStatement = connection.prepareStatement(sql); // 获取SQL预编译对象 if (args != null){ // 对参数数组的判断,如果为null表明不需要参数注入 for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i+1,args[i]); } } ResultSet resultSet = preparedStatement.executeQuery(); // 执行SQL ResultSetMetaData metaData = resultSet.getMetaData(); //获取结果集的元数据对象 int columnCount = metaData.getColumnCount(); // 元数据对象获取总共的记录的列数 while (resultSet.next()){ // 判断是否还有下一个记录行 Map<Integer,Object> map = new HashMap<Integer, Object>(); //作为每一行的记录 for (int i = 0; i < columnCount ; i++) { // 通过遍历记录列数,获取每个列的值 Object object = resultSet.getObject(i + 1); map.put(i,object); // 装入每个字段的值 } list.add(map); // 遍历多少,装载多少个记录 } resultSet.close(); // 资源释放 JdbcUtil.closeResource(connection,preparedStatement); } catch (Exception e){ e.printStackTrace(); } return list; // 返回结果 } /** * 通用SQL查询2,反射ORM实体类 * @param tClass * @param sql * @param args * @param <T> * @return */ public static <T> List<T> queryList(Class<T> tClass,String sql,Object[] args){ Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try{ connection = JdbcUtil.getConnection(); preparedStatement = connection.prepareStatement(sql); // 判断参数注入,遍历数组注入,缩写一行 if (args != null) for (int i = 0; i < args.length; i++) preparedStatement.setObject(i+1,args[i]); resultSet = preparedStatement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); List<T> tList = new ArrayList<T>(); while(resultSet.next()){ T t = tClass.newInstance(); // 反射出实体类对象 for (int i = 0; i < columnCount; i++) { Object columnValue = resultSet.getObject(i + 1); // 字段的值 String columnLabel = metaData.getColumnLabel(i + 1); // 字段的别名 Field field = tClass.getDeclaredField(columnLabel); // 反射获取实例的字段属性 field.setAccessible( true ); // 解开访问权限 field.set(t,columnValue); // 用字段实例注入值 } tList.add(t); } return tList; } catch (Exception e){ e.printStackTrace(); } finally { try { assert resultSet != null; resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } JdbcUtil.closeResource(connection,preparedStatement); } return null; } }
测试
为什么不要使用Statement执行SQL?
SQL拼接注入问题,数据危险访问,泄露安全
使用PrepareStatement执行SQL的原因:
- 防止Statement的拼接注入问题
- 性能比Statement更高
https://yq.aliyun.com/articles/635015
两种思想:
- 面向接口编程
- ORM思想【Object Relational Mapping】
一张数据表 对应了 一个类
表中的一条数据 对应了 一个对象
表中的一个字段 对应了 对象的一个属性
SQL需要结合列名和表的属性编写,注意别名的问题
两种技术:
- JDBC结果集的元数据 ResultSetMetaData ,元数据,就是修饰数据的数据,例如 int p,这些修饰也是一种数据,称为元数据
获取查询记录的列数 getColumnCount()
获取列的别名 getCoulumnLabel()
- 可通过反射创建类的实例,获取指定的属性并赋值