MySQL:JDBC 详细内容

news2024/11/14 21:34:59

文章目录

  • Day 04:
  • 一、JDBC
    • 1. 数据库驱动
    • 2. 概述
    • 3. 第一个 JDBC 程序
    • 4. JDBC 中对象的解释
  • 二、改进 JDBC 程序
    • 1. 思路
    • 2. 实践注意点
    • 3. 分析
    • 4. 结果
    • 5. 代码
  • 三、SQL 注入问题
  • 四、PreparedStatement 对象
    • 1. 实践注意点
    • 2. 分析(增、删、改、查)
    • 3. SQL 注入问题的解决
    • 4. 代码
  • 五、使用 IDEA 连接数据库
  • 六、JDBC 操作事务
    • 1. 步骤及注意点
    • 2. 代码实现
  • 七、数据库连接池
    • 1. 概述
    • 2. DBCP
    • 3. C3P0
  • 注意:

Day 04:

一、JDBC

1. 数据库驱动

驱动:驱动程序即添加到操作系统中的一小块代码,其中包含有关硬件设备的信息。有了此信息,计算机就可以与设备进行通信。驱动程序是硬件厂商根据操作系统编写的配置文件,可以说没有驱动程序,计算机中的硬件就无法工作。

种类:声卡驱动、显卡驱动、数据库驱动等。

注意:我们的程序会通过数据库驱动,来和数据库打交道,不同的数据库驱动由不同的厂商去做。

2. 概述

JDBC:Java数据库连接,Java Database Connectivity

定义:是 Java 语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

公司:JDBC 是 Sun 公司提供的。

对于开发人员来说,我们只需要掌握 JDBC 接口的操作。

3. 第一个 JDBC 程序

  • 准备工作:在 Navicat 中创建测试数据库 jdbcStudy
CREATE DATABASE `jdbcStudy` CHARACTER SET utf8 COLLATE utf8_general_ci;

USE `jdbcStudy`;

CREATE TABLE IF NOT EXISTS `users`(
 `id` INT PRIMARY KEY,
 `name` VARCHAR(40),
 `password` VARCHAR(40),
 `email` VARCHAR(60),
 birthday DATE
);

INSERT INTO `users`(`id`,`name`,`password`,`email`,`birthday`) VALUES
(1,'张三','123456','zs@sina.com','1980-12-04'),
(2,'李四','123456','lisi@sina.com','1981-12-04'),
(3,'王五','123456','wangwu@sina.com','1979-12-04');
  • 在 IDEA 中创建一个 Maven 项目(方便下一步导入依赖),删除 src 文件夹,建立子模块,在子项目的 main 中建立 java 以及 resources 文件夹,并标记功能。

  • pom.xml 文件中导入依赖(mysql 数据库驱动包)。
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>

  • 创建 JdbcDemo01 类,编写 JDBC 程序
package com.Sun3285.jdbc;

import java.sql.*;

public class JdbcDemo01 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动,固定写法
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2. url 和用户信息,用 ? 连接参数
        // useUnicode=true&characterEncoding=uft8&useSSL=true&serverTimezone=Asia/Shanghai  编码、字符集、使用安全的连接、时区
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai";
        String username = "root";
        String password = "1142553864qq";

        // 3. 连接数据库,connection 代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);

        // 4. 得到执行 SQL 的对象,即:statement 可以执行 SQL
        Statement statement = connection.createStatement();

        // 5. 编写要执行的 SQL
        String sql = "SELECT * FROM `users`;";

        // 6. 执行 SQL,得到的 resultSet 为结果集,封装了全部的查询出来的结果,链表的形式
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()) {
            System.out.println("id = " + resultSet.getObject("id"));
            System.out.println("name = " + resultSet.getObject("name"));
            System.out.println("password = " + resultSet.getObject("password"));
            System.out.println("email = " + resultSet.getObject("email"));
            System.out.println("birthday = " + resultSet.getObject("birthday"));
            System.out.println("=========================");
        }

        // 7. 释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

步骤总结(具体看代码中注释):

  1. 加载驱动;
  2. url 和用户信息;
  3. 连接数据库;
  4. 得到执行 SQL 的对象;
  5. 编写要执行的 SQL;
  6. 执行 SQL;
  7. 释放连接。

4. JDBC 中对象的解释

  • 第 2 步:在 url 中
