一篇文章搞懂如何使用JDBC操作数据库,还有JDBC进阶操作

news2025/1/16 8:18:45

目录

  • 简介
    • 什么是JDBC
    • 如何使用JDBC
      • 1、获取连接
      • 2、操作数据
      • 3、关闭连接,释放资源
      • 使用技巧
  • 查询操作
    • 创建表,插入模拟数据
    • 使用Java查询数据的数据
    • SQL注入问题
    • 使用PreparedStatement查询
  • 更新操作
    • 插入
    • 插入并获取主键
    • 更新
    • 删除
  • JDBC事务
  • JDBC的批量操作
  • JDBC连接池

简介

什么是JDBC

JDBC是Java DataBase Connectivity的缩写。它是让Java程序连接数据的接口。

Java程序要连接数据库,必然是需要通过网络连接,和数据库商制定协议来实现连接的。所以Java推出了JDBC这一套连接数据库的空接口,然后不同的数据库厂商来根据JDBC来实现连接自己数据库的驱动。

对于Java来说连接数据库只需要操作JDBC接口即可,需要连接哪个厂商的数据库就导入哪个厂商的驱动程序。
在这里插入图片描述
看上面这个图并且拿MySQL来举例,Java程序如何连接MySQL数据库,并且如何操作MySQL是通过MySQL数据库的厂商自己开发的MySQL驱动来实现的,并且是依赖Java提供的JDBC标准来实现的。所以Java程序想要操作数据库,只需要导入MySQL驱动,然后调用JDBC接口即可实现。

如何使用JDBC

整体的流程为:1、获取连接;2、访问数据库;3、关闭连接;

1、获取连接

1)连接是什么呢?
在Java中连接是Connection,相当于Java程序和数据库的TCP连接,通过Connection就可以操作数据库了。

2)如何获得呢?

Connection conn = DriverManager.getConnection("连接数据库的URL", "用户名", "密码”);

通过DriverManager.getConnection()来获取一个连接

连接数据库的URL
URL是各个数据库厂商指定的连接格式,例如MySQL是:jdbc:mysql://<hostname>:<port>/<db>?key1=value1&key2=value2

<hostname>是ip地址
<port>是端口
<db>是数据库名
key1和key2是连接的参数,可以有很多个

如下这个连接就是连接到本机MySQL的test数据库

jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8

2、操作数据

获取到JDBC连接后,就可以操作数据库了。

1)通过Connection提供的createStatement()来获取一个Statement对象,

Statement stmt = conn.createStatement()

注意:Statement也是系统资源,使用完成需要释放

2)通过Statement对象就可以执行SELECTUPDATE等操作了
可以通过Statement对象的prepareStatement来执行SQL

更多详细数据库操作将在后面演示

3、关闭连接,释放资源

上面获取到的ConnectionStatement都是系统资源,使用完后一定要及时释放,否则会导致系统资源耗尽,其他程序无法使用。

可以执行它们的close()方法来释放资源,并且要先释放Statement再释放Connection。例如:

// 获取连接
Connection conn = ...
// 获取Statement
Statement stmt = conn.createStatement();

// 释放Statement
stms.close();
// 释放连接
conn.close();

还有一个在执行查询语句时会返回ResultSet对象,ResultSet也是系统资源,使用完成后要释放

使用技巧

