• JDBC(一)基础知识


    1 jdbc入门示例

    1.1 代码

    导入maven依赖

    <!-- mysql-Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例查询代码

    package xyz.deepmez.mezbatis.v0;
    
    import java.sql.*;
    
    public class Main {
    
        /**
         * 链接数据库相关参数
         */
        static final String DB_URL = "jdbc:mysql://****:3306/lwj?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false";
        static final String user = "***";
        static final String pass = "***";
    
        public static void main(String[] args) {
            Connection conn = null;
    
            try {
                // 注册驱动,执行静态代码
                Class.forName("com.mysql.jdbc.Driver");
                System.out.println("-----连接数据库----");
    
                // 连接,之后会交给连接池去处理
                conn = DriverManager.getConnection(DB_URL, user, pass);
    
                // 这个连接去做相应的操作
                String sql1 = "select * from student where id=10001";
                Statement statement = conn.createStatement();
                ResultSet rs1 = statement.executeQuery(sql1);
                //数据输出
                while (rs1.next()) {
                    System.out.printf("id=%d,age=%d,name=%s%n", rs1.getInt("id"), rs1.getInt("age"), rs1.getString("name"));
                }
                rs1.close();
                statement.close();
                //完成并关闭数据库
                conn.close();
                System.out.println("查询结束关闭数据库");
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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

    1.2 知识点

    • 加载驱动:用Class.forName,因为jdbc的驱动类是一个static的静态代码块DriverManager.registerDriver(new Driver());,所以只要类加载了,这段逻辑就会走,jdbc的驱动就会实例化一个对象注册到DriverManager;
    • Connection:从驱动管理拿链接,这个类似于从数据源拿连接,后续的工作把部分做成了数据库连接池的逻辑;
    • Statement:具体执行某SQL的对象;

    2 事务控制

    用Connection做事务控制;

    public class Main {
        public static void main(String[] args) {
            Connection conn = null;
    
            try {
                // 注册驱动,执行静态代码
                Class.forName("com.mysql.jdbc.Driver");
                System.out.println("-----连接数据库----");
    
                // 连接,之后会交给连接池去处理
                conn = DriverManager.getConnection(DB_URL, user, pass);
    
                // 这个连接去做相应的操作
                String sql1 = "update student set name='李维佳' where id = 10004";
                String sql2 = "update student set name='李维佳' where id = 10006";
                Statement statement = conn.createStatement();
                conn.setAutoCommit(false);
                try {
                    int count = statement.executeUpdate(sql1);
                    System.out.println("成功条数:" + count);
    
                    count = statement.executeUpdate(sql2);
                    System.out.println("成功条数:" + count);
                    conn.commit();
                } catch (Exception e) {
                    conn.rollback();
                    e.printStackTrace();
                }
    
                statement.close();
                //完成并关闭数据库
                conn.close();
                System.out.println("查询结束关闭数据库");
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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

    事务的控制在connection层面;

    3 结果集

    针对查询操作,Statement的执行结果存放的对象,在下一次SQL执行后覆盖关闭

    4 prepareStatement

    4.1 防止SQL注入

    public class Main {
        public static void main(String[] args) {
            Connection conn = null;
    
            try {
                // 注册驱动,执行静态代码
                Class.forName("com.mysql.jdbc.Driver");
                System.out.println("-----连接数据库----");
    
                // 连接,之后会交给连接池去处理
                conn = DriverManager.getConnection(DB_URL, user, pass);
    
                // 这个连接去做相应的操作
                String sql = "select * from student where id = ?";
    
                // 获得执行对象
                PreparedStatement statement = conn.prepareStatement(sql);
                statement.setInt(1, 10006);
                ResultSet resultSet = statement.executeQuery();
                //数据输出
                while (resultSet.next()) {
                    System.out.printf("id=%d,age=%d,name=%s%n", resultSet.getInt("id"), resultSet.getInt("age"), resultSet.getString("name"));
                }
    
                statement.close();
                conn.close();
                System.out.println("查询结束关闭数据库");
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 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

    防止SQL注入,提前把SQL准备好,只填入变量,如果变量里面有可能改变的SQL的字符,会转义;

    4.2 预编译效率高

    mysql支持预编译;

    -- 定义一个预编译语句
    prepare statement_1 from 'select * from user where id=?';
    -- 设置变量
    set @id=1;
    -- 执行
    execute statement_1 using @id;
    -- 释放
    deallocate prepare statement_1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实际上在conn.prepareStatement(sql)就和数据库交互了在预编译SQL了;

    5 连接池

    SUN官方定义好了数据源连接池接口DataSource,各业务方实现即可;
    使用方式

    5.1 配置properties

    # -数据库连接相关-
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://000.000.000.00:3306/lwj?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&useServerPrepStmts=true
    username=name
    password=123456
    # -数据源连接池相关-
    # 初始化连接数量
    initialSize=1
    # 最大连接池个数
    maxActive=2
    # 最大等待时间(单位毫秒)
    maxWait=3000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.2 从连接池获得连接

    public class Main {
        public static void main(String[] args) throws Exception {
            // 从数据源连接池获得连接
            Properties properties = new Properties();
            properties.load(new FileInputStream("src/main/resources/druid.properties"));
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    
            // 多线程获得三个连接
            for (int i = 0; i < 3; i++) {
                int finalI = i;
                new Thread(() -> {
                    try {
                        System.out.println("请求第" + finalI + "个连接");
                        Connection connection = dataSource.getConnection();
                        if (Objects.nonNull(connection)) {
                            System.out.println("获得第" + finalI + "个连接成功");
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注意:从连接池获得的连接,在使用后需要关闭close,关闭是放回连接池,不是真的断开TCP连接

    6 并发控制

    在应用服务器中,对于数据库的访问往往是并发的,这时候需要系统做并发的兼容,考虑从查询粒度依次增加:

    • Statement:如果并发使用Statement去处理,即多条SQL由同一个Statement去执行,那么会遇到的问题就是多线程之间的结果集丢失,不可取;
    • Connection:并发使用Connection取处理,即多个SQL都由同一个Connection去处理,那么会遇到的问题是多线程之间的事务造成非预期的回滚,因为Connection共用一个事务管理;

    所以可行的方案是单独开一个Connection处理当前的SQL任务

  • 相关阅读:
    kotlin基础
    C++异步并发编程future、promise和packaged_task三者的区别和联系
    开发板移植RTOS操作系统,RTOS操作系统适配开发板整理大全
    【Python入门五】第三方库(包)介绍
    第三章Linux环境基础开发工具使用(yum+rzsz+vim+g++和gcc+gdb+make和Makefile+进度条+git)
    【游记】CSP2023-S2
    使用TS进行Vue-Router的Meta类型扩展
    Linux内存管理知识总结(一)
    【小程序源码】王者荣耀神器助手
    linux和windows的共享文件夹的设置(ubuntu18.04)
  • 原文地址:https://blog.csdn.net/weixin_44215363/article/details/125471970