[Java]JDBC学习笔记(尚硅谷康师傅JDBC) (一)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: [Java]JDBC学习笔记(尚硅谷康师傅JDBC)(一)

🥽 视频链接及资源下载

视频:【尚硅谷JDBC核心技术视频教程(康师傅带你一站式搞定jdbc)】

资源下载:【百度网盘】

提取码:1234


🥽 JDBC简介

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这个类库可以以一种标准的方法、方便地访问数据库资源

JDBC驱动为对JDBC中定义的接口的实现。不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

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

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

JDBC API 是一系列的接口,它使得应用程序能够进行数据库连接,执行SQL语句,并且得到返回结果。Java程序员只需要面向这套接口编程即可。


🥽 JDBC程序访问数据库步骤

  1. 导入对应的包和驱动
  2. 创建连接数据库的驱动对象
  3. 加载注册连接数据库的驱动
  4. 通过驱动获取连接数据库的对象
  5. 通过连接数据库的对象获取执行SQL语句的对象
  6. 执行SQL语句得到结果集对象
  7. 关闭对应的对象和连接

🥽 获取数据库连接

🌊 导入jar包

【MySQL JDBC下载官网】

压缩包下载到本地后解压,在IDE项目根目录下新建lib目录

把下载的压缩包解压后的jar包复制到lib目录下

复制完成后鼠标右键jar包,添加为库

🌊 Driver 接口

java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现

在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现

  • Oracle的驱动:oracle.jdbc.driver.OracleDriver
  • mySql的驱动: com.mysql.jdbc.Driver

🌊 JDBC URL

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

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

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

  • 对于 Oracle 数据库连接,采用如下形式:
    jdbc:oracle:thin:@localhost:1521:atguigu
  • 对于 SQLServer 数据库连接,采用如下形式:
    jdbc:microsoft:sqlserver//localhost:1433; DatabaseName=sid
  • 对于 MYSQL 数据库连接,采用如下形式:
    jdbc:mysql://localhost:3306/atguigu

🌊 方式1:

如有报错后面的解决方法可能会有帮助

@Test
    public void testConnection1() throws Exception {
        // 获取 Driver 驱动对象
        Driver driver = new com.mysql.jdbc.Driver();
        // 配置获取连接数据对象需要的信息
        // url:http://localhost:8080/gmall/keyboard.jpg
        // jdbc:mysql:协议
        // localhost:ip地址
        // 3306:默认mysql的端口号
        // test:test数据库
        // 字符集与数据库不一致  ?characterEncoding=utf8  设置字符集
        String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8";
        // 将用户名和密码封装到Properties对象中
        Properties info = new Properties();
        info.setProperty("user", "root");
        info.setProperty("password", "123456");
        // 通过驱动对象根据配置信息获取连接数据库的对象
        Connection connection = driver.connect(url, info);
        // 打印连接对象,查看是否连接成功
        System.out.println(connection);
    }

🌊 方式2:

对方式一的迭代:在如下的程序中不出现第三方的api,使得程序具有更好的可移植性

@Test
    public void testConnection2() throws Exception {
        // 获取 Driver 驱动对象
        // 使用反射
        // 获取连接数据库驱动的类
        Class clazz = Class.forName("com.mysql.jdbc.Driver");
        // 通过连接驱动的类创建连接驱动Driver对象
        Driver driver = (Driver) clazz.newInstance();
        // 数据库的url
        String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8";
        // 连接数据库需要的用户信息配置对象
        Properties info = new Properties();
        info.setProperty("user", "root");
        info.setProperty("password", "123456");
        // 获取连接数据库的对象
        Connection connection = driver.connect(url, info);
        System.out.println(connection);
    }

🌊 方式3:

使用DriverManager替换Driver

DriverManager 类是驱动程序管理器类,负责管理驱动程序

  • DriverManager.registerDriver(com.mysql.jdbc.Driver);
  • 通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实
    例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会
    调用 DriverManager.registerDriver() 方法来注册自身的一个实例
