熬夜总结学了一半的JDBC

一、 JDBC概述

1、JDBC定义

1、JDBC 是一种可用于,执行 SQL 语句的, JavaAPI。它由 Java 语言编写的一些类和界面组成。
2、JDBC为数据库,应用开发人员,提供了一种标准的应用程序设计接口,使开发人员可以用纯 Java 语言,编写完整的数据库应用程序。
3、 通过使用 JDBC,开发人员可以很方便地将 SQL 语句,传送给几乎任何一种数据库。
4、JDBC 是一种底层 API,这意味着它将直接调用 SQL 命令。
5、JDBC 还扩展了 Java 的功能。例如,用 Java 和 JDBC API 可以发布含有 applet 的网页。

2、数据的持久化储存

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用
在这里插入图片描述

3、JDBC体系结构

JDBC接口(API)包括两个层次:

  • 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
  • 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。在这里插入图片描述

4、JDBC程序编写步骤

在这里插入图片描述

二、数据库连接

1、数据库连接的三要素

1、Driver接口实现类
java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口
mySql的驱动:com.mysql.jdbc.Driver

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、URL

  • DBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。

  • JDBC URL的标准由三部分组成,各部分间用冒号分隔。

    • jdbc:子协议:子名称
    • 协议:JDBC URL中的协议总是jdbc
    • 子协议:子协议用于标识一个数据库驱动程序
    • 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名

    例子:jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true"

3、用户名和密码
user=root
password=123456

2、连接方式

1、不使用配置文件

