了解JDBC

了解JDBC

一,JDBC介绍

	Java Database Connectivity
Java规定的数据库连接接口,SUN公司提供的,为了简化程序员操作数据库的过程。
SUN公司要求数据库提供商,按照JDBC API接口规范,完成对应Java程序的数据连接操作,规范Jar包,并且提供对应的操作方法。

JDBC接口中核心的内容
	java.sql.*
	javax.sql.*

二,JDBC连接数据库所需的必要条件

cmd > mysql -hlocalhost -uroot -p123456

1. 明确连接使用的数据库是MySQL数据库
2. 明确当前数据库的主机地址,IP地址,域名,localhost表示本地
3. -uroot 用户名
4. -p123456 对应用户名密码

JDBC连接数据库也是需要这四个条件的
	1. 确定连接的数据库所在网络地址和对应操作哪一个数据库
		这里使用一个符合JDBC规范的URL
		URL jdbc:mysql://localhost:3306/nzgp2001
	2. 用户名 user root
	3. 密码 password 123456
	
URL
	含义
		jdbc目前操作数据库的主协议
		mysql子协议
		localhost 数据库服务器所在的网路偶地址
		3306 数据库默认端口号
		nzgp2001 当前URL连接操作对应数据库是哪一个

JDBC是第三方提供的内容
	获取对应的Jar
		mysql-connector-java-5.1.47.jar
		从官网获取对应的Jar包
		mvnrepository Maven查询网址

三,JDBC连接MySQL数据库

3.1 操作流程

1. 导入Jar包
	在项目根目录创建lib目录,放入对应jar包,引入依赖
2. 加载驱动
	Java程序只是规定了接口规范,但是没有实现
	数据库连接需要使用JDBC对应驱动
3. 准备必要参数连接数据库
4. 获取数据库连接
5. 关闭资源

3.2 数据库连接演示代码

package com.qfedu.a_jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**

  • JDBC连接数据库操作,获取数据库连接方式

  • @author Anonymous 2020/3/23 15:51
    /
    public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
    /

    1. 加载驱动
    */
    Class.forName(“com.mysql.jdbc.Driver”);

     /*
     2. 准备必要的数据
      */
     String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
     String user = "root";
     String password = "123456";
    
     /*
      3. 获取数据库连接
      */
     Connection connection = DriverManager.getConnection(url, user, password);
    
     System.out.println(connection);
    
     /*
     4. 关闭资源
      */
     connection.close();
    
     }
     }	
    

3.3 数据库驱动加载过程

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
// 在.class文件加载到内存时运行,并且有且只执行一次
// 代码初始化过程!!!
static {
    try {
        // DriverManager驱动管理器注册了当前com.mysql.jdbc.Driver
        // 相对于当前Java程序拥有了连接MySQL数据库的必要的驱动条件
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}

/**
 * Construct a new driver and register it with DriverManager
 * 
 * @throws SQLException
 *             if a database error occurs.
 */
public Driver() throws SQLException {
    // Required for Class.forName().newInstance()
}
}

四,JDBC核心API[能记住最好,记不住拉倒]

class java.sql.DriverManager 驱动管理类
--| static java.sql.Connection getConnection(String url, String user, String password);
/*
	这里是根据听的数据库连接URL,对应的user用户名和password密码,获取数据库连接对象
*/

interface java.sql.Connection 数据库连接接口
--| java.sql.Statement createStatement();
/*
	获取数据库SQL语句搬运工对象,从Java程序搬运SQL语句到数据库中,同时Statement也是一个资源对象。
*/
--| java.sql.PreparedStatement prepareStatement(String sql);
/*
	获取数据库SQL语句【预处理】搬运工对象,Java程序的SQL语句,在创建PreparedStatement对象时,将SQL语句交给数据库预处理操作,可以解决一定的【SQL语句注入问题】,同时提高一定的效率,PreparedStatement也是一个资源对象
*/

