03-JDBC

打印 上一主题 下一主题

主题 873|帖子 873|积分 2619

1、JDBC简介

1.1、客户端操作MySQL数据库的方式


  • 使用DOS命令行方式
  • 使用第三方客户端来访问MySQL:SQLyog、Navicat、....
  • 通过程序来访问MySQL数据库

    • 而通过Java来访问MySQL数据库,就是JDBC的概念


1.2、JDBC的概念


  • 什么是JDBC

    • Java Data Base Connectivity:Java数据库连接

  • JDBC作用

    • 通过JDBC可以让Java程序操作数据库

  • JDBC本质

    • 官方(SUN)公司定义的一套操作所有关系型数据库的规则,即接口(API)
    • 各个数据库厂商去实现这套接口,提供数据库驱动jar包
    • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

  • JDBC的好处

    • 只需要会调用JDBC接口中的方法即可,使用简单
    • 使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库

2、JDBC API详解

2.1、JDBC四个核心对象


  • JDBC的使用步骤

    • 注册驱动
    • 获取数据库连接
    • 获取SQL语句对象
    • 执行SQL语句并返回结果
    • 处理结果
    • 释放资源

  • JDBC四个核心对象



  • JDBC交互图



2.2、JDBC注册驱动


  • Java程序需要通过数据库驱动才能连接到数据库,因此需要注册驱动

    • 在注册驱动之前需要先导入驱动的Jar包


  • JDBC注册驱动

    • java.sql.DriverManager类用于注册驱动。提供如下方法注册驱动

        1. static void registerDriver(Driver driver)        // 向DriverManager 注册给定驱动程序
        复制代码


  • 示例代码

      1. public class Demo01 {
      2.   public static void main(String[] args) throws Exception {
      3.             // 注册驱动
      4.             DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      5.   }
      6. }
      复制代码

  • 提示

    • MySQL 5 之后的驱动包,可以省略注册驱动的步骤
    • 自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类

2.3、获取Connection连接


  • Connection 介绍

    • 表示Java程序与数据库之间的连接,只有拿到Connection才能操作数据库
    • DriverManager类中的静态方法描述static Connection getConnection(String url, String user, String password)连接到给定数据库URL,并返回连接
    • 参数说明

      • String url:连接数据库的URL,用于说明数据库的位置
      • String user:数据库的账号
      • String password:数据库的密码

    • 连接数据库的URL地址格式

      • 协议名:子协议://服务器名或IP地址:端口号/数据库名

    • MySQL写法

      • jdbc:mysql://localhost:3306/day03

    • 如果是本地服务器,端口号是默认的3306,则可以简写

      • jdbc:mysql:///day03


2.4、获取Statement对象


  • 在java.sql.Connection接口中有如下方法获取到Statement对象

      1. Statement createStatement()        // 创建一个Statement对象来将SQL语句发送到数据库
      复制代码

  • 代码案例

      1. // 1.注册驱动
      2. // 2.获取连接
      3. Connection conn = DriverManager.getConnection("jdbc:mysql:///day03", "root", "123");
      4. // 3.获取Statement
      5. Statement stmt = conn.createStatement();
      复制代码

3、JDBC实现对单表数据增加、删除、修改


  • 我们要对数据库进行增、删、改、查,需要使用Statement对象来执行SQL语句
  • Statement的API介绍

      1. ResultSet executeQuery(String sql)
      2. // 用于执行查询语句; 返回查询到的结果集
      3. int executeUpdate(String sql)
      4. // 用于执行除查询外的SQL; 返回影响的行数
      复制代码

4、JDBC获取数据(Result类)

4.1、ResultSet的原理


  • ResultSet用于保存执行查询SQL语句的结果。我们不能一次性去除所有的数据,需要一行一行的去除
  • ResultSet内部有一个指针,记录获取到哪行数据

    • 获取查询结果
      1. boolean   next():
      2.   /*
      3.   (1) 将光标从当前位置向前移动一行
      4.   (2)判断当前行是否为有效行
      5. 返回值:
      6.   true:有效行,当前行有数据
      7.   false:无效行,当前行没有数据
      8. */
      复制代码
    • 应用案例

        1. while (rs.next()) {
        2.         rs.getXxx(字段名); // 取出数据
        3. }
        复制代码