public class TestConnection {
    
    
    @Test
    public void getConnection() {
    
    
        try {
    
    
            //1、连接数据库的4个基本要素
            String url = "jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true";
            String user = "root";
            String password = "123456";
            String driverName = "com.mysql.jdbc.Driver";

            //2、实例化Driver
            Class clazz = Class.forName(driverName);
            Driver driver = (Driver) clazz.newInstance();

            //3.注册驱动
            DriverManager.registerDriver(driver);

            //连接数据库
            Connection connection = DriverManager.getConnection(url, user, password);
            System.out.println(connection);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

实际上,我们可以省略这一步,或者说成简化,因为,在Driver类的源码中有这样一段代码

static {
    
    
                try {
    
    
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
    
    
                    throw new RuntimeException("Can't register driver!");
                }
            }

静态代码块,随着类的加载而执行,在Class.forName(driverName)中,我们就已经将Driver加载进了内存。
所以,我们还可以将代码,进行简化。

  @Test
    public void getConnection2(){
    
    
        try {
    
    
            //连接数据库的4个要素
            String url="jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true";
            String user="root";
            String password="123456";
            String driverName="com.mysql.jdbc.Driver";

            //加载驱动
            Class.forName(driverName);

            //数据库连接
            Connection connection = DriverManager.getConnection(url, user, password);
            System.out.println(connection);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

2、使用配置文件

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

user=root
password=123456
url=jdbc:mysql://localhost:3306/test_1?useUnicode=true&characterEncoding=utf8&usessl=true
driverClass=com.mysql.jdbc.Driver
    @Test
    public void getConnection3() {
    
    
        try {
    
    
            //加载配置文件
            InputStream is = TestConnection.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties ps = new Properties();
            ps.load(is);

            //读取配置信息
            String url=ps.getProperty("url");
            String user=ps.getProperty("user");
            String password=ps.getProperty("password");
            String driverName=ps.getProperty("driverClass");

            //加载驱动
            Class.forName(driverName);

            //连接数据库
            Connection connection = DriverManager.getConnection(url, user, password);
            System.out.println(connection);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

三、JDBC的使用

1、Statement的不安全性与sql注入问题

JDBC Statement 形式的,数据库操作,是将一个组装好的,带有数据的SQL,直接提交给,MySQL服务。是极其不安全的。

举个例子:
数据库与表设计

-- 创建数据库
CREATE DATABASE `calss`;

-- 切换到 bank 库
USE `calss`;

-- 创建表
CREATE TABLE `user_balance` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  `balance` BIGINT NOT NULL ,
  PRIMARY KEY (`id`)
)
ENGINE = INNODB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci;

插入数据

INSERT INTO `user_balance`(`name`,`balance`)VALUES('张三',100);

INSERT INTO `user_balance`(`name`,`balance`)VALUES('李四',101);
package Connection4;
import java.sql.*;
/**
 * @author shkstart
 * @create 2021-08-22 15:50
 */
public class UnsafeStatement {
    
    
    private static String user = "root";
    private static String password = "123456";
    private static String driverClass = "com.mysql.jdbc.Driver";
    private static String url = "jdbc:mysql://localhost:3306/calss?useUnicode=true&characterEncoding=utf8";

    public static void select(String name) throws ClassNotFoundException, SQLException {
    
    
        Class.forName(driverClass);
        Connection conn =  DriverManager.getConnection(url,user, password);
        Statement stmt = null;
        try {
    
    
            stmt = conn.createStatement();
            String sql = String.format("SELECT * FROM user WHERE name='%s'", name);
            System.out.println(sql);
            ResultSet resultSet = stmt.executeQuery(sql);
            while (resultSet.next()) {
    
    
                System.out.printf("id: %s, name: %s, balance: %s\n",
                        resultSet.getLong("id"),
                        resultSet.getString("name"),
                        resultSet.getLong("balance"));
            }
            resultSet.close();
        } finally {
    
    
            if (stmt != null) {
    
    
                stmt.close();
            }
            conn.close();
        }
    }

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
    
    
        select("zhangsan' OR '1'='1");
    }
}

查询结果

在这里插入图片描述

在这里,我们只是想通过name查询单条记录,如果黑客通过sql注入暴力破解密码。那那么数据库将毫无保留的,展现在黑客的面前

2、使用PreparedStatement实现增删改查

PreparedStatement,继承自Statement,但比Statement功能强大的多。

优点:

1、PreparedStatement是预编译的,比Statement速度快。
当同时要执行多条相同结构sql语句时使用,这时可以用setObject(),addBatch()和executeBatch()这几个函数。

2、可以防止sql注入。
对JDBC而言,SQL注入攻击只对Statement有效,对PreparedStatement是无效的,这是因为PreparedStatement不允许在插入时改变查询的逻辑结构

1、增

@Test
    public void insertTest(){
    
    
        Connection conn=null;
        PreparedStatement ps=null;
        try {
    
    
            // 1.读取配置文件中的4个基本信息
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
            Properties pros = new Properties();
            pros.load(is);

            String user = pros.getProperty("user");
            String password = pros.getProperty("password");
            String url = pros.getProperty("url");
            String driverClass = pros.getProperty("driverClass");

            // 2.加载驱动
            Class.forName(driverClass);

            // 3.获取连接
            conn = DriverManager.getConnection(url, user, password);

            //4、预编译sql,返回preparedStatement实例
            String sql="insert into customers(name,email,birth) values(?,?,?)";
            ps = conn.prepareStatement(sql);

            //5、填充占位符
            ps.setObject(1,"张国荣");
            ps.setObject(2,"[email protected]");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse("1000-01-01");
            ps.setDate(3,new java.sql.Date(date.getTime()));

            //6、执行操作
            ps.execute();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //7、资源的关闭
            if(ps!=null){
    
    
                try {
    
    
                    ps.close();
                } catch (SQLException throwables) {
    
    
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
    
    
                try {
    
    
                    conn.close();
                } catch (SQLException throwables) {
    
    
                    throwables.printStackTrace();
                }
            }
        }
    }

运行前
在这里插入图片描述
运行后
在这里插入图片描述

2、删

   @Test
    public void testCommonUpdate() {
    
    
        String sql="delete from customers where id>?";
        update(sql,8);
    }

    public void update(String sql, Object... args) {
    
    //sql中占位符的个数与可变形参的长度相同!
        Connection conn = null;
        PreparedStatement ps = null;
        try {
    
    
            //1.获取数据库的连接
            conn = JDBCUtils.getConnection();
            //2.预编译sql语句,返回PreparedStatement的实例
            ps = conn.prepareStatement(sql);
            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
    
    
                ps.setObject(i + 1, args[i]);//小心参数声明错误!!
            }
            //4.执行
            ps.execute();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //5.资源的关闭
            JDBCUtils.closeResouce(conn, ps);
        }
    }

删除前
在这里插入图片描述
删除后
在这里插入图片描述

3、改

  @Test
    public void updatetest() throws SQLException, IOException, ClassNotFoundException {
    
    
        Connection conn=null;
        PreparedStatement ps=null;
        try {
    
    
            //1、获取数据库连接
            conn = JDBCUtils.getConnection();
            //2、预编译sql,返回prearedStatement实例
            String sql="update customers set name=? where id =?";
            ps = conn.prepareStatement(sql);

            //3、填充占位符
            ps.setObject(1,"欢子");
            ps.setObject(2,8);
            
            //4、执行
            ps.execute();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //5、关闭资源
            JDBCUtils.closeResouce(conn,ps);
        }

    }

运行前

在这里插入图片描述

运行后
在这里插入图片描述

4、查

public class PreparedStatementList {
    
    
    @Test
    public void getList(){
    
    
    String sql="select id,name,email,birth from customers where id<?";
        List<Customer> allList = getAllList(Customer.class, sql, 8);
        allList.forEach(System.out::println);
    }
    public <T> List<T> getAllList(Class<T> clazz, String sql, Object...args){
    
    
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
    
    
            //1、读取配置文件
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);

            for (int i = 0; i <args.length ; i++) {
    
    
                ps.setObject(i+1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取结果集中的列数
            int columnCount = rsmd.getColumnCount();
            //创建集合,存放数据
            ArrayList<T> list = new ArrayList<T>();
            if(rs.next()){
    
    
                T t = clazz.newInstance();
                //处理结果集的每一列
                for (int i = 0; i <columnCount ; i++) {
    
    
                    //获取列值
                    Object columValue = rs.getObject(i + 1);
                    //获取列名
                    String columnname = rsmd.getColumnLabel(i+1);
                    //给t对象指定columnname属性
                    Field field = clazz.getDeclaredField(columnname);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                list.add(t);
            }
            return list;

        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //关闭资源
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }
}

3、操作数据库的方法总结

方法名 功能描述
java.sql.Driver JDBC 驱动程序需要实现的接口
Properties 配置文件所使用的类
forname 返回一个类
DriverManager 管理驱动
Connection 与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果
Statement 用于执行静态SQL语句并返回其生成的结果的对象
PreparedStatement 表示预编译的SQL语句的对象
ResultSet 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。 ResultSet对象保持一个光标指向其当前的数据行。 最初,光标
getMetaData 检索一个ResultSetMetaData对象,其中包含有关执行此PreparedStatement对象时将返回的ResultSet对象的列的信息

猜你喜欢

转载自blog.csdn.net/weixin_46457946/article/details/119781227