@Test
    public void testConnection3() throws Exception {
        // 获取 Driver 驱动对象
        // 使用反射
        Class clazz = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) clazz.newInstance();
        // 连接数据库的配置信息
        String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8";
        String user = "root";
        String password = "285013";
        // 通过 DriverManager 注册驱动
        DriverManager.registerDriver(driver);
        // 通过 DriverManager 获取连接对象
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
    }

🌊 方式4:

可以只是加载驱动,不用显示的注册驱动,因为在加载驱动时候就会进行驱动的注册。

@Test
    public void testConnection4() throws Exception {
        // 连接需要的三个基本信息
        String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8";
        String user = "root";
        String password = "123456";
        // 使用反射加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 相较于方式3,省略了如下操作
        // Driver driver = (Driver) clazz.newInstance();
        // 通过 DriverManager 注册驱动
        // DriverManager.registerDriver(driver);
        // 通过驱动管理获取数据库的连接
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
    }

可以省略的原因:

在mysql的Driver实现类中,声明了如下的操作:

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

🌊 方式5(final版):

将数据库连接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接

此种方式的好处:

  1. 实现了数据与代码的分离。实现了解耦
  2. 如果需要修改配置文件信息,可以避免程序重新打包。

配置文件:

配置文件在src下,等号两边不能有空格!!!!!!

user=root
password=123456
url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8
driverClass=com.mysql.jdbc.Driver

@Test
    public void testConnection5() throws Exception {
        // 通过反射获取读取配置文件的输入流
        InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
        // 配置信息对象
        Properties properties = new Properties();
        properties.load(is); // 加载配置信息
        // 得到配置信息
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");
        // 加载驱动
        Class.forName(driverClass);
        // 获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println(connection);
    }

🌊 连接报错:

💦 Cause: java.sql.SQLException: Unknown initial character set index ‘255’ received from server.

Cause: java.sql.SQLException: Unknown initial character set index '255' received from server.

【解决方法博客链接】


🥽 使用Statement操作数据表的弊端【了解】

使用Statement的弊端:

  • 需要拼写sql语句
  • 存在SQL注入的问题

如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令

User 类:

public class User {
    private String user;
    private String password;
    public User() {
    }
    public User(String user, String password) {
        super();
        this.user = user;
        this.password = password;
    }
    @Override
    public String toString() {
        return "User [user=" + user + ", password=" + password + "]";
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

StatementTest 测试类:

public class StatementTest {
    // 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
    //如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement
    @Test
    public void testLogin() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String user = scanner.nextLine();
        System.out.print("请输入密码:");
        String password = scanner.nextLine();
        //SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'
        String sql = "SELECT user,password FROM user_table WHERE user = '"+ user +"' AND password = '"+ password +"'";
        User returnUser = get(sql, User.class);
        if(returnUser != null){
            System.out.println("登录成功");
        }else{
            System.out.println("用户名不存在或密码错误");
        }
    }
    // 使用Statement实现对数据表的查询操作
    public <T> T get(String sql, Class<T> clazz) {
        T t = null;
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            // 1.加载配置文件
            InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties pros = new Properties();
            pros.load(is);
            // 2.读取配置信息
            String user = pros.getProperty("user");
            String password = pros.getProperty("password");
            String url = pros.getProperty("url");
            String driverClass = pros.getProperty("driverClass");
            // 3.加载驱动
            Class.forName(driverClass);
            // 4.获取连接
            conn = DriverManager.getConnection(url, user, password);
            st = conn.createStatement();
            rs = st.executeQuery(sql);
            // 获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            // 获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            if (rs.next()) {
                t = clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    // //1. 获取列的名称
                    // String columnName = rsmd.getColumnName(i+1);
                    // 1. 获取列的别名
                    String columnName = rsmd.getColumnLabel(i + 1);
                    // 2. 根据列名获取对应数据表中的数据
                    Object columnVal = rs.getObject(columnName);
                    // 3. 将数据表中得到的数据,封装进对象
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(t, columnVal);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

当输入的用户名为:1' or 密码为:=1 or '1' = '1就会造成SQL注入问题

因为此时进行字符串拼接完成后,需要执行的SQL语句变为:

SELECT user,password 
FROM user_table 
WHERE user = '1' or ' AND password = '=1 or '1' = '1'

判断是否成立的条件有原来的两个条件相与变为三个条件相或:

user = '1' 
or  ' AND password = '=1 
or  '1' = '1'

其中 '1' = '1'一定成立,此时不管账号密码是否正确一定可以登录系统。

以上就是SQL注入问题。


🥽 PreparedStatement

🌊 PreparedStatement 实现表数据的添加

package preparedstatement;
import org.junit.Test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
public class PreparedStatementUpdate {
  // 向customers表中添加一条数据
    @Test
    public void testInsert() throws Exception {
    // 通过类加载器获取输入流
        InputStream is = PreparedStatementUpdate.class.getClassLoader().getResourceAsStream("jdbc.properties");
    // 创建数据库配置信息对象
        Properties properties = new Properties();
        properties.load(is); // 加载配置信息
    // 得到配置信息
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");
    // 加载驱动
        Class.forName(driverClass);
    // 获取数据库的连接对象
        Connection conn = DriverManager.getConnection(url, user, password);
    // 预编译SQL语句,返回PreparedStatement实例对象
    // sql语句
    // ? 为占位符
        String sql = "INSERT INTO customers(name, email, birth) VALUES (?, ?, ?);";
    // 通过连接对象获取 PreparedStatement 对象
        PreparedStatement ps = conn.prepareStatement(sql);
    // 填充占位符 第一个参数为填充占位符的索引(从1开始), 第二个参数为填充到占位符的数据
        ps.setString(1, "ZS");
        ps.setString(2, "zs@qq.com");
        ps.setString(3, "1000-10-10");
    // 执行操作
        ps.execute();
    // 资源的关闭
        ps.close();
        conn.close();
    }
}
SELECT * FROM customers
WHERE name = 'ZS';

🌊 数据库连接与关闭操作的封装

package utils;
import preparedstatement.PreparedStatementUpdate;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
 * 操作数据库的工具类
 */
public class JDBCUtils {
    /**
     * 获取数据库的连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
    // 通过类加载器获取输入流
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
    // 创建数据库配置信息对象
        Properties properties = new Properties();
        properties.load(is); // 加载配置信息
    // 得到配置信息
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");
    // 加载驱动
        Class.forName(driverClass);
    // 获取数据库的连接对象
        Connection conn = DriverManager.getConnection(url, user, password);
    // 返回连接对象
        return conn;
    }
    /**
     * 关闭资源操作
     * @param connection
     * @param ps
     */
    public static void closeResource(Connection connection, Statement ps) {
      // 如果存在Statement或Statement的子类对象,进行关闭操作
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        // 进行数据库的关闭
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
Java 关系型数据库 数据库连接
JDBC:Java与数据库的“黄金搭档”,为何它如此重要?
JDBC:Java与数据库的“黄金搭档”,为何它如此重要?
46 8
|
2月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
2月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
1月前
|
Java 数据安全/隐私保护
java学习笔记(基础习题)
java学习笔记(基础习题)
32 0
|
1月前
|
Java 程序员 开发工具
java学习笔记
java学习笔记
34 0
|
1月前
|
SQL Java 数据库连接
如何在 Java 脚本中有效地使用 JDBC
如何在 Java 脚本中有效地使用 JDBC
16 0
|
2月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
|
3月前
|
SQL Java 关系型数据库
探索Java数据库连接的奥秘:JDBC技术全攻略
探索Java数据库连接的奥秘:JDBC技术全攻略
56 8
|
3月前
|
SQL Java 数据库连接
JDBC之旅:从陌生到熟悉的Java数据库连接之路
JDBC之旅:从陌生到熟悉的Java数据库连接之路
28 8
|
3月前
|
SQL Java 数据库连接
Java开发者必知:JDBC连接数据库的“三大法宝”
Java开发者必知:JDBC连接数据库的“三大法宝”
33 7