4.2、ResultSet获取数据的API


  • ResultSet获取数据的API是有规律的get后面加数据类型。我们统称getXXX()
  • 方法名说明boolean getBoolean(String columnLabel)获取boolean值byte getByte(String columnLabel)获取byte值double getDouble(String columnLabel)获取double值int getInt(String columnLabel)获取int值long getLong(String columnLabel)获取long值String getString(String columnLabel)获取String值
4.3、ResultSet的getXXX方法与MySQL中数据类型的对应



  • 注意

    • 这只是一个建议,不按这个表的对应关系也可以,只要数据类型可以自动转换。如:int类型,使用String去取,也是可以的。但如果是String类型,使用int去取就不行

5、JDBC事务处理


  • JDBC操作银行转账的事务

    • Connection接口中与事务有关的方法


  • 使用步骤

    • 1.注册驱动
    • 2.获取连接
    • 3.开启事务
    • 4.获取Statement对象
    • 5.执行SQL语句
    • 6.提交或者回滚事务
    • 7.关闭资源

  • demo

      1. package _02MySQL.Day03_JDBC.demo05_事务处理_重点;
      2. import java.sql.Connection;
      3. import java.sql.DriverManager;
      4. import java.sql.SQLException;
      5. import java.sql.Statement;
      6. /**
      7. * JDBC事务处理
      8. *
      9. * 数据准备:
      10. *      CREATE TABLE tb_account (
      11. *                 id INT PRIMARY KEY AUTO_INCREMENT,
      12. *                 NAME VARCHAR(10),
      13. *                 balance DOUBLE
      14. *      );
      15. *
      16. *-- 添加数据
      17. * INSERT INTO tb_account (NAME, balance) VALUES ('张三', 1000), ('李四', 1000);
      18. */
      19. public class Demo05 {
      20.     public static void main(String[] args) throws SQLException{
      21.         // 1. 注册驱动
      22.         Connection connection = null;
      23.         try {
      24.             // 2. 获取数据库连接
      25.             connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123");
      26.             // 3. 开启事务
      27.             connection.setAutoCommit(false);    // 关闭自动提交
      28.             // 4. 获取Statement对象
      29.             Statement statement = connection.createStatement();
      30.             // 5. 执行SQL
      31.             String sql1 = "update tb_account set balance = balance - 500 where name = '张三'";
      32.             String sql2 = "update tb_account set balance = balance + 500 where name = '李四'";
      33.             statement.executeUpdate(sql1);
      34.             statement.executeUpdate(sql2);
      35.             // 模拟失败
      36.             // ...
      37.             // 6. 提交事务
      38.             System.out.println("提交事务!");
      39.             connection.commit();
      40.         } catch (Exception e) {
      41.             // 6. 有异常,事务回滚
      42.             if (connection != null) {
      43.                 connection.rollback();
      44.             }
      45.         }finally {
      46.             // 7. 关闭资源
      47.             if (connection != null) {
      48.                 connection.close();
      49.             }
      50.         }
      51.     }
      52. }
      复制代码

