jdbc学习笔记01

JDBC

1. 概念

Java DataBase Connectivity Java数据库连接(用Java操作数据库)

定义了操作所有关系型数据库的规范(接口),实现类(数据库驱动jar包)由各数据库厂商实现用来操作数据库

2. 快速入门

  • 步骤
    1. 导入驱动jar包 mysql-connector-java-8.0.19.jar
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义 sql
    5. 获取执行 sql 语句的对象 Statement
    6. 执行 sql,接收返回的结果
    7. 处理结果
    8. 释放资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Main {
public static void main(String[] args) throws Exception {
//1. 导入驱动jar包(复制jar包,右键 add as library)
//2. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//或使用下面这种写法(耦合性较高)
//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

//3. 获取数据库连接对象 Connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/learn?serverTimezone=GMT%2B8&characterEncoding=utf-8", "root", "4869");
//4. 定义 sql
String sql = "update user set sex = '女' where id = 42";
//5. 获取执行 sql 语句的对象 Statement
Statement stmt = connection.createStatement();
//6. 执行 sql,接收返回的结果
int count = stmt.executeUpdate(sql);
//7. 处理结果
System.out.println(count);
//8. 释放资源
stmt.close();
connection.close();
}
}

3. 详解各个对象

  1. DriverManager:驱动管理对象

    • 功能:

      1. 注册驱动

        static void registerDriver (Driver driver) 使用 DriverManager注册给定的驱动程序

        写代码时使用 Class.forName("com.mysql.cj.jdbc.Driver");

        另外,发现不注册驱动也能运行成功,这是因为在mysql5之后,在jar包中存在文件mysql-connector-java-8.0.19.jar\META-INF\services\java.sql.Driver,里面写的有默认驱动

      2. 获取数据库连接

        • 方法:static Connection getConnection(String url, String user, String password)

        • 参数:

          • url:指定连接的路径

            • 语法:jdbc:mysql://ip地址(域名): 端口号/数据库名称?serverTimezone=所在时区

              mysql-jdbc 6.0以上版本需要在连接数据库url后面指定所在时区。
              中国用的是GMT+8的时区,在jdbc连接的url后面加上东八区的serverTimezone=Asia/Shanghai

              serverTimezone=Hongkong

              serverTimezone=GMT%2B8(%2B是“+”号)

              即可解决问题

            • 例子:jdbc:mysql://localhost:3306/learn?serverTimezone=Hongkong

            • 细节:如果连接的是本机mysql服务器,并且mysql服务的默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?serverTimezone=时区名称

          • user:用户名

          • password:密码

  2. Connection:数据库连接对象

    1. 功能:
      1. 获取执行 sql 的对象
        • Statement createStatement()
        • PreparedStatement prepareStatement(String sql)
      2. 管理事务
        • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false即开启事务
        • 提交事务:commit()
        • 回滚事务:rollback()
  3. Statement:执行sql对象

    1. 执行 sql
      1. boolean execute(String sql):可以执行任意的 sql 了解
      2. int executeUpdate(String sql):执行 DML(insert、update、delete)语句、DDL语句(create、alter、drop)语句
        • 返回值:影响的行数,可以通过这个影响的行数判断 DML 语句是否执行成功,返回值 > 0执行成功
      3. ResultSet executeQuery(String sql):执行 DQL(select)语句
        • (其返回值和 2. 不同)
  4. ResultSet:结果集对象,封装查询结果

    • boolean next():光标向下移动一行

    • getXxx(参数):获取数据

      • Xxx代表数据类型,如int getInt(),getString()
      • 参数:
        1. int:代表列的编号,从 1 开始 ,如:getString(1)
        2. String:代表列名称,如:getDouble("balance")
    • 注意:

      • 使用步骤:

        1. 游标向下移动一行
        2. 判断是否有数据
        3. 获取数据
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        String sql = "select * from user";
        //5. 获取执行 sql 语句的对象 Statement
        Statement stmt = connection.createStatement();
        //6. 执行 sql,接收返回的结果
        ResultSet rs = stmt.executeQuery(sql);
        //7. 处理结果
        // 循环判断游标是否是最后一行末尾
        while (rs.next()){
        int id = rs.getInt(1);
        String name = rs.getString("username");
        System.out.println("id = " + id + " username = " + name);
        }
  5. PreparedStatement:执行sql对象

    1. SQL 注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全问题

      1. 输入用户名随便,输入密码:a' or ‘a' = ‘a
      2. sql:select * from user where username = ‘asdad‘ and password = ‘a' or ‘a‘ = ‘a'
    2. 解决sql注入问题:使用PreparedStatement对象来解决

    3. 预编译的sql:参数使用?作为占位符,用户的输入只是给占位符赋值

    4. 步骤:

      1. 导入驱动jar包 mysql-connector-java-8.0.19.jar
      2. 注册驱动
      3. 获取数据库连接对象 Connection
      4. 定义 sql
        • 注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?;
      5. 获取执行 sql 语句的对象 PrepareStatement Connection.prepareStatement(String sql)
      6. ? 赋值:
        • 方法:setXxx(参数1, 参数2)
          • 参数1:?的位置编号,从 1 开始
          • 参数2:?的值
      7. 执行 sql,接收返回的结果
      8. 处理结果
      9. 释放资源
    5. 后期都会使用PreparedStatement来完成增删改查的所有操作

      1. 可以防止SQL注入
      2. 效率更高
    6. 代码示例:(可以防止 sql 注入)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      public static void main(String[] args) {
      ResultSet rs = null;
      PreparedStatement pstmt = null;
      Connection connection = null;
      try {
      connection = JDBCUtils.getConnection();
      //4. 定义 sql
      String sql = "select * from user where username = ? and sex = ?";
      //5. 获取执行 sql 语句的对象 PreparedStatement
      pstmt = connection.prepareStatement(sql);
      //6. 给 ? 赋值
      pstmt.setString(1,"ascasc");
      pstmt.setString(2,"a' or ‘a‘ = ‘a");
      //7. 执行查询,不需要传参
      rs = pstmt.executeQuery();
      //8. 判断,输出为 false
      System.out.println(rs.next());
      } catch (SQLException e) {
      e.printStackTrace();
      } finally {
      JDBCUtils.close(rs,pstmt,connection);
      }
      }