String url = "协议://主机地址:端口号/数据库名?参数1&参数2&参数3&参数x";

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai";
// 协议:jdbc:mysql
// 主机地址:localhost
// 端口号:mysql 默认为 3306
// 数据库名:jdbcstudy
// 若干参数:useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai 编码、字符集、使用安全的连接、时区
  • 第 3 步connection 对象代表数据库,可以设置数据库自动提交(setAutoCommit),事务提交(commit)、回滚(rollback)等;
  • 第 4 步statement 对象是执行类,可以执行 SQL;
  • 第 6 步statement 对象可以执行的方法,例如
statement.execute();        // 执行任何 SQL
statement.executeQuery();   // 执行查询操作,得到的 resultSet 为结果集,封装了全部的查询出来的结果,链表的形式
statement.executeUpdate();  // 执行更新、插入、删除操作,返回一个受影响的行数(int 类型)
  • 第 6 步查询操作得到resultSet结果集,封装了全部的查询出来的结果
    • 可以获得指定的数据类型:调用 getObjectgetStringgetInt 等方法;
    • 可以遍历:调用 next 方法,指针移动到下一个数据(先移动,再判断);
  • 第 7 步:释放资源必须做,先开后关。

二、改进 JDBC 程序

通过提取工具类,来改进 JDBC 程序,之后只写 SQL 语句和输出即可。

1. 思路

  1. driverurlusername 以及 password 信息放到一个属性 properties 文件中存放
  2. 编写一个工具类 JdbcUtils 完成重复代码的封装
  3. 自定义的类只需调用工具类,以及写 SQL 语句即可。

2. 实践注意点

  1. 属性 properties 文件存放信息时,没有引号没有分号,如:driver=com.mysql.cj.jdbc.Driver
  2. 工具类完成重复代码的封装,可以把第 1、2、3、7 步放入这里;
  3. 工具类中,可以把读取属性文件和加载驱动,放入静态代码块中,当第一次用到本类时,执行唯一的一次;
  4. 工具类中,第 3 步(连接数据库)和第 7 步(释放连接)可以封装为静态方法;
  5. 总体步骤与之前步骤一样,只是提取了工具类,简化了代码

3. 分析

  • 工具类 JdbcUtils.java

  • 插入数据类 TestDemo01.java

4. 结果

5. 代码

  • 属性文件 db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=1142553864qq
  • 工具类 JdbcUtils.java