interface java.sql.Statement 数据库SQL语句搬运工对象
--| int executeUpdate(String sql);
/*
	执行数据库修改数据,insert,update,delete...,返回值类型是int类型,是当前SQL语句搬运到数据库执行之后,数据库运行对于当前操作受到影响的行数
	2 rows affected in 5 ms
*/
--| java.sql.ResultSet executeQuery(String sql);
/*
	执行数据库查询语句,select操作,执行的结果是一个java.sql.ResultSet,结果集对象,当前操作返回值never null
*/

interface java.sql.PreparedStatement 数据库SQL语句【预处理】搬运工对象
    PreparedStatement extends java.sql.Statement
--| int executeUpdate();
/*
	执行数据库修改操作,insert,update,delete...处理的SQL语句是在创建PreparedStatement对象过程预处理的SQL语句,并且返回值是int类型, 为当前操作对于数据表中收到影响的行数
*/
--| java.sql.ResultSet executeQuery();
/*
	执行数据库查询语句,select操作,的SQL语句是在创建PreparedStatement对象过程预处理的SQL语句,执行的结果是一个java.sql.ResultSet,结果集对象,当前操作返回值never null
*/
--| setXXX(int index, XXX value)
/*
	PreparedStatement预处理的SQL语句是可以带有参数的,这里是对于SQL语句参数进行赋值操作,这里有指定的操作下标,和对应的数据,数据类型繁多
*/
    
interface java.sql.ResultSet 数据库结果集接口
--|XXX getXXX(int columnIndex);
/*
	根据查询结果中,字段所处的位置下标获取对应数据,XXX是指定类型
*/
--|XXX getXXX(String fieldName);
/*
	根据查询结果中,字段所处的字段名获取对应数据,XXX是指定类型
*/
--| boolean next();
/*
	判断当前查询结果集中是否还有可以键遍历操作的数据,如果没有。或则当前结果集中是无数据情况 Empty Set,直接返回fasle
*/

五,Statement操作SQL语句

5.1 Statement插入SQL数据操作