6、JDBC实现用户登录


  • 模拟用户输出账号和密码登录网站
  • 案例分析

    • 1.使用数据库中保存用户的账号和密码
    • 2.让用户输入账号和密码
    • 3.使用SQL根据用户的账号和密码去数据库查询数据
    • 4.如果查询到数据,说明登录成功
    • 5.如果查询不到数据,说明登录失败

  • Demo

      1. package com.itheima.demo06_JDBC实现用户登录;
      2. import java.sql.*;
      3. import java.util.Scanner;
      4. /**
      5. * JDBC实现用户登录案例
      6. */
      7. public class Demo06 {
      8.     public static void main(String[] args) throws SQLException {
      9.         Scanner scanner = new Scanner(System.in);
      10.         String userName = null;
      11.         String password = null;
      12.         // 1. 接收用户名
      13.         while (true) {
      14.             System.out.println("请输入用户名:");
      15.             String line = scanner.nextLine();
      16.             if ("".equals(line)) {
      17.                 continue;
      18.             }
      19.             userName = line;
      20.             break;
      21.         }
      22.         // 2.接收用户密码
      23.         while (true) {
      24.             System.out.println("请输入密码:");
      25.             String line = scanner.nextLine();
      26.             if ("".equals(line)) {
      27.                 continue;
      28.             }
      29.             password = line;
      30.             break;
      31.         }
      32.         // 3.根据用户名和密码,查询用户
      33.         String sql = "SELECT * FROM USER WHERE NAME = '" + userName + "' AND PASSWORD = '" + password + "';";
      34.         String jdbcUrl = "jdbc:mysql://localhost:3306/day03";
      35.         Connection conn = DriverManager.getConnection(jdbcUrl, "root", "root");
      36.         Statement stmt = conn.createStatement();
      37.         ResultSet rs = stmt.executeQuery(sql);
      38.         // 4.根据查询结果,判断是否登录成功
      39.         // 4.1 如果查询到数据,显示登录成功
      40.         if (rs.next()) {
      41.             System.out.println("登录成功!欢迎您," + userName);
      42.         } else {
      43.             // 4.2 如果查询不到数据,显示登录失败
      44.             System.out.println("登录失败!用户名或密码错误...");
      45.         }
      46.     }
      47. }
      复制代码

7、SQL注入攻击

7.1、SQL注入问题


  • 在我们前面JDBC实现登录案例中,当我们输入以下密码的时候,可以发现账号和密码都不对竟然登录成功了!

      1. 请输入用户名:
      2. hehe
      3. 请输入密码:
      4. a'or'1'='1
      复制代码

  • 字符串拼接,把输入的字符串全都将其视为sql语句,导致statement对象查询直接为真,条件不起作用!
  • 问题分析

      1. "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";
      2. // 将用户输入的账号密码拼接后
      3. "SELECT * FROM user WHERE name='hehe' AND password='a'or'1'='1';"
      复制代码

  • SQL注入攻击的原理

    • 按照正常道理来说,在密码处输入的所有内容,都应该认为是密码的组成
    • 但是现在Statement对象在执行sql语句时,将密码的一部分内容当作查询条件来执行了。

7.2、解决SQL注入


  • PreparedStatement预编译执行者对象

    • 预编译:SQL语句子在执行前就已经编译好了,执行速度更快
    • 安全性更高:没有字符串拼接的SQL语句,所以避免SQL注入的问题
    • 代码的可读性更好,是因为没有字符串拼接

  • PreparedStatement使用

    • SQL语句中的参数使用?作为参为辐
    • 给?占位符赋值

  • 设置参数

    • setXxx(参数1,参数2):Xxx代表数据类型
    • 参数1:第几个?(编号从1开始)
    • 参数2:?的实际参数

  • 执行SQL语句

    • int executeUpdate()

      • 执行insert、update、delete语句

    • ResultSet executeQuery()

      • 执行select语句


  • demo

      1. String sql = "SELECT * FROM USER WHERE NAME=? AND PASSWORD=?;";
      2. PreparedStatement pstmt = conn.prepareStatement(sql);
      3. pstmt.setString(1, “zhangsan”);
      4. pstmt.setString(2, “6666”);
      复制代码