package com.Sun3285.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {
            // 通过反射获取类加载器,再转变为流
            InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");

            Properties properties = new Properties();
            properties.load(is);

            driver = (String) properties.get("driver");
            url = (String) properties.get("url");
            username = (String) properties.get("username");
            password = (String) properties.get("password");

            // 加载驱动
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    // 释放连接资源
    public static void release(Connection connection, Statement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 插入/修改/删除/查询数据类 TestDemo.java
package com.Sun3285.jdbc;

import com.Sun3285.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestDemo {

    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            // 插入
            String sql1 = "INSERT INTO `users` (`id`, `name`, `password`, `email`, `birthday`) VALUES" +
                    "(4, '健康', '1375380', '1375380@qq.com', '1999-11-01'), " +
                    "(5, '快乐', '7894561', '7894561@qq.com', '1998-2-3');";
            int column1 = statement.executeUpdate(sql1);
            if (column1 > 0) {
                System.out.println("插入数据成功!");
            }
            
            // 修改
            String sql2 = "UPDATE `users` SET `password` = '753159' WHERE `id` = 2 OR `birthday` = '1979-12-4';";
            String sql3 = "UPDATE `users` SET `name` = '天天', `email` = '123abc@qq.com' WHERE `id` = 1;";
            int column2 = statement.executeUpdate(sql2);
            int column3 = statement.executeUpdate(sql3);
            if (column2 > 0 || column3 > 0) {
                System.out.println("修改数据成功!被修改的数据有 " + (column2 + column3) + " 条。");
            }
            
            // 删除
            String sql4 = "DELETE FROM `users` WHERE `name` = '李四';";
            int column4 = statement.executeUpdate(sql4);
            if (column4 > 0) {
                System.out.println("删除数据成功!被删除的数据有 " + column4 + " 条。");
            } else {
                System.out.println("没有删除数据。");
            }
            
            // 查询
            String sql5 = "SELECT `name` AS '姓名', `password` AS '密码', `birthday` AS '生日' FROM `users`;";
            resultSet = statement.executeQuery(sql5);
            System.out.println("查询到的信息如下:");
            while (resultSet.next()) {
                System.out.println("姓名:" + resultSet.getString("姓名"));
                System.out.println("密码:" + resultSet.getString("密码"));
                System.out.println("生日:" + resultSet.getString("生日"));
                System.out.println("================");
            }
            
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

三、SQL 注入问题

百度百科

SQL 是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用 SQL 。而 SQL 注入是将 Web 页面的原 URL、表单域或数据包输入的参数,修改拼接成 SQL 语句,传递给 Web 服务器,进而传给数据库服务器以执行数据库命令。

如果 Web 应用程序的开发人员对用户所输入的数据或 cookie 等内容不进行过滤或验证(即存在注入点)就直接传输给数据库,就可能导致拼接的 SQL 被执行,获取对数据库的信息以及提权,发生 SQL 注入攻击

举例:模拟用户的登录:输入用户名和密码,来进行登录。

注意

  • SQL 中 AND优先级高于 OR
  • 登录也可以写为:login("", "' OR '1=1");

运行结果

代码

package com.Sun3285.jdbc;

import com.Sun3285.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQLInjection {

    public static void main(String[] args) {

        // login("健康", "1375380");
        login("' OR '1=1", "' OR '1=1");
    }

    // 登录方法
    public static void login(String username, String password) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = JdbcUtils.getConnection();
            statement = connection.createStatement();

            String sql = "SELECT * FROM `users` WHERE `name` = '" + username + "' AND `password` = '" + password + "';";

            resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                resultSet.previous();
                while (resultSet.next()) {
                    System.out.println("登录成功!");
                    System.out.println("姓名:" + resultSet.getString("name"));
                    System.out.println("密码:" + resultSet.getString("password"));
                    System.out.println("邮箱:" + resultSet.getString("email"));
                    System.out.println("生日:" + resultSet.getString("birthday"));
                    System.out.println("=========================");
                }
            } else {
                System.out.println("登录失败,用户名或密码错误!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(connection, statement, resultSet);
        }
    }
}

四、PreparedStatement 对象

PreparedStatement 继承Statement 可以防止 SQL 注入,并且效率更好。防止 SQL 注入的本质:把传递进来的参数当作字符,假设其中存在转义字符,会直接忽略。例如,单引号 ' 会被直接转义,然后被直接忽略。

1. 实践注意点

  1. 与之前 JDBC 程序的步骤有所区别:在创建了数据库对象 connection 后,进行编写 SQL预编译传递参数执行
  2. 注意 PreparedStatementsetDate 方法,第二个参数java.sql.Date
  3. 在编写 SQL 时,使用 ? 占位符代替参数;
  4. 在传递参数时,不能重复给相同位置的参数赋值后执行,因为会覆盖上一次的值,可以先执行,再再次赋值;
  5. 在传递参数时,方法的第一个参数是占位符 ? 的位置(从 1 开始),第二个参数是要传入的值;
  6. 与之前 JDBC 程序不同,在调用 executeUpdateexecuteQuery 方法执行时,不传入 SQL,在预编译时已经传入了 SQL;
  7. 整体学习过程:一个简单的 JDBC 程序 --> 通过提取工具类进行改进,实现简化 --> 发现 SQL 注入问题,使用 PreparedStatement 对象防止 SQL 注入 --> 节省资源,使用数据库连接池。

2. 分析(增、删、改、查)

插入数据查询数据为例:

  • 插入数据

  • 查询数据

3. SQL 注入问题的解决

在原先基础上,将 Statement 对象替换PreparedStatement 对象。

运行结果

4. 代码

  • 插入/修改/删除/查询数据类 PreparedDemo.java
package com.Sun3285.jdbc;

import com.Sun3285.utils.JdbcUtils;

import java.sql.*;

public class PreparedDemo {

    public static void main(String[] args) {

        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();

            // 插入
            String sql1 = "INSERT INTO `users` (`id`, `name`, `password`, `email`, `birthday`) VALUES (?,?,?,?,?);";
            pst = con.prepareStatement(sql1);
            pst.setInt(1, 6);
            pst.setString(2, "李龙");
            pst.setString(3, "147258");
            pst.setString(4, "147258@qq.com");
            pst.setDate(5, Date.valueOf("1989-2-3"));
            int column1 = pst.executeUpdate();
            if (column1 > 0) {
                System.out.println("插入数据成功!共插入 " + column1 + " 条数据。");
            } else {
                System.out.println("没有插入数据!");
            }
            
            // 修改
            String sql2 = "UPDATE `users` SET `birthday` = ? WHERE `name` = ? OR `id` = ?;";
            pst = con.prepareStatement(sql2);
            pst.setDate(1, Date.valueOf("2007-9-1"));
            pst.setString(2, "天天");
            pst.setInt(3, 6);
            int column2 = pst.executeUpdate();
            if (column2 > 0) {
                System.out.println("修改数据成功!共修改 " + column2 + " 条数据。");
            } else {
                System.out.println("没有修改数据!");
            }

            // 删除
            String sql3 = "DELETE FROM `users` WHERE `email` = ?;";
            pst = con.prepareStatement(sql3);
            pst.setString(1, "147258@qq.com");
            int column3 = pst.executeUpdate();
            if (column3 > 0) {
                System.out.println("删除数据成功!共删除 " + column3 + " 条数据。");
            } else {
                System.out.println("没有删除数据!");
            }
            
            // 查询
            String sql4 = "SELECT `name` AS '姓名', `birthday` AS '生日' FROM `users` WHERE `id` > ? AND `password` = ?; ";
            pst = con.prepareStatement(sql4);
            pst.setInt(1, 3);
            pst.setString(2, "222222");
            rs = pst.executeQuery();
            if (rs.next()) {
                System.out.println("查询成功!结果为:");
                rs.previous();
                while (rs.next()) {
                    System.out.println("姓名:" + rs.getString("姓名"));
                    System.out.println("生日:" + rs.getString("生日"));
                    System.out.println("========================");
                }
            } else {
                System.out.println("没有查询到数据!");
            }

            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(con, pst, rs);
        }
    }
}
  • SQL 注入问题的解决
package com.Sun3285.jdbc;

import com.Sun3285.utils.JdbcUtils;

import java.sql.*;

public class SQLInjection2 {

    public static void main(String[] args) {

        // login("健康", "1375380");
        login("", "' OR '1=1");
    }

    // 登录方法
    public static void login(String username, String password) {

        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();

            String sql = "SELECT * FROM `users` WHERE `name` = ? AND `password` = ?;";

            pst = con.prepareStatement(sql);

            pst.setString(1, username);
            pst.setString(2, password);

            rs = pst.executeQuery();

            if (rs.next()) {
                rs.previous();
                while (rs.next()) {
                    System.out.println("登录成功!");
                    System.out.println("姓名:" + rs.getString("name"));
                    System.out.println("密码:" + rs.getString("password"));
                    System.out.println("邮箱:" + rs.getString("email"));
                    System.out.println("生日:" + rs.getString("birthday"));
                    System.out.println("=========================");
                }
            } else {
                System.out.println("登录失败,用户名或密码错误!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(con, pst, rs);
        }
    }
}

五、使用 IDEA 连接数据库

Navicat 一样,IDEA 也可以连接数据库,来管理数据库。

注意

  • 在这一步中,进行参数设置以及连接数据库
  • 在此之前需要加载数据库驱动,即在 pom.xml 配置文件中导入依赖(mysql 数据库驱动包)见 一、3;
  • 这里的 URL 需要和之前一致,可以不写数据库;
  • 测试连接,成功后,选择 OK

注意:如果由于 MySQL 的版本或驱动不对,而出现错误,可以在这个地方修改(一般不要改)。


六、JDBC 操作事务

之前已经学过用 SQL 语言实现一个事务(如下图),现在用 Java 来实现一个事务。

1. 步骤及注意点

JDBC 操作事务的步骤

  1. 关闭数据库自动提交(开启事务):con.setAutoCommit(false);
  2. 执行一组业务;
  3. 提交事务:con.commit()
  4. 可选择)可以在 catch 语句中显示地定义回滚语句:con.rollback();

注意点

  1. 每次在使用 JDBC 时,需要先检查属性文件 db.properties 中的各个参数,例如 url 中的数据库是否是要操作的数据库;
  2. 用 JDBC 操作事务时,关闭数据库自动提交的时候,会自动开启事务;
  3. 事务的回滚 con.rollback() 可以不写,因为如果事务执行失败,会默认回滚;

2. 代码实现

举例:实现转账业务。

package com.Sun3285.jdbc;

import com.Sun3285.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TransactionDemo01 {

    public static void main(String[] args) {

        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();

            // 关闭数据库自动提交,自动会开启事务
            con.setAutoCommit(false);

            String sql1 = "update `account` set `money` = `money` - 2000 where `name` = ?;";
            pst = con.prepareStatement(sql1);
            pst.setString(1, "Sun3285");
            pst.executeUpdate();

            String sql2 = "update `account` set `money` = `money` + 2000 where `name` = ?;";
            pst = con.prepareStatement(sql2);
            pst.setString(1, "Sun1478");
            pst.executeUpdate();

            // 提交事务
            con.commit();
            System.out.println("转账成功!");

        } catch (SQLException e) {
            try {
                // 出现异常,回滚事务
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JdbcUtils.release(con, pst, rs);
        }
    }
}

七、数据库连接池

1. 概述

背景:每次连接数据库和释放连接资源都十分浪费系统资源

解决方法:编写一个数据库连接池需要实现 DataSource 接口(如下图)。

比较知名的 DataSource 接口的实现类(开源的数据源实现,会用就好)有:DBCPC3P0Druid 等。

注意

  • 当使用了数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了;
  • 无论使用什么数据源,本质都是一样的DataSource 接口不会变,方法就不会变。

所用到的池化技术:准备一些预先的资源,过来就连接预先准备好的。例如:常用连接数为 10 个,可以设置最小连接数为 10,最大连接数为 15(业务最高承载上限)。若 15 个业务都在进行,其他的业务将会排队等待,还可以设置等待超时为 5 秒,若业务的排队等待时间超过 5 秒,则不会继续等待。

2. DBCP

注意

  1. 使用数据库连接池 BasicDataSource 实现了 DataSource 接口,代替了之前连接数据库的步骤;
  2. 创建数据源 dataSource 时使用了工厂模式,用来创建 BasicDataSource 类的对象,可以理解为将属性文件中的各个参数传给 BasicDataSource 类,然后来创建数据源。

步骤

  • 准备工作:在 pom.xml 中导入依赖:commons-dbcp 以及 commons-pool
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>

<dependency>
    <groupId>commons-pool</groupId>
    <artifactId>commons-pool</artifactId>
    <version>1.6</version>
</dependency>
  • 编写配置文件,是 properties 类型的文件,存放参数(参数由自己设置,注意 driverClassName 的名字不能改变,固定的
#连接设置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=1142553864qq

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8;useSSL=true;serverTimezone=Asia/Shanghai

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
  • 修改工具类,用数据库连接池来代替手动创建数据库连接

  • 其他步骤不变,编写测试类

3. C3P0

注意

  1. 使用数据库连接池 ComboPooledDataSource 实现了 DataSource 接口,代替了之前连接数据库的步骤;

  2. 在编写 xml 配置文件时,可以指定多个数据源,还需要注意 &amp; 表示与的意思;

  3. 在获取数据源 dataSource 时,不写参数代表获取默认的数据源,有参数代表指定的数据源。

步骤

  • 准备工作:在 pom.xml 中导入依赖:c3p0 以及 mchange-commons-java
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>mchange-commons-java</artifactId>
    <version>0.2.15</version>
</dependency>
  • 编写配置文件,是 xml 类型的文件,设置参数
<?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>
    <!--
    c3p0的缺省(默认)配置
    如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource();"这样写就表示使用的是c3p0的缺省(默认)
    -->
    <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=Asia/Shanghai</property>
        <property name="user">root</property>
        <property name="password">1142553864qq</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <!-- 指定的数据源 -->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=Asia/Shanghai</property>
        <property name="user">root</property>
        <property name="password">1142553864qq</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>
  • 修改工具类,用数据库连接池来代替手动创建数据库连接

  • 其他步骤不变,编写测试类


注意:

  1. 将一个普通项目(模块)转换为 Maven 项目(模块)的方法:选中项目(模块)右键,选择 Add Framework Support... ,打开后选择 Maven 项目。

  1. 程序执行时,会先找到 main 函数(相当于入口),从 main 函数开始执行,当 main 函数执行完毕退出后,整个程序就结束。

  2. 代码块分为静态代码块(类加载时调用,一个类的静态代码块会执行一次)和普通初始化代码块(在每次创建对象时会调用一次),执行静态代码块,执行普通初始化代码块。可以用静态代码块来一次性对静态成员变量进行赋值。

  3. 当使用 JDBC 进行查询 executeQuery 操作时,若没有查询到数据,得到的 resultSet 结果集不为 null因为此时结果集中还包括了字段名。因此,要判断是否查询到数据,不能if (resultSet != null)而应该if (resultSet.next()) ,如果结果为 false ,则说明没有查询到数据。

  4. 理解结果集 resultSetnext 方法:最初指针被置于第一行之前(即 beforeFirst),当执行 next 方法时,指针会移动到下一行,此时若有下一行,结果为 true ,再执行 getString 等方法时,得到指针所指的这一行数据;若没有下一行,结果为 false 。与 next 相反的另一个方法为 previous

  5. SQL 中 AND优先级高于 OR

  6. java.sql.Datejava.util.Date不一样的包,前者是数据库用的,后者是 Java 用的,前者继承了后者

  1. properties 类型和 xml 类型的配置文件可以放在资源目录 resources 下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/439612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑能录屏吗?当然可以!看看这3种方法!

案例&#xff1a;电脑有录屏功能吗&#xff1f; “我的客户让我发一个项目展示的视频&#xff0c;完成这个任务需要对电脑进行录制。问题是&#xff0c;台式电脑有录屏功能吗&#xff1f;笔记本电脑有录屏功能吗&#xff1f;电脑能录屏吗&#xff1f;有没有好心人解答一下我的…

一遍讲清楚:偏向锁到轻量级锁的升级过程(为什么耗资源)

目录 上原理&#xff1a; 细说原理&#xff1a; 什么是锁记录呢&#xff1f; 什么是Mark Word 呢&#xff1f; 上图解&#xff1a; 上原理&#xff1a; 偏向锁使⽤了⼀种等到竞争出现才释放锁的机制&#xff0c;所以当其他线程尝试竞争偏向锁时&#xff0c; 持有偏向锁的…

Java基础——IO流+字节/字符流使用

IO流 &#xff08;1&#xff09;IO流的概述&#xff1a; IO流也称为输入&#xff0c;输出流&#xff0c;就是用来读写数据的。I表示input&#xff0c;是数据从硬盘文件读入到内存的过程&#xff0c;称之输入&#xff0c;负责读。O表示output&#xff0c;是内存程序的数据从内…

【socket通信】python实现简单socket通信| server和client

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、socket通信基础知识1.1基础知识1.2socket类型 二、socket python实现2.1.服务器代码 server.py2.2.客户端代码 client.py2.3.如何运行 补充的计网知识 前言…

2023年TikTok网红营销:从短视频到直播,多维度提升品牌价值

随着TikTok的持续热度&#xff0c;TikTok网红营销已经成为了品牌推广中不可忽视的一部分。在2023年&#xff0c;TikTok网红营销将会继续保持强劲的发展势头。本文Nox聚星将会详细介绍2023年TikTok网红营销的发展趋势&#xff0c;并探讨品牌应该如何抓住这些趋势来提高自己的推广…

「 JavaSE 」说说什么是泛型的类型擦除?

「 JavaSE 」说说什么是泛型的类型擦除&#xff1f; 参考&鸣谢 面试官&#xff1a;说说什么是泛型的类型擦除&#xff1f; Dr Hydra Java泛型类型擦除以及类型擦除带来的问题 蜗牛大师 文章目录 「 JavaSE 」说说什么是泛型的类型擦除&#xff1f;一、前言二、类型擦除做了…

Windows应急响应排查思路

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 Windows应急响应 一、用户分析1、普通用户2、隐藏用户3、克隆账户 二、日志分析1、Window…

基于互相关性的信号同步

许多测量涉及多个传感器异步采集的数据。如果您要集成信号并以关联式研究它们&#xff0c;您必须同步它们。为此&#xff0c;请使用 xcorr。 例如&#xff0c;假设有一辆汽车经过一座桥。它产生的振动由位于不同位置的三个相同传感器进行测量。信号有不同到达时间。 将三个时…

【技术分享】Livedata粘性事件实现源码解析,让你彻底掌握数据更新机制

概述 Livedata粘性事件是Android中常用的一种观察者模式&#xff0c;它可以让数据在发生改变时通知观察者并更新UI。在实际开发中&#xff0c;我们可能会遇到粘性事件的情况&#xff0c;即先发送了一个数据&#xff0c;后来才有观察者来监听&#xff0c;这时候我们需要保证观察…

集合Map高频面试题

1、介绍下 HashMap 的底层数据结构吧。 在 JDK 1.8&#xff0c;HashMap 底层是由 “数组链表红黑树” 组成&#xff0c;如下图所示&#xff0c;而在 JDK 1.8 之前是由 “数组链表” 组成&#xff0c;就是下图去掉红黑树。 2、为什么使用“数组链表”&#xff1f; 使用 “数组…

如何用 ModelScope 实现 “AI 换脸” 视频

前言 当下&#xff0c;视频内容火爆&#xff0c;带有争议性或反差大的换脸视频总能吸引人视线。虽然 AI 换脸在市面上已经流行了许久&#xff0c;相关制作工具或移动应用也是数不胜数。但是多数制作工具多数情况下不是会员就是收费&#xff0c;而且替换模板有限。以下在实战的角…

三电技术之电机电驱技术

三电技术之电机电驱技术 1 基本功能 电动汽车驱动电机及其控制系统是电动汽车的心脏&#xff0c;是把电能转化为机械能来驱动车辆的部件。它的任务是在驾驶人的控制下&#xff0c;高效率地将动力电池的能量转化为车轮的动能&#xff0c;或者将车轮上的动能反馈到动力电池中。 …

16 个优秀的 Vue 开源项目

为什么我们要关注Vue Vue是一个用于构建用户界面的JavaScript框架。值得关注的是&#xff0c;它在没有谷歌和Facebook的支持下获得了大量的人气。 Vue是结合react和angular的最好的方法&#xff0c;并且拥有一个有凝聚力的&#xff0c;活跃的&#xff0c;能够应对开发问题的大型…

消息队列如何保证消息的幂等性

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 文章目录 什么是幂等性什么是消息的幂等性为什么会出现消息幂等性问题该如何解决消息幂等性问题总结 在分布式系统中&#xff0c;消息队列…

【C++入门必备知识:命名空间与关键字】

【C入门必备知识&#xff1a;命名空间与关键字】 【命名空间】①.命名空间定义Ⅰ.正常定义命名空间Ⅱ.嵌套定义命名空间Ⅲ.合并命名空间 ②.命名空间的使用Ⅰ.命名空间名称及域作用限定符Ⅱ.using成员引入Ⅲ.using namespace名称全部引入 ③.注意事项 【C关键字(C98)】 【命名空…

Ubuntu系统安装docker、docker-compse

环境&#xff1a;Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-100-generic x86_64) 一、安装docker 1.卸载旧版本 ubuntu下自带了docker的库&#xff0c;不需要添加新的源。 但是ubuntu自带的docker版本太低&#xff0c;需要先卸载旧的再安装新的 sudo apt-get remove docker docke…

免费的语音转文字软件有哪些?推荐一款好用的

随着人工智能技术的不断发展&#xff0c;语音识别技术已经得到了广泛的应用。语音转文字软件是其中的一种应用&#xff0c;它能够将人们说出的话语自动转化为文字&#xff0c;从而方便人们进行文本处理、记录、存档等操作。在现实生活中&#xff0c;有很多人需要使用语音转文字…

Redis的哈希表是如何扩容的?

文章目录 一般面试回答哈希表结构字典数据结构解决哈希冲突扩容/缩容对字典的哈希表rehash步骤 渐进式rehash渐进式rehash步骤 相关问题 一般面试回答 redis 解决冲突的方法是使用链地址法&#xff0c;另外当容量不足的时候&#xff0c;则使用Rehash 进行扩容。 Rehash&#x…

96-Linux_UDP实现客户端和服务器端

UDP实现客户端和服务器端 一.udp实现客户端和服务器端的编程流程二.udp实现实现客户端和服务器端所用的接口1.socket2.sendto3.recvfrom 三.udp服务器端代码和客户端的代码1.服务器端2.客户端 一.udp实现客户端和服务器端的编程流程 udp提供无链接的,不可靠的,数据报服务; 二…

burp suite 插件编写-基础

文章目录 前言一、插件的官方文档二、Montoya API jar包结构三、HTTP 处理程序小结 四、代码示例 前言 burp插件入门。入门&#xff0c;我们大概有一个框架&#xff0c;心里不再有怎么做&#xff0c;为什么可以这么做的疑问。现在就要更具体的来回答“怎么做”这个问题。我们通…