@Test
public void testInsert() {
    Statement statement = null;
    Connection connection = null;
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备必要的连接数据
        String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
        String user = "root";
        String password = "123456";
        //3. 获取数据库连接
        connection = DriverManager.getConnection(url, user, password);
        // 4. 获取Statement对象
        statement = connection.createStatement();
        // 5. 准备SQL语句
        String sql = "insert into nzgp2001.user(userName, password) value ('老黑', '123456')";
        // 6. 执行SQL语句
        int affectedRows = statement.executeUpdate(sql);
        System.out.println("affectedRows:" + affectedRows);
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try {
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5.2 Statement修改SQL数据操作

@Test
public void testUpdate() {
    Statement statement = null;
    Connection connection = null;
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备必要的连接数据
        String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
        String user = "root";
        String password = "123456";
        //3. 获取数据库连接
        connection = DriverManager.getConnection(url, user, password);
        // 4. 获取Statement对象
        statement = connection.createStatement();
        // 5. 准备SQL语句
        String sql = "update nzgp2001.user set userName ='航海中路彭于晏' where id = 1";
        // 6. 执行SQL语句
        int affectedRows = statement.executeUpdate(sql);
        System.out.println("affectedRows:" + affectedRows);
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try {
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5.3 Statement删除SQL数据操作

@Test
public void testDelete() {
    Statement statement = null;
    Connection connection = null;
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备必要的连接数据
        String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
        String user = "root";
        String password = "123456";
        //3. 获取数据库连接
        connection = DriverManager.getConnection(url, user, password);
        // 4. 获取Statement对象
        statement = connection.createStatement();
        // 5. 准备SQL语句
        String sql = "delete from user where id > 2";
        // 6. 执行SQL语句
        int affectedRows = statement.executeUpdate(sql);
        System.out.println("affectedRows:" + affectedRows);
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try {
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5.4 Statement查询SQL数据操作

// 查询指定的一个数据行,转换成对应的User对象
@Test
public void testSelectOne() {
    ResultSet resultSet = null;
    Statement statement = null;
    Connection connection = null;
    User user1 = null;
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备必要的连接数据
        String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
        String user = "root";
        String password = "123456";
        // 3. 获取数据库连接
        connection = DriverManager.getConnection(url, user, password);
        // 4. 准备SQL语句
        String sql = "select * from nzgp2001.user where id = 1";
        // 5. 获取Statement对象
        statement = connection.createStatement();
        // 6. 执行SQL语句
        resultSet = statement.executeQuery(sql);
        // 7. ResultSet结果集对象解析过程
        while (resultSet.next()) {
            // 通过指定的字段获取对应的数据
            int id = resultSet.getInt("id");
            String userName = resultSet.getString("userName");
            String password1 = resultSet.getString("password");
            user1 = new User(id, userName, password1);
            System.out.println(user1);
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

// 查询多个数据
@Test
public void testSelectAll() {
    ResultSet resultSet = null;
    Statement statement = null;
    Connection connection = null;
    // 准备了一个存储User对象的List集合
    List<User> list = new ArrayList<>();
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 准备必要的连接数据
        String url = "jdbc:mysql://localhost:3306/nzgp2001?useSSL=true";
        String user = "root";
        String password = "123456";
        // 3. 获取数据库连接
        connection = DriverManager.getConnection(url, user, password);
        // 4. 准备SQL语句
        String sql = "select * from nzgp2001.user";
        // 5. 获取Statement对象
        statement = connection.createStatement();
        // 6. 执行SQL语句
        resultSet = statement.executeQuery(sql);
        // 7. ResultSet结果集对象解析过程
        while (resultSet.next()) {
            // 通过指定的字段获取对应的数据
            int id = resultSet.getInt("id");
            String userName = resultSet.getString("userName");
            String password1 = resultSet.getString("password");
            // 从数据库中读取的User数据保存到对象中,添加到List内
            list.add(new User(id, userName, password1));
        }
        for (User user1 : list) {
            System.out.println(user1);
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

六, JDBC工具类封装

需要完成的内容
	1. 数据库连接对象java.sql.Connection获取过程
	2. 关闭资源

JDBC工具类
	1. 所有的方法都是static修饰的静态方法
	2. 需要考虑自动加载过程,完成一些必要数据的自动处理
		url
		driver
		user
		password
	3. 所需数据库连接条件保存到文件中
	4. 关闭方法提供多种多样组合方法

【注意】
	db.properties文件保存到src目录下
	
# 当前JDBC连接所需的驱动
driverClass=com.mysql.jdbc.Driver

# 数据库连接符合JDBC规范的url
url=jdbc:mysql://localhost:3306/nzgp2001?useSSL=true

# 用户名
user=root

# 密码
password=123456

代码演示

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类,负责数据库连接对象和数据库资源关闭
 *
 * @author Anonymous 2020/3/24 10:08
 */
public class JdbcUtil {
    // 静态成员变量,保存一些必要的数据
    private static String url = null;
    private static String user = null;
    private static String password = null;


    // 利用static修饰的静态代码块完成文件字段自动读取和驱动自动加载过分
    static {
        try {
            // Properties实现类,里面的数据保存形式都是键值对形式
            Properties properties = new Properties();

            // 使用字节输入流,加载对应db.properties,数据保存到Properties对象中
            properties.load(new FileInputStream("./src/db.properties"));

            // 从Properties读取对应的数据
            String driverClass = properties.getProperty("driverClass");
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");

            // 完整驱动加载
            Class.forName(driverClass);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回数据库连接对象,连接失败返回null
     *
     * @return java.sql.Connection 数据库连接对象
     */
    public static Connection getConnection() {
        Connection connection = null;

        try {
            // 通过DriverManager驱动管理类,使用必要参数获取数据库连接
            connection = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return connection;
    }

    /*
    以下三个方法实际上都是执行同一个方法,使用这种方式
        1. 简化代码结构
        2. 规范化所有的操作
     */

    /**
     * 处理数据库操作对应的资源问题
     *
     * @param connection java.sql.Connection 数据库连接对象
     */
    public static void close(Connection connection) {
        close(connection, null, null);
    }

    /**
     * 处理数据库操作对应的资源问题
     *
     * @param connection java.sql.Connection 数据库连接对象
     * @param statement java.sql.Statement 数据库SQL语句搬运工对象
     */
    public static void close(Connection connection, Statement statement) {
        close(connection, statement, null);
    }

    /**
     * 处理数据库操作对应的资源问题
     *
     * @param connection java.sql.Connection 数据库连接对象
     * @param statement java.sql.Statement 数据库SQL语句搬运工对象
     * @param resultSet java.sql.ResultSet 数据库查询结果集对象
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }

            if (statement != null) {
                statement.close();
            }

            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

七,PreparedStatement使用

7.1 PreparedStatement插入数据SQL完成

@Test
public void testInsert() {
    User user = new User(10, "逗比匿名君", "123456");
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        // 获取数据库连接
        connection = JdbcUtil.getConnection();
        // 准备SQL语句
        // ? 表示SQL语句参数占位符!!!
        String sql = "insert into nzgp2001.user(id, userName, password) VALUE (?,?,?)";
        // 预处理SQL语句,获取PreparedStatement对象
        preparedStatement = connection.prepareStatement(sql);
        // SQL语句赋值操作,SQL语句参数是从1开始
        preparedStatement.setObject(1, user.getId());
        preparedStatement.setObject(2, user.getUserName());
        preparedStatement.setObject(3, user.getPassword());
        // 使用PreparedStatement执行SQL语句
        int affectedRows = preparedStatement.executeUpdate();
        System.out.println("affectedRows:" + affectedRows);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement);
    }
}

7.3 PreparedStatment修改SQL完成

 @Test
public void testUpdate() {
    User user = new User(10, "逗比匿名君", "航海中路彭于晏");
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = JdbcUtil.getConnection();
        String sql = "update user set userName = ?, password = ? where id = ?";
        preparedStatement = connection.prepareStatement(sql);
        // 赋值SQL语句参数
        preparedStatement.setObject(1, user.getUserName());
        preparedStatement.setObject(2, user.getPassword());
        preparedStatement.setObject(3, user.getId());
        int affectedRows = preparedStatement.executeUpdate();
        System.out.println("affectedRows:" + affectedRows);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement);
    }
}

7.3 PreparedStatment删除SQL完成

@Test
public void testDelete() {
    int id = 7;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = JdbcUtil.getConnection();
        String sql = "delete from user where id = ?";
        preparedStatement = connection.prepareStatement(sql);
        // 赋值参数
        preparedStatement.setObject(1, id);
        int affectedRows = preparedStatement.executeUpdate();
        System.out.println("affectedRows:" + affectedRows);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement);
    }
}

7.4 PreparedStatment查询SQL完成

@Test
public void testSelectOne() {
    int id = 10;
    User user = null;
    
    ResultSet resultSet = null;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    
    try {
        connection = JdbcUtil.getConnection();
        String sql = "select * from user where id = ?";
        preparedStatement = connection.prepareStatement(sql);
        
        // 赋值参数
        preparedStatement.setObject(1, id);
        resultSet = preparedStatement.executeQuery();
        
        if (resultSet.next()) {
            String userName = resultSet.getString("userName");
            String password = resultSet.getString("password");
            user = new User(id, userName, password);
        }
        
        if (user != null) {
            System.out.println(user);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement, resultSet);
    }
}
@Test
public void testSelectAll() {
    List<User> list = new ArrayList<>();
    
    ResultSet resultSet = null;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    
    try {
        connection = JdbcUtil.getConnection();
        String sql = "select * from user";
        
        preparedStatement = connection.prepareStatement(sql);
        resultSet = preparedStatement.executeQuery();
        
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String userName = resultSet.getString("userName");
            String password = resultSet.getString("password");
            list.add(new User(id, userName, password));
        }
        
        for (User user : list) {
            System.out.println(user);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement, resultSet);
    }
}

八,SQL注入问题

Statement是一个SQL语句搬运工对象,不存在SQL语句语预处理能力,Java代码SQL语句原封不动搬运到数据库!!!
	PreparedStatement 存在SQL语句预处理过程,这个过程可以有效的防止一定条件的SQL注入
	Statement存在SQL注入问题,而PreparedStatemen可以有效的避免SQL注入

墙裂推荐使用PreparedStatement
	1. PreparedStatement操作性更强
	2. PreparedStatement安全性更高

代码演示

package com.qfedu.a_statement;

import util.JdbcUtil;

import java.sql.*;

/**
 * 使用Statement和PreparedStatement完成Select操作
 *
 * @author Anonymous 2020/3/24 11:07
 */
public class Demo1 {
    private static String userName = "逗比匿名君";
    private static String password = "fdafdsafdsa' or 1=1 -- ";

    public static void main(String[] args) {
        /*
        Statement是一个SQL语句搬运工对象,不存在SQL语句语预处理能力,Java代码SQL语句原封不动搬运到数据库!!!
        PreparedStatement 存在SQL语句预处理过程,这个过程可以有效的防止一定条件的SQL注入
        */
        statementSelect();
        preparedStatementSelect();
    }

    public static void statementSelect() {
        ResultSet resultSet = null;
        Statement statement = null;
        Connection connection = null;


        try {
            connection = JdbcUtil.getConnection();
            statement = connection.createStatement();

            // SQL语句准备
            String sql = "select * from user where userName = '" + userName + "' and password = '" + password + "'";
            /*
            select * from user where userName = '逗比匿名君' and password = 'fdafdsafdsa' or 1=1 -- '
            */

            resultSet = statement.executeQuery(sql);

            if (resultSet.next()) {
                System.out.println("Statement 登陆成功");
            } else {
                System.out.println("Statement 登陆失败");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(connection, statement, resultSet);
        }
    }

    public static void preparedStatementSelect() {
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;
        Connection connection = null;
        try {
            connection = JdbcUtil.getConnection();
            String sql = "select * from user where userName = ? and password = ?";

            preparedStatement = connection.prepareStatement(sql);

            preparedStatement.setObject(1, userName);
            preparedStatement.setObject(2, password);

            resultSet = preparedStatement.executeQuery();

            if (resultSet.next()) {
                System.out.println("PreparedStatement 登陆成功");
            } else {
                System.out.println("PreparedStatement 登陆失败");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(connection, preparedStatement, resultSet);
        }
    }
}

九,BaseDao封装

9.1 需求和问题

需求
	1. 完成通用的更新方法,满足insert,update,delete操作
	2. 完成通用的查询方法,满足select

问题
	1. 数据库连接对象获取[解决]
	2. 资源关闭[解决]
	3. PreparedStatement参数赋值过程【未解决】
		a. 参数个数
			PreparedStatement预处理SQL语句?有多少个
		b. 赋值顺序‘
			参数传入方式和顺序问题
	4. 查询结果集解析过程【未解决】
		a. 返回值是一个List<目标数据类型>集合
		b. 返回值是一个Object数组

9.2 元数据

三种元数据
	数据库元数据
		通过java.sql.Connection获取对应的元数据
		
	SQL语句元数据
		通过java.sql.PreparedStatement获取对应的元数据
		
	数据库结果集元数据
		通过java.sql.ResultSet获取对应的元数据
		
MetaData
【重点】
	1. SQL语句元数据,参数元数据其中的参数个数 对应 ? 占位符个数
	2. 结果集元数据中的字段个数,和对应当前字段下标的字段名字

代码演示

package com.qfedu.b_matedata;

import org.junit.Test;
import util.JdbcUtil;

import java.sql.*;

/**
 * 元数据测试
 *
 * @author Anonymous 2020/3/24 14:39
 */
public class TestMetaData {
    // 数据库元数据演示
    @Test
    public void databaseMetaData() throws SQLException {
        Connection connection = JdbcUtil.getConnection();

        // 数据库元数据
        DatabaseMetaData metaData = connection.getMetaData();

        System.out.println("UserName:" + metaData.getUserName());
        System.out.println("DriverVersion:" + metaData.getDriverVersion());
        System.out.println("DriverName:" + metaData.getDriverName());
        System.out.println("URL:" + metaData.getURL());

        System.out.println(connection);
    }

    @Test
    public void sqlMetaData() throws SQLException {
        // 获取数据库连接
        Connection connection = JdbcUtil.getConnection();

        // 准备SQL语句
        // ? 表示SQL语句参数占位符!!!
        String sql = "insert into nzgp2001.user(id, userName, password) VALUE (?,?,?)";

        // 预处理SQL语句,获取PreparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();

        // [重点]
        System.out.println("当前SQL语句的参数个数:"  + parameterMetaData.getParameterCount());
        JdbcUtil.close(connection, preparedStatement);
    }

    @Test
    public void resultMetaData() throws SQLException {
        // 获取数据库连接
        Connection connection = JdbcUtil.getConnection();

        // 准备SQL语句
        // ? 表示SQL语句参数占位符!!!
        String sql = "select * from user";

        // 预处理SQL语句,获取PreparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        ResultSet resultSet = preparedStatement.executeQuery();

        ResultSetMetaData metaData = resultSet.getMetaData();

        // 查询结果集的字段个数
        int columnCount = metaData.getColumnCount();

        while (resultSet.next()) {
            for (int i = 1; i <= columnCount; i++) {
                // 字段名字
                String columnName = metaData.getColumnName(i);

                // 通过字段名从数据库结果集中读取对应字段的数据
                System.out.println(columnName + ":" + resultSet.getObject(columnName));
            }
        }
        // [重点]
        JdbcUtil.close(connection, preparedStatement, resultSet);
    }
}

9.3 BeanUtils使用

BeanUtils提供了对于符合JavaBean规范的实体类进行赋值,取值,拷贝操作的一系列方法,可以自动完成数据类型转换,方便开发者在数据交互中使用。
	所有的方法都是静态方法
	三个方法
		1. 赋值指定成员变量对应数据
			a. 符合JavaBean规范的类对象
			b. 指定成员变量的名字
			c. Object类型数据用于赋值成员变量
			
		2. 取值指定成员变量的数据
			a. 符合JavaBean规范的类对象
            b. 指定成员变量的名字
            返回值是对应当前成员变量的数据类型
			
		3. 拷贝符合JavaBean规范的两个对象数据
			a. 符合JavaBean规范的目标类对象
			b. 符合JavaBean规范的目标数据源对象		
           
        4. 真香方法,从Map双边对联中匹配赋值数据到符合JavaBean规范的类对象
        	a. 符合JavaBean规范的类对象
        	b. Map双边队列

代码演示

package com.qfedu.c_testbeanutils;

import com.qfedu.a_statement.User;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;

/**
 * BeanUtils测试
 *
 * @author Anonymous 2020/3/24 15:09
 */
public class Demo1 {
    @Test
    public void testSetProperty()
            throws InvocationTargetException, IllegalAccessException {
        User user = new User();

        // 给符合JavaBean规范的指定成员变量赋值操作
        BeanUtils.setProperty(user, "id", "123");
        BeanUtils.setProperty(user, "userName", "骚磊");
        BeanUtils.setProperty(user, "password", 123456);

        System.out.println(user);
    }

    @Test
    public void testGetProperty()
            throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        User user = new User(1, "骚磊", "2344567");

        System.out.println(BeanUtils.getProperty(user, "id"));
        System.out.println(BeanUtils.getProperty(user, "userName"));
        System.out.println(BeanUtils.getProperty(user, "password"));
    }

    @Test
    public void testCopyProperties() throws InvocationTargetException, IllegalAccessException {
        User user = new User(1, "骚磊", "2344567");
        User user1 = new User();

        System.out.println("before:" + user1);
        BeanUtils.copyProperties(user1, user);

        System.out.println("after:" + user1);
    }

    // populate
    @Test
    public void 真香() throws InvocationTargetException, IllegalAccessException {
        HashMap<String, Integer> map = new HashMap<>();

        map.put("userName", 100);
        map.put("location:", 1);
        map.put("password", 1111);
        map.put("id", 2);

        User user = new User();

        System.out.println("before:" + user);
        BeanUtils.populate(user, map);

        System.out.println("after:" + user);

    }
}

9.4 通用更新方法实现

分析:
	完成通用的更新方法,update,insert,delete操作
	权限修饰符:
		public
	返回值类型:
		int 当前SQL语句参数,数据库收到影响的行数
	方法名:
		update
	形式参数列表:
		1. String sql语句
			指定执行的SQL语句 update insert delete。。。
		2. SQL语句可能需要参数
			SQL有可能没有参数,有可能多个参数,而且参数类型都不一样!!!
			a. Object...
				Object类型的不定长参数
			b. Object[]
				所有对应当前SQL语句的参数都存储在Object类型数组中
		(String sql, Object[] parameters)

方法声明:
	public int update(String sql, Object[] parameters)

代码演示

/**
 * 通用的更新方法,需要参数是SQL语句和对应当前SQL语句的Object类型参数数组
 *
 * @param sql        String类型的SQL语句,需要执行的方法
 * @param parameters 对应当前SQL语句的参数列表。Object类型数组
 * @return SQL语句执行数据库受到影响的行数
 */
public int update(String sql, Object[] parameters) {
    int affectedRows = 0;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    
    try {
        connection = JdbcUtil.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        
        /*
        获取SQL语句参数个数!!!通过SQL语句元数据获取(ParameterMetaData)
         */
        int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
        
        /*
        parameterCount 参数个数不能为0
        parameters != null 参数数组不为null,因为存在当前方法没有参数,数组传入null
        parameterCount == parameters.length 参数个数和传入的Object类型参数数容量一致
         */
        if (parameterCount != 0 && parameters != null && parameterCount == parameters.length) {
            for (int i = 0; i < parameters.length; i++) {
                /*
                SQL语句参数下标从1开始
                数组数据下标从0开始
                 */
                preparedStatement.setObject(i + 1, parameters[i]);
            }
        }
        
        // 执行SQL语句
        affectedRows = preparedStatement.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement);
    }
    
    return affectedRows;
}

9.5 通用查询方法实现

分析:
	完成通用的查询方法,select操作
	权限修饰符:
		public
	需要声明泛型
		T ==> Type
	返回值类型:
		List<指定数据类型>
	方法名:
		query
	形式参数列表:
		1. String sql语句
			指定执行的SQL语句  Select语句
		2. SQL语句可能需要参数
			SQL有可能没有参数,有可能多个参数,而且参数类型都不一样!!!
			a. Object...
				Object类型的不定长参数
			b. Object[]
				所有对应当前SQL语句的参数都存储在Object类型数组中
		3. 告知当前SQL语句出现的查询结果对应数据类型是哪一个
			Class<T> cls
			a. 泛型T
				用于数据类型约束,传入哪一个类的.class当前T对应的就是哪一个类
			b. Class 反射对应的Class类对象
				为所欲为!!!
				有了对应类的.class字节码文件对应Class对象。可以通过反射为所欲
				为
            (String sql, Object[] parameters, Class<T> cls)

方法声明:
	public <T> List<T> query(String sql, Object[] parameters, Class<T> cls)

代码演示

/**
 * 通用的查询方法,查询cls指定数据类型,返回值是一个List集合
 *
 * @param sql        Select SQL语句
 * @param parameters 对应当前SQL语句的参数
 * @param cls        指定数据类型,同时提供Class对象,便于里用反射操作,约束泛型类型
 * @param <T>        泛型占位符
 * @return 指定数据类型的List集合,如果集合中不存在数据 size() == 0 返回null
 */
public <T> List<T> query(String sql, Object[] parameters, Class<T> cls) {
    ResultSet resultSet = null;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    
    List<T> list = new ArrayList<>();
    
    try {
        connection = JdbcUtil.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        
        /*
        获取SQL语句参数个数!!!通过SQL语句元数据获取(ParameterMetaData)
         */
        int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
        
        /*
        parameterCount 参数个数不能为0
        parameters != null 参数数组不为null,因为存在当前方法没有参数,数组传入null
        parameterCount == parameters.length 参数个数和传入的Object类型参数数容量一致
         */
        if (parameterCount != 0 && parameters != null && parameterCount == parameters.length
            for (int i = 0; i < parameters.length; i++) {
                /*
                SQL语句参数下标从1开始
                数组数据下标从0开始
                 */
                preparedStatement.setObject(i + 1, parameters[i]);
            }
        }
            
        // 执行SQL语句,得到结果集对象
        resultSet = preparedStatement.executeQuery();
        // 结果集元数据
        ResultSetMetaData metaData = resultSet.getMetaData();
        // 字段个数
        int columnCount = metaData.getColumnCount();
            
        while (resultSet.next()) {
            // 根据Class类型创建对象,对象的类型是T类型
            T t = cls.getConstructor().newInstance();
            for (int i = 1; i <= columnCount; i++) {
                // 获取字段名
                String fieldName = metaData.getColumnName(i);
                // 获取对应字段数据
                Object value = resultSet.getObject(fieldName);
                // 给符合JavaBean规范的实现类,指定成员变量fieldName,赋值value
                BeanUtils.setProperty(t, fieldName, value);
            }
            
            list.add(t);
        }
    } catch (SQLException | NoSuchMethodException | InstantiationException
            | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement, resultSet);
    }
            
    // 判断结果,如果List中没有存储数据,返回null
    return list.size() != 0 ? list : null;
}

9.6 BaseDao方法补充

9.6.1 为什么要提供该方法
完成一个查询返回值类型是Object[],并且是存储于List集合中的一种方式,实际返回值类型是 List<Object[]>
	处理的是查询数据结果无法映射到类对象中,ORM,所有的数据按照查询结果字段顺序要求从数据库读取数据保存到Object数组,为了能满足多行数据查询要求,Object数组存储到List中
9.6.2 方法分析
分析:
	权限修饰符:
		public
	返回值类型:
		List<Object[]>
	方法名:
		query
	形式参数列表:
		String sql select查询语句
		对应当前SQL语句的参数
方法声明:
	public List<Object[]> query(String sql, Object[] parameters)

代码演示

/**
 * 通用查询方法,返回值是对应字段数据的Object类型数组,并且存储于List集合
 *
 * @param sql        Select查询SQL语句
 * @param parameters 对应当前SQL语句的参数
 * @return 包含数据行数据的List<Object[]> 如果没有查询到数据,返回null
 */
public List<Object[]> query(String sql, Object[] parameters) {
    ResultSet resultSet = null;
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    List<Object[]> list = new ArrayList<>();
    try {
        connection = JdbcUtil.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        /*
        获取SQL语句参数个数!!!通过SQL语句元数据获取(ParameterMetaData)
         */
        int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
        /*
        parameterCount 参数个数不能为0
        parameters != null 参数数组不为null,因为存在当前方法没有参数,数组传入null
        parameterCount == parameters.length 参数个数和传入的Object类型参数数容量一致
         */
        if (parameterCount != 0 && parameters != null && parameterCount == parameters.length)
            for (int i = 0; i < parameters.length; i++) {
                /*
                SQL语句参数下标从1开始
                数组数据下标从0开始
                 */
                preparedStatement.setObject(i + 1, parameters[i]);
            }
        }
        resultSet = preparedStatement.executeQuery();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while (resultSet.next()) {
            Object[] values = new Object[columnCount];
            for (int i = 1; i <= columnCount; i++) {
                values[i - 1] = resultSet.getObject(i);
            }
            list.add(values);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(connection, preparedStatement, resultSet);
    }
    return list.size() != 0 ? list : null;
}
9.6.3 BaseDao优化
// Ctrl + Alt + M 光标选择代码块生成一个方法
/**
 * 类内私有化处理PreparedStatement预处理SQL语句和参数数组赋值操作
 *
 * @param preparedStatement 预处理SQL语句对应的PreparedStatement对象
 * @param parameters        对应当前SQL语句的Object类型数组
 * @throws SQLException SQL异常
 */
private void parseSqlParameter(PreparedStatement preparedStatement, Object[] parameters) throws SQLException {
    /*
    获取SQL语句参数个数!!!通过SQL语句元数据获取(ParameterMetaData)
    */
    int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
    
    /*
    parameterCount 参数个数不能为0
    parameters != null 参数数组不为null,因为存在当前方法没有参数,数组传入null
    parameterCount == parameters.length 参数个数和传入的Object类型参数数容量一致
    */
    if (parameterCount != 0 && parameters != null && parameterCount == parameters.length) {
        for (int i = 0; i < parameters.length; i++) {
            /*
            SQL语句参数下标从1开始
            数组数据下标从0开始
            */
            preparedStatement.setObject(i + 1, parameters[i]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值