7.3、使用PreparedStatement改写登录案例


    1. package _02MySQL.Day03_JDBC.demo08_PreparedStatement改写登录案例;
    2. import java.sql.*;
    3. import java.util.Scanner;
    4. /**
    5. * PreparedStatement改写登录案例
    6. */
    7. public class Demo08 {
    8.     public static void main(String[] args) throws SQLException {
    9.         // 1. 注册驱动
    10.         // 2. 获取连接
    11.         Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123");
    12.         // 3. 获取PreparedStatement对象
    13.         String sql = "select * from day03.user where phoneNumber = ? and password = ?";
    14.         PreparedStatement preparedStatement = connection.prepareStatement(sql);
    15.         // 准备参数
    16.         Scanner scanner = new Scanner(System.in);
    17.         String phoneNumber = null;
    18.         String password = null;
    19.         do {
    20.             System.out.print("请输入账号:");
    21.             phoneNumber = scanner.nextLine();
    22.         } while ("".equals(phoneNumber));
    23.         do {
    24.             System.out.print("请输入密码: ");
    25.             password = scanner.nextLine();
    26.         } while ("".equals(password));
    27.         // 4. 执行SQL语句
    28.         preparedStatement.setString(1, phoneNumber);
    29.         preparedStatement.setString(2, password);
    30.         ResultSet resultSet = preparedStatement.executeQuery();
    31.         // 5. 处理结果
    32.         if (resultSet.next()) {
    33.             System.out.println("欢迎您,尊敬的" + phoneNumber + "用户");
    34.         }else {
    35.             System.out.println("账号或密码错误!");
    36.         }
    37.         // 6. 释放资源
    38.         connection.close();
    39.     }
    40. }
    复制代码
8、JDBC连接池

8.1、常规数据库连接的问题


  • JDBC访问数据库的步骤

    • 创建数据库连接
    • 运行SQL语句
    • 关闭连接

  • 上述动作每次数据库访问都会执行这样的重复动作
  • 而每次创建数据库连接的问题

    • 获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源。这样数据库连接对象的使用率低

8.2、数据库连接池简介


  • 现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗。数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率
  • 连接池的概念

    • 连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的

  • 连接池的原理

    • 1.启动连接池,连接池就会初始化一些连接
    • 2.当用户需要使用数据库连接,直接从连接池中取出
    • 3.当用户使用完连接,会将连接重新放回连接池中

  • 连接池的好处

    • 连接池中保存一些连接,这些连接可以重复使用,降低数据资源的消耗

8.3、常用连接池的介绍


  • javax.sql.DataSource表示数据库连接池,也是JDK中提供的一个接口,没有具体的实现,它的实现由连接池的厂商去实现。我们只需要学习这个工具如何使用

      1. public interface DataSource{
      2.           Connection getConnection();
      3.   ...
      4. }
      复制代码

  • 常用的连接池实现组件有以下这些

    • 阿里巴巴-德鲁伊Druid连接池

      • Druid是阿里巴巴开源平台上的一个项目

    • C3P0是一个开源的连接池,目前使用它的开源项目有Hibernate,Spring等
    • DBCP(DataBase Connection Pool)数据库连接池,是Tomcat使用的连接池组件

8.3、Druid连接池简介


  • Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过了其他数据连接池,同时加入了日志监控,可以很好的监控数据库连接池和SQL的执行情况。

    • Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产大环境部署的严苛考验

  • Druid常用的配置参数

    • 方法名说明initialSize刚启动连接池时,连接池中包含连接的数量maxActive连接池中最多可以放多少个连接maxWait获取连接时最大等待时间,单位毫秒

  • com.alibaba.druid.pool.DruidDataSourceFactory类创建连接池的方法

      1. public static DataSource createDataSource(Properties properties) // 创建一个连接池,连接池的参数使用properties中的数据
      复制代码

  • 我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。Druid连接池的配置文件名称随便,放到src目录下面方便加载

    • druid.properties文件内容
      1. driverClassName=com.mysql.jdbc.Driver
      2. url=jdbc:mysql://127.0.0.1:3306/day17
      3. username=root
      4. password=root
      5. initialSize=5
      6. maxActive=10
      7. maxWait=3000
      复制代码

