JDBC

JDBC介绍

Java Database Connectivity
    Java规定的数据库连接接口,SUN公司提供,为了简化程序员操作数据库的过程。
SUN公司要求数据库提供商,按照JDBC API接口规范,完成对应Java程序的数据连接操作,
规范jar包,并且提供对应的操作方法。
    JDBC接口中核心的内容:
        java.sql.*
        javax.sql.*

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

1.确定连接的数据库所在网络地址和对应操作哪一个数据库,这里需要使用一个符合JDBC规范的URL
    jdbc:mysql://localhost:3306/tbName
    含义:
        jdbc:目前操作数据库的主协议
        mysql:主协议
        localhost:数据库服务器所在的网络地址
        3306:数据库默认端口号
        tbName:当前URL连接操作对应数据库是哪一个
        JDBC是第三方提供的内容,所以要获取对应的jar包
    
2.用户名
3.密码

JDBC连接MySQL数据库

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

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        /*
        1.加载驱动
        */
        Class.forName("com.mysql.jdbc.Driver");

        /*
            准备必要的数据
         */
        String url = "jdbc:mysql://localhost:3306/wcc?useSSL=true";
        String user = "root";
        String password = "10086";

        /*
            3.获取数据库连接
         */
        Connection connection = DriverManager.getConnection(url,user,password);

        System.out.println(connection);
        /*
            4.关闭资源
         */
        connection.close();
    }
}


//显示结果
//com.mysql.jdbc.JDBC4Connection@7dc36524

如何导入jar包

打开IDEA

点击左上角的File ==> Project Structure

点击左边栏中的 Modules

之后,点击Dependencies

点击最右边的 + 号,选择第一个 JAR

然后选择导入的jar包路径,然后选择该jar包

就OK了

java数据地址范围 java数据库地址如何知道_SQL

JDBC核心API

 

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

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


interface java.sql.Statement 数据库SQL语句搬运工对象
    int executeUpdate(String sql);
    执行数据库修改数据,insert,update,delete...返回值时int类型,是当前SQL语句搬运到数据库执行
    之后,数据库运行对于当前操作受到影响的行数
    java.sql.ResultSet executeQuery(String sql);
    执行数据库查询语句,select操作,执行的结果是一个java.sql.ResultSet结果集对象,当前操作never null
    interface java.sql.PrepareStatement 数据库SQL语句[预处理]搬运工对象
        PreparedStatement extends java.sql.Statement
    int executeUpdate();
    执行数据库修改数据,insert,update,delete...处理的SQL语句是在创建PrepareStatement对象过程预处理的SQL语句,返回值时int类型,是当前SQL语句搬运到数据库执行
    java.sql.ResultSet executeQuery();
    
    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,直接返回false

Statement操作SQL语句

Statement插入SQL数据操作

public class StatementTest {
    @Test
    public void testInsert(){
        Statement statement = null;
        Connection connection = null;

       //加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/wcc?useSSL=true";
            String user = "root";
            String password = "131477";
            //获取数据库连接
            connection = DriverManager.getConnection(url,user,password);
            //准备statement对象
            statement = connection.createStatement();
            //准备SQL语句
            String sql = "insert into wcc.student (age,name) values (22,'jkkkkk')";
            int affectedRows = statement.executeUpdate(sql);

            System.out.println("影响的行数:" + affectedRows);

            statement.close();
            connection.close();
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}
//显示结果:
//影响的行数:1

Statement修改SQl数据操作

@Test
    public void testUpdate(){
        try {
            Statement statement = null;
            Connection connection = null;
            //加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //准备必要的连接数据
            String url = "jdbc:mysql://localhost:3306/wcc?useSSL=true";
            String user = "root";
            String password = "131477";
            //获取数据库连接
            connection = DriverManager.getConnection(url,user,password);
            //获取Statement对象
            statement = connection.createStatement();
            //准备sql语句
            String sql = "update wcc.student set age = 10086 where name = 'wcc'";
            //执行sql语句
            int affectedRows = statement.executeUpdate(sql);
            System.out.println("影响到的行数:" + affectedRows);
            connection.close();
            statement.close();
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}
//显示结果:
//影响到的行数:1

 

JDBC高级

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();
        }
    }
}

JDBC工具类封装

需要完成的内容:
    1.数据库连接对象java.sql.Connection获取过程
    2.关闭资源
JDBC工具类
    1.所有的方法都是static修饰的静态方法
    2.需要考虑自动加载过程,完成一些必要数据的自动处理
        url
        driver
        user    
        password
    3.所需数据库连接条件保存到文件中
    4.关闭方法提供多种多样组合方法
# 当前JDBC连接所需的驱动
driverClass=com.mysql.jdbc.Driver

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

# 用户名
user=root

# 密码
password=123456
package util;

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的使用

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);
    }
}

PreparedStatement修改数据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);
    }
}

PreparedStatement删除数据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);
    }
}

PreparedStatement查询数据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);
    }
}

BaseDao封装

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

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

元数据

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

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);

    }
}

通用更新方法实现

分析:
    完成通用的更新方法,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;
}

通用查询方法实现

分析:
	完成通用的查询方法,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;
}