抽取JDBC工具类:JDBCUtils

  • 目的:简化书写

  • 分析:

    1. 抽取注册驱动

    2. 抽取一个方法获取连接对象

      • 需求:不传递参数,同时保证工具类的通用性

      • 解决:配置文件(src/jdbc.properties)

        1
        2
        3
        4
        url=jdbc:mysql:///learn?serverTimezone=Hongkong
        user=root
        password=4869
        driver=com.mysql.cj.jdbc.Driver
  1. 抽取一个方法释放资源

    代码示例:

    JDBCUtils.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    package com.conv.util;

    import java.io.FileReader;
    import java.io.IOException;
    import java.net.URL;
    import java.sql.*;
    import java.util.Properties;

    /**
    * JDBC工具类
    */
    public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;

    /**
    * 文件的读取,只需要读取一次即可拿到这些值,使用静态代码块
    */
    static {
    try {
    //1. 读取资源文件,获取值
    Properties pro = new Properties();

    //2. 加载文件
    //方法 1(实测不可行)
    // 获取src路径下的文件的方式----->ClassLoader 类加载器
    // ClassLoader classLoader = JDBCUtils.class.getClassLoader();
    // // 根路径是 src 目录,所以只用写相对路径就行
    // URL res = classLoader.getResource("jdbc.properties");
    // String path = res != null ? res.getPath() : null;
    // System.out.println(path);
    // if (path != null) {
    // pro.load(new FileReader(path));
    // }

    //方法 2
    // 该行代码实测是可行的(可能java8不行?)若不行的话可采用上面的方法
    pro.load(new FileReader("src/jdbc.properties"));
    //3. 获取数据,赋值
    url = pro.getProperty("url");
    user = pro.getProperty("user");
    password = pro.getProperty("password");
    driver = pro.getProperty("driver");
    //4. 注册驱动
    Class.forName(driver);

    } catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
    }
    }

    /**
    * 获取连接
    *
    * @return 连接对象
    * @throws SQLException
    */
    public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url, user, password);
    }

    /**
    * 关闭资源
    *
    * @param statement
    * @param connection
    */
    public static void close(Statement statement, Connection connection) {
    try {
    if (statement != null) {
    statement.close();
    }
    if (connection != null) {
    connection.close();
    }
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }

    /**
    * 关闭资源
    *
    * @param resultSet
    * @param statement
    * @param connection
    */
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
    try {
    if (resultSet != null) {
    resultSet.close();
    }
    if (statement != null) {
    statement.close();
    }
    if (connection != null) {
    connection.close();
    }
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }

    main.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package com.conv.jdbc;

    import com.conv.util.JDBCUtils;

    import java.sql.*;

    public class Main {
    public static void main(String[] args) {
    ResultSet rs = null;
    Statement stmt = null;
    Connection connection = null;
    try {
    connection = JDBCUtils.getConnection();
    //4. 定义 sql
    //String sql = "update user set sex = '男' where id = 42";
    String sql = "select * from user";
    //5. 获取执行 sql 语句的对象 Statement
    stmt = connection.createStatement();
    //6. 执行 sql,接收返回的结果
    rs = stmt.executeQuery(sql);
    //7. 处理结果
    while (rs.next()) {
    int id = rs.getInt(1);
    String name = rs.getString("username");
    System.out.println("id = " + id + " username = " + name);
    }
    } catch (SQLException e) {
    e.printStackTrace();
    } finally {
    JDBCUtils.close(rs,stmt,connection);
    }
    }
    }
  • sql 语句拼接Java中的变量

    java中的字符串只能双引号(js可以单引号也可以双引号)引上,如果字符串中需要拼接变量,该变量用单引号括起来,然后加两个双引号再加两个加号,中间就是变量

    例:

    1
    2
    String username, password;	//username 和 password 已经定义
    String sql = “select * from user where username = '"+username+"' and password = '"+password+"'

JDBC 控制事务

  1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败

  2. 操作:

    1. 开启事务:
    2. 提交事务
    3. 回滚事务
  3. 使用Connection对象来管理事务

    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false即开启事务
      • 在执行sql之前开启事务
    • 提交事务:commit()
      • 当所有sql都执行完提交事务
    • 回滚事务:rollback()
      • 在catch中回滚事务
  4. 代码示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public static void main(String[] args) {
    ResultSet rs = null;
    PreparedStatement pstmt1 = null;
    PreparedStatement pstmt2 = null;
    Connection connection = null;
    try {
    //1. 获取连接
    connection = JDBCUtils.getConnection();
    //开启事务
    connection.setAutoCommit(false);
    //2.定义 sql
    //2.1 张三 - 300
    String sql1 = "update account set money = money - ? where id = ?";
    //2.2 李四 + 300
    String sql2 = "update account set money = money + ? where id = ?";
    //3. 获取执行sql对象
    pstmt1 = connection.prepareStatement(sql1);
    pstmt2 = connection.prepareStatement(sql2);

    //4. 设置参数
    pstmt1.setInt(1, 300);
    pstmt1.setInt(2, 1);

    pstmt2.setInt(1, 300);
    pstmt2.setInt(2, 2);

    //5. 执行sql
    pstmt1.executeUpdate();
    //手动制造异常
    int i = 1 / 0;

    pstmt2.executeUpdate();

    connection.commit();

    } catch (Exception e) {
    //事务回滚(回滚到事务开启之前的状态)
    try {
    if (connection != null) {
    connection.rollback();
    }
    } catch (SQLException ex) {
    ex.printStackTrace();
    }
    e.printStackTrace();
    } finally {
    JDBCUtils.close(pstmt1, connection);
    JDBCUtils.close(pstmt2, null);
    }
    }
坚持原创技术分享,感谢您的支持和鼓励!