8.4、Druid连接池使用步骤


  • 1.导入druid-1.0.0.jar的jar包
  • 2.赋值druid.properties文件到src下,并设置对应参数
  • 3.加载properties文件的内容到Properties对象中
  • 4.创建Druid连接池,使用配置文件中的参数
  • 5.从Druid连接池中取出连接
  • 6.执行SQL语句
  • 7.关闭资源
9、JDBC案例(在Java程序中实现对数据库的增删改查)


  • Demo类

      1. package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
      2. import java.sql.Connection;
      3. import java.sql.PreparedStatement;
      4. import java.sql.ResultSet;
      5. import java.sql.SQLException;
      6. import java.util.ArrayList;
      7. import java.util.List;
      8. /**
      9. * JDBC增删改查练习---- 完成商品品牌数据的增删改查操作
      10. * 查询数据:查询所有数据
      11. * 添加数据:添加品牌
      12. * 修改数据:根据id修改
      13. * 删除数据:根据id删除
      14. */
      15. public class Demo10 {
      16.     /*
      17.         数据准备:
      18.             创建一个商品品牌的数据库表 tb_brand
      19.             创建一个Brand实体类
      20.      */
      21.     public static void main(String[] args) throws Exception {
      22.         // 1. 增加数据
      23. //        addBrand();
      24.         // 2. 删除数据
      25.         deleteBrand();
      26.         // 3. 修改数据
      27.         editBrand();
      28.         // 4. 查询数据
      29.         seekBrand();
      30.     }
      31.     public static void addBrand() throws SQLException {
      32.         // 获取连接
      33.         Connection connection = DataSourceUtils.getConnection();
      34.         // 获取preparedStatement对象
      35.         String sql = "insert into day03.tb_brand (brand, description, headquarters) values" +
      36.                 "(?, ?, ?)";
      37.         PreparedStatement preparedStatement = connection.prepareStatement(sql);
      38.         preparedStatement.setString(1, "格力空调");
      39.         preparedStatement.setString(2, "美好生活格力造");
      40.         preparedStatement.setString(3, "China");
      41.         // 得到statement对象返回的影响行数
      42.         int i = preparedStatement.executeUpdate();
      43.         System.out.println(i);
      44.         // 将连接返回给连接池
      45.         connection.close();
      46.     }
      47.     // 根据id删除数据
      48.     public static void deleteBrand() throws SQLException {
      49.         // 获取连接
      50.         Connection connection = DataSourceUtils.getConnection();
      51.         // 编写删除数据的SQL, 并获取preparedStatement对象
      52.         String sql = "delete from day03.tb_brand where id = ?";
      53.         PreparedStatement preparedStatement = connection.prepareStatement(sql);
      54.         preparedStatement.setInt(1, 5);
      55.         // 执行SQL, 返回影响的行数
      56.         int i = preparedStatement.executeUpdate();
      57.         System.out.println(i);
      58.         // 将连接返回给连接池
      59.         connection.close();
      60.     }
      61.     // 根据id编辑数据
      62.     public static void editBrand() throws SQLException {
      63.         // 获取连接
      64.         Connection connection = DataSourceUtils.getConnection();
      65.         // 编写sql,并获取preparedStatement对象
      66.         String sql = "update day03.tb_brand set brand = ?, description = ?, headquarters = ? where id = ?";
      67.         PreparedStatement preparedStatement = connection.prepareStatement(sql);
      68.         // 补全SQL
      69.         preparedStatement.setString(1, "华强北");
      70.         preparedStatement.setString(2, "天上地下我华强北说第二,谁敢说第一");
      71.         preparedStatement.setString(3, "中国深圳");
      72.         preparedStatement.setInt(4, 1);
      73.         // 执行SQL语句, 返回影响的行数
      74.         int i = preparedStatement.executeUpdate();
      75.         System.out.println(i);
      76.         // 关闭连接,将连接返回给连接池
      77.         connection.close();
      78.     }
      79.     public static void seekBrand() throws SQLException {
      80.         // 获取连接
      81.         Connection connection = DataSourceUtils.getConnection();
      82.         // 获取Statement对象
      83.         String sql = "select * from day03.tb_brand";
      84.         PreparedStatement preparedStatement = connection.prepareStatement(sql);
      85.         // 得到数据集合
      86.         ResultSet resultSet = preparedStatement.executeQuery();
      87.         // 处理数据
      88.         List<Brand> brandList = new ArrayList<>();  // 存储数据
      89.         while (resultSet.next()) {
      90.             int id = resultSet.getInt("id");
      91.             String brand = resultSet.getString("brand");
      92.             String description = resultSet.getString("description");
      93.             String headquarters = resultSet.getString("headquarters");
      94.             brandList.add(new Brand(id, brand, description, headquarters));
      95.         }
      96.         brandList.forEach((brand) -> System.out.println("data = " + brand));
      97.         // 将连接返回给连接池
      98.         connection.close();
      99.     }
      100. }
      复制代码

  • DataSourceUtils工具类(获取Datasource)

      1. package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
      2. import com.alibaba.druid.pool.DruidDataSourceFactory;
      3. import javax.sql.DataSource;
      4. import java.io.InputStream;
      5. import java.sql.Connection;
      6. import java.sql.SQLException;
      7. import java.util.Properties;
      8. /**
      9. * 工具类
      10. *      -- 创建Druid连接池
      11. */
      12. public class DataSourceUtils {
      13.     private static DataSource dataSource;
      14.     static {    // 利用静态代码块加载创建连接池的操作
      15.         try {
      16.             // 1. 载入配置文件
      17.             // 获取properties配置文件
      18.             InputStream inputStream = DataSourceUtils.class.getResourceAsStream("brand.properties");
      19.             Properties properties = new Properties();
      20.             properties.load(inputStream);
      21.             // 2. 创建连接
      22.             dataSource = DruidDataSourceFactory.createDataSource(properties);
      23.         }catch (Exception e) {
      24.             e.printStackTrace();
      25.         }
      26.     }
      27.     public static Connection getConnection() throws SQLException {
      28.         return dataSource.getConnection();
      29.     }
      30. }
      复制代码

  • Brand实体类

      1. package _02MySQL.Day03_JDBC.demo10_JDBC增删改查练习;
      2. public class Brand {
      3.     private int id;
      4.     private String brand;
      5.     private String description;
      6.     private String headquarters;
      7.     public Brand(int id, String brand, String description, String headquarters) {
      8.         this.id = id;
      9.         this.brand = brand;
      10.         this.description = description;
      11.         this.headquarters = headquarters;
      12.     }
      13.     public int getId() {
      14.         return id;
      15.     }
      16.     public void setId(int id) {
      17.         this.id = id;
      18.     }
      19.     public String getBrand() {
      20.         return brand;
      21.     }
      22.     public void setBrand(String brand) {
      23.         this.brand = brand;
      24.     }
      25.     public String getDescription() {
      26.         return description;
      27.     }
      28.     public void setDescription(String description) {
      29.         this.description = description;
      30.     }
      31.     public String getHeadquarters() {
      32.         return headquarters;
      33.     }
      34.     public void setHeadquarters(String headquarters) {
      35.         this.headquarters = headquarters;
      36.     }
      37.     @Override
      38.     public String toString() {
      39.         return "Brand{" +
      40.                 "id=" + id +
      41.                 ", brand='" + brand + '\'' +
      42.                 ", description='" + description + '\'' +
      43.                 ", headquarters='" + headquarters + '\'' +
      44.                 '}';
      45.     }
      46. }
      复制代码

  • brand.properties配置文件

      1. driverClassName=com.mysql.cj.jdbc.Driver
      2. url=jdbc:mysql://127.0.0.1:3306/day03
      3. username=root
      4. password=123
      5. initialSize=5
      6. maxActive=10
      7. maxWait=3000
      复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表