【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()

- 可通过反射创建类的实例,获取指定的属性并赋值

 

posted @ 2020-04-23 19:45  emdzz  阅读(206)  评论(0编辑  收藏  举报