可以直接将获取资源的代码写到try括号内try(”获取资源"),这样就不需要显示的指定释放资源了,会自动释放。

try (Connection conn = DriverManager.getConnection("连接数据库的URL", "用户名", "密码”)) {
    try (Statement stmt = conn.createStatement()) {
        try (ResultSet rs = stmt.executeQuery("SELECT ...")) {
        
        }
    }
} catch (SQLException e) {
	 throw new RuntimeException(e);
}

查询操作

创建表,插入模拟数据

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `gender` int(0) DEFAULT NULL,
  `birthdate` date DEFAULT NULL,
  `country` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `created_at` timestamp(0) DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, 'user1', 'user1@example.com', 1, '1990-01-15', 'USA', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (2, 'user2', 'user2@example.com', 2, '1985-05-20', 'Canada', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (3, 'user3', 'user3@example.com', 1, '1998-09-10', 'Australia', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (4, 'user4', 'user4@example.com', 1, '1982-03-02', 'UK', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (5, 'user5', 'user5@example.com', 3, '1995-11-12', 'Germany', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (6, 'user6', 'user6@example.com', 1, '2000-07-25', 'France', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (7, 'user7', 'user7@example.com', 2, '1993-04-08', 'Spain', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (8, 'user8', 'user8@example.com', 0, '1989-08-30', 'Italy', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (9, 'user9', 'user9@example.com', 0, '1987-12-18', 'Japan', '2023-08-12 16:59:57');
INSERT INTO `users` VALUES (10, 'user10', 'user10@example.com', 2, '2002-02-05', 'China', '2023-08-12 16:59:57');

SET FOREIGN_KEY_CHECKS = 1;

使用Java查询数据的数据

我们来直接上代码:

public class SelectDemo { 
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        // 获取连接
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            // 获取Statement
            try (Statement stmt = conn.createStatement()) {
                // 执行查询SQL,返回ResultSet对象
                try (ResultSet rs = stmt.executeQuery("SELECT id,username, email, gender, country FROM users WHERE id=1")) {
                    // 遍历结果集
                    while (rs.next()) {
                        long id = rs.getLong(1);
                        String username = rs.getString(2); // 注意:索引从1开始
                        String email = rs.getString(3);
                        int gender = rs.getInt(4);
                        String country = rs.getString(5);
                        System.out.println("id=" + id + ", username=" + username + ", email=" + email + ", gender=" + gender + ", country=" + country);
                    }
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

看到这个代码,大家应该就一部了然了,下面我们再来讲解一下关键代码
1)、通过执行Statement对象的executeQuery方法来执行SQL,返回了ResultSet对象
2)、ResultSet对象就是此次查询的结果集,可以使用while循环来遍历结果集
3)、通过rs.next()来判断是否还有下一行记录,没有记录则返回false推出循环,如果有则移动到下一行记录
4)、通过getLonggetStringgetInt等方法来获取返回结果,参数是列的索引,从1开始

必须根据SELECT的列的对应位置来调用getLong(1),getString(2)这些方法,否则对应位置的数据类型不对,将报错。

SQL注入问题

上面是通过stms.executeQuery()执行SQL查询的,这个SQL是如果要加参数是通过拼接的方式形成最终的SQL来执行的。

我们来假设一个例子,一个用户要登录,需要通过查询语句来判断用户是否存在:

User login(String name, String pass) {
    ...
    stmt.executeQuery("SELECT * FROM user WHERE login=" + name + " AND pass=" + pass);
    ...
}

这里namepass传的是正常值Tom123456,那么执行的SQL就是:

SELECT * FROM user WHERE login='Tom' AND pass='123456'

看起来好像没有什么问题,但是如果用户传的是Tom OR 1=1 #123456呢?执行的SQL就是:

SELECT * FROM user WHERE login='bob' OR 1=1 # ADN pass='123456'

这条语句有1=1,#将后面的SQL失效了,是不是你写的sql是什么都能查询成功了。这就是SQL注入

那么如何解决SQL注入的问题呢?
最简单的方式就是不要使用SQL拼接的这种方式来操作数据库,使用PreparedStatement来操作数据库。

使用PreparedStatement查询

PreparedStatement是写一个SQL,然后用?作为占位符,然后对占位符传入不同的数据来执行的。

public class SelectDemo {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            try (PreparedStatement ps = conn.prepareStatement("SELECT id,username, email, gender, country FROM users WHERE gender = ? AND country = ?")) {
                ps.setObject(1, 3); // 注意:索引从1开始
                ps.setObject(2, "Germany");
                try (ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        // 这里可用索引的形式,也可以使用列名称的形式
                        long id = rs.getLong("id");
                        String username = rs.getString("username");
                        String email = rs.getString("email");
                        int gender = rs.getInt("gender");
                        String country = rs.getString("country");
                        System.out.println("id=" + id + ", username=" + username + ", email=" + email + ", gender=" + gender + ", country=" + country);
                    }
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

我们来讲解一下这个代码:
1)、首先SQL不是采用拼接的形式,而是传入参数的地方使用占位符?来写SQL
2)、使用ps.setObject(2, "Germany")来传入参数,第一个传索引,从1开始,第二个传真正的参数
3)、获取结果和之前的方式是一样的,不过多了一种可以通过列名称来获取的方式

PreparedStatement是如何避免SQL注入的
在使用PreparedStatement查询的情况下,数据库服务器不会将参数的内容视为 SQL 语句的一部分来进行处理,而是在数据库完成 SQL 语句的编译之后,才套用参数运行。因此就算参数中含有破坏性的指令,也不会被数据库所运行。

使用Java对数据库进行操作时,必须使用PreparedStatement,严禁任何通过参数拼字符串的代码!

更新操作

更新操作和查询不同的是:
更新操作使用executeUpdate()执行SQL,返回结果是int,表示改变的记录数量
查询操作使用executeQuery()执行SQL,返回结果是ResultSet

插入

public class InsertDemo {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO users(username, email, gender) VALUES (?,?,?)")) {
                ps.setObject(1, "张三"); // 注意:索引从1开始
                ps.setObject(2, "zhangsan@qq.com");
                ps.setObject(3, 2);
                int n = ps.executeUpdate(); // 这里使用executeUpdate()
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

插入并获取主键

如果数据表设置了自增主键,那么如果在插入数据返回自增主键呢?而不是返回影响记录行数。

public class InsertDemo1 {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            // 第二个参数设置Statement.RETURN_GENERATED_KEYS
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO users(username, email, gender) VALUES (?,?,?)",
                    Statement.RETURN_GENERATED_KEYS)) {
                ps.setObject(1, "李四"); // 注意:索引从1开始
                ps.setObject(2, "lisi@qq.com");
                ps.setObject(3, 2);
                int n = ps.executeUpdate(); // 这里依然返回的是影响记录行数

                // 要使用getGeneratedKeys()来获取ResultSet对象
                try (ResultSet rs = ps.getGeneratedKeys()) {
                    // 从ResultSet对象来获取主键id
                    if (rs.next()) {
                        Long id = rs.getLong(1); // 注意:索引从1开始
                        System.out.println(id);
                    }
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

我们来解释一下这个代码:
1)、主要是在执行prepareStatement()方法时第二个参数传递了Statement.RETURN_GENERATED_KEYS
2)、其他都是相同的,executeUpdate()返回的依然是影响记录行数
3)、使用getGeneratedKeys()来获取ResultSet对象,然后从ResultSet对象来获取主键id
4)、ResultSet也是资源,所以最后要释放资源

更新

更新操作和新增操作是一样的,把SQL换成UPDATE就可以了,返回结果是影响记录行数

public class UpdateDemo {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            try (PreparedStatement ps = conn.prepareStatement("UPDATE users SET username=? WHERE id=?")) {
                ps.setObject(1, "王五"); // 注意:索引从1开始
                ps.setObject(2, 15);
                int n = ps.executeUpdate(); // 返回更新的行数
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

删除

删除操作和新增、更新操作是一样的,把SQL换成DELETE就可以了,返回结果是影响记录行数

public class DeleteDemo {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            try (PreparedStatement ps = conn.prepareStatement("DELETE FROM users WHERE username=?")) {
                ps.setObject(1, "李四"); // 注意:索引从1开始
                int n = ps.executeUpdate(); // 返回更新的行数
                System.out.println(n);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

JDBC事务

这里我们只演示JDBC是如何操作事务的。

public class TransactionDemo {
    public static void main(String[] args) throws SQLException {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";
        Connection conn = null;
        // 创建连接
        try {
            conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        try {
            // 关闭事务自动提交
            conn.setAutoCommit(false);

            // 新增数据
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO users(username, email, gender) VALUES (?,?,?)")) {
                ps.setObject(1, "张三"); // 注意:索引从1开始
                ps.setObject(2, "zhangsan@qq.com");
                ps.setObject(3, 2);
                int n = ps.executeUpdate(); // 这里使用executeUpdate()
            }

            int a = 10 / 0;

            // 更新数据
            try (PreparedStatement ps = conn.prepareStatement("UPDATE users SET username=? WHERE id=?")) {
                ps.setObject(1, "王五"); // 注意:索引从1开始
                ps.setObject(2, 15);
                int n = ps.executeUpdate(); // 返回更新的行数
            }

            // 提交事务
            conn.commit();

        } catch (Exception e) {
            // 抛出异常回滚事务
            conn.rollback();
            throw new RuntimeException(e);
        } finally {
            // 恢复连接原来的状态
            conn.setAutoCommit(true);
            // 关闭连接
            conn.close();
        }
    }
}

我们来解释一下这里的代码:
1)、获取连接默认事务是自动提交的,即执行一条SQL立即就提交了
2)、所有要conn.setAutoCommit(false)关闭自动提交事务,只有在调用conn.commit()才能提交事务
3)、如果代码抛出了异常,则会在catch语句块中执行conn.rollback()回滚事务。
4)、最后在finally中通过conn.setAutoCommit(true)把连接恢复到初始状态,然后conn.close()关闭连接

如果要设定事务的隔离级别,可以使用如下代码:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

JDBC的批量操作

如果一条一条的执行SQL,那么每一次操作都是一次网络请求,众所周知,网络请求是很慢的,所以如果要执行1000条SQL,那肯定不能说一条一条去执行。

测试发现:1000条SQL,一条一条执行需要1254ms,如果使用批处理,只需要345ms,整整快了3倍。

public class BatchDemo {
    public static void main(String[] args) {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        long start = System.currentTimeMillis();

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            conn.setAutoCommit(false);

            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO users(username, email, gender) VALUES (?,?,?)")) {

                // 批量插入5条记录
                for (int i = 0; i < 1000; i++) {
                    ps.setObject(1, "李四" + i);
                    ps.setObject(2, "lisi" + i + "@qq.com");
                    ps.setObject(3, 2);
                    
                    // 这里不再是执行SQL语句了,变成添加到batch
                    ps.addBatch();
                }

                // 执行所有SQL
                int[] ns = ps.executeBatch();
                
//                // 查看每个SQL的返回结果
//                for (int n : ns) {
//                    System.out.println(n + " inserted.");
//                }
                conn.commit();
            }

            long end = System.currentTimeMillis();

            System.out.println("time:" + (end - start));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

其实批处理和正常操作数据库是一样的,不过在传入参数后是执行ps.addBatch(),最后再使用ps.executeBatch()执行命令。

这种批操作有特别的优化,速度远远快于普通循环执行SQL。

JDBC连接池

在执行JDBC的增删改的操作时,如果每一次操作都来一次打开连接,操作,关闭连接的动作,那么可以想象到创建和销毁JDBC连接的开销有多大。为了避免频繁的创建和销毁JDBC连接,我们可以通过连接池(Connection Pool)复用已经创建好的连接。
在这里插入图片描述

JDBC连接池有一个标准的接口javax.sql.DataSource,注意这个类位于Java标准库中,但仅仅是接口。要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。常用的JDBC连接池有:

  • HikariCP
  • C3P0
  • BoneCP
  • Druid

Druid连接池—目前最热门的连接池,由阿里巴巴开发,所以下面我们用Druid来演示:

先导入Druid依赖

<!-- druid -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.0.9</version>
</dependency>

然后添加配置信息

Properties properties = new Properties();
 properties.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");
properties.setProperty("url", "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8");
properties.setProperty("username", "root");
properties.setProperty("password", "123456");
properties.setProperty("initialSize", "5"); // 初始化连接数量
properties.setProperty("maxActive", "10"); // 最大连接数量
properties.setProperty("maxWait", "3000"); // 连接最大超时时间

然后就可以创建连接池,正常使用了

public class DruidDemo {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");
        properties.setProperty("url", "jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=utf8");
        properties.setProperty("username", "root");
        properties.setProperty("password", "521125");
        properties.setProperty("initialSize", "5"); // 初始化连接数量
        properties.setProperty("maxActive", "10"); // 最大连接数量
        properties.setProperty("maxWait", "3000"); // 连接最大超时时间

        try {
            // 创建连接池
            DataSource dataSe = DruidDataSourceFactory.createDataSource(properties);

            // 从连接池获取连接
            Connection conn = dataSe.getConnection();

            // 执行SQL
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO users(username, email, gender) VALUES (?,?,?)")) {
                ps.setObject(1, "张三"); // 注意:索引从1开始
                ps.setObject(2, "zhangsan@qq.com");
                ps.setObject(3, 2);
                int n = ps.executeUpdate(); // 这里使用executeUpdate()
            }

            // 释放连接,这里是把连接返回连接池
            conn.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

1)、连接池内部维护了若干个Connection实例,如果调用dataSe .getConnection(),就选择一个空闲连接,并标记它为【正在使用】然后返回
2)、对Connection调用close(),那么就把连接再次标记为【空闲】从而等待下次调用

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

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

相关文章

微服务与Nacos概述-5

引入OpenFeign 添加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><groupId>com.alibaba.cloud</groupId>…

【Linux】CentOS7.3环境下安装Mysql 8.0.30

CentOS7.3环境下安装Mysql 8.0.30 1.mysql官网下载安装包 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 2.上传安装包到linux服务器并解压 tar -vxf mysql-8.0.30-linux-glibc2.12-x86_64.tar.xz修改名称 mv mysql-8.0.30-linux-glibc2.12-x86_6…

中心极限定理例题

关于大数定律的两个题目。 例1 注意牢记公式&#xff1a; P { X } P { ∑ i 1 n x i − n μ n σ < x } ∫ − ∞ x e − x 2 2 d x 2 π P\{ X\} P \{\frac { \sum_{i1}^{n} x_i - n \mu}{\sqrt {n} \sigma} < x \} \frac {\int _{-\infty} ^{x} e ^{- \frac {x^…

keil下载程序具体过程3:从jlink的log开始

引言 本篇文章开始&#xff0c;跟着jlink的log&#xff0c;我们将跟踪镜像文件具体的下载过程。 一、jlink的log 下面的是keil点击Download按钮输出的log。 FromELF: creating hex file... ".\Objects\Project.axf" - 0 Error(s), 0 Warning(s). Build Time Elapsed…

C++ 语言的输入与输出

C 标准 I/O 库包含 iostream、fstream 和 sstringstream。iostream、fstream 比较常用&#xff0c;一般操作于输入和输出&#xff0c;相较于前两者来说 sstringstream 的出现频率就低了许多&#xff0c;一般操作于数据的格式化。为了能更好的理解 C 语言的标准 I/O 库&#xff…

通过MATLAB自动产生Hamming编译码的verilog实现,包含testbench

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 1. 原理 1.1 编码规则 1.2 错误检测和纠正 2. 实现过程 2.1 编码过程 2.2 解码过程 3. 应用领域 3.1 数字通信 3.2 存储系统 3.3 ECC内存 3.4 数据传输 5.算法完整程序工程 1.算法…

MySQL多表关联查询

目录 1. inner join&#xff1a; 2. left join&#xff1a; 3. right join&#xff1a; 4.自连接 5.交叉连接&#xff1a; 6、联合查询 7、子查询 1. inner join&#xff1a; 代表选择的是两个表的交差部分。 内连接就是表间的主键与外键相连&#xff0c;只取得键值一致…

全国各地区数字经济工具变量-文本词频统计(2002-2023年)

数据简介&#xff1a;本数据使用全国各省工作报告&#xff0c;对其中数字经济相关的词汇进行词频统计&#xff0c;从而构建数字经济相关的工具变量。凭借数字经济政策供给与数字经济发展水平的相关系数的显著性作为二者匹配程度的划分依据&#xff0c;一定程度上规避了数字经济…

Vue.js2+Cesium1.103.0 九、淹没分析效果

Vue.js2Cesium1.103.0 九、淹没分析效果 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"><spanid"button"style"position: absolute; right: 50px; top: 50px; z-index: 999; font-size: 24px…

旅卦-火山旅

前言&#xff1a;人生就像一趟旅行&#xff0c;为谋生奔波也是旅&#xff0c;旅是人生的常态&#xff0c;我们看一下易经里的旅卦&#xff0c;分析下卦辞和爻辞以及自己的理解。 目录 卦辞 爻辞 总结 卦辞 旅&#xff1a;小亨&#xff0c;旅贞吉。 卦序&#xff1a;穷大者…

oracle 增加控制文件

oracle 增加控制文件 1、看control_file路径 SQL> show parameter controlNAME TYPE VALUE ------------------------------------ ----------- ------------------------------ control_file_record_keep_time integer …

Grafana展示k8s中pod的jvm监控面板/actuator/prometheus

场景 为保障java服务正常运行&#xff0c;对服务的jvm进行监控&#xff0c;通过使用actuator组件监控jvm情况&#xff0c;使用prometheus对数据进行采集&#xff0c;并在Grafana展现。 基于k8s场景 prometheus数据收集 配置service的lable&#xff0c;便于prometheus使用labl…

大数据课程I2——Kafka的架构

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的架构&#xff1b; ⚪ 掌握Kafka的Topic与Partition&#xff1b; 一、Kafka核心概念及操作 1. producer生产者&#xff0c;可以是一个测试线程&#xff0c;也…

青大数据结构【2014】

一、单选 二、简答 为了解决顺序队列的假溢出问题,提出了循环队列,即把存储队列的表从逻辑上看成一个环 判别队列空和满有三种方法: 1)采用计数器判别,空时,计数器为0;满时,计数器为maxsize; 2)另设一个布尔变量以匹配队列的满和空; 3)少用一个元素的空间,约…

无向图邻接矩阵(C++ 代码)

#include<iostream>//无向图邻接矩阵 #define mvnum 100 using namespace std; typedef char Vertextype;//顶点数据类型 typedef int Arctype;//边权值类型 typedef struct {Vertextype vexs[mvnum];//顶点表Arctype arcs[mvnum][mvnum];//邻接矩阵int vexnum, arcnum;/…

解决macOS执行fastboot找不到设备的问题

背景 最近准备给我的备用机Redmi Note 11 5G刷个类原生的三方ROM&#xff0c;MIUI实在是用腻了。搜罗了一番&#xff0c;在XDA上找到了一个基于Pixel Experience开发的ROM&#xff1a;PixelExperience Plus for Redmi Note 11T/11S 5G/11 5G/POCO M4 Pro 5G (everpal)&#xf…

JavaScript激活严格模式

在JavaScript中&#xff0c;严格模式是一种特殊的模式&#xff0c;通过’use strict’;去激活严格模式&#xff01;在 JavaScript 中&#xff0c;“use strict” 是一种指令&#xff0c;表示在代码运行时启用严格模式&#xff0c;从而禁止使用一些不安全或者不规范的语法&#…

MFC第三十天 通过CToolBar类开发文字工具栏和工具箱、GDI+边框填充以及基本图形的绘制方法、图形绘制过程的反色线模型和实色模型

文章目录 CControlBar通过CToolBar类开发文字工具栏和工具箱CMainFrame.hCAppCMainFrm.cppCMainView.hCMainView.cppCEllipse.hCEllipse.cppCLine.hCLine.cppCRRect .hCRRect .cpp CControlBar class AFX_NOVTABLE CControlBar : public CWnd{DECLARE_DYNAMIC(CControlBar)pro…

【高频面试题】JVM篇

文章目录 一、JVM组成1.什么是程序计数器2.什么是Java堆&#xff1f;3.能不能介绍一下方法区(元空间&#xff09;4.你听过直接内存吗5.什么是虚拟机栈6.垃圾回收是否涉及栈内存&#xff1f;7.栈内存分配越大越好吗&#xff1f;8.方法内的局部变量是否线程安全&#xff1f;9.什么…

ntfy 实现消息订阅和通知(无需注册、无需服务器,太好了)

目录 一、下载 ntfy 的Delphi 库&#xff08;打开ntfy for Delphi 的开源库地址&#xff09; 二、创建发布消息程序 三、订阅&#xff08;接收&#xff09;消息程序 四、说明&#xff1a; 五、程序下载&#xff08;包含库&#xff09;&#xff1a; ntfy 可让你在任何电脑上通…