java基础学习——jdbc基础知识详细介绍
引言
数据的存储
我们在开发 java 程序时,数据都是存储在内存中的,属于临时存储,当程序停止或重启时,内存中的数据就会丢失,我们为了解决数据的长期存储问题,有以下解决方案:
- 通过 IO流书记,将数据存储在本地磁盘中,这样就解决了持久化问题,但是数据没有结构和逻辑,不方便管理和维护。
- 通过关系型数据库(例如 MySQL),将数据按照特定的格式交由数据库管理系统维护,关系型数据库是通过库和表分隔不同的数据,表中的数据的存储方式是行和列,区分相同格式不同值的数据。
数据的操作
数据存储在数据库中,仅仅只是解决了我们数据存储的问题,当我们程序运行时,需要读取数据以及对数据做增删改的操作,那么我们如何通过 java 程序对数据库中的数据做增删改查呢?
答案就是今天的主角—— jdbc
jdbc
什么是 jdbc
JDBC(Java Database Connectivity)是Java编程语言中用于执行 SQL 语句的 API,它为数据库访问提供了一种标准的方法。通过使用 JDBC API,开发者可以以一种统一的方式与各种不同的数据库进行交互,而无需关心底层的数据库驱动细节。
以下是关于 JDBC 的一些关键点:
- 通用性:JDBC 允许 Java 应用程序连接到几乎任何 SQL 数据库,包括 MySQL、Oracle、PostgreSQL、Microsoft SQL Server 等。
- 数据库无关性:编写一次代码,就可以在不同类型的数据库上运行,只要相应地更换数据库驱动即可。
- 驱动程序:为了使 JDBC 能够与特定的数据库通信,需要有相应的数据库驱动程序。这些驱动程序实现了JDBC 接口,并负责处理与特定数据库的通信协议。
- JDBC URL:每个数据库都有一个唯一的 URL 格式,用于建立到数据库的连接。这个 URL 通常包含数据库类型、主机地址、端口号和数据库名称等信息。
- 连接管理:JDBC 提供了
java.sql.Connection
接口来表示与数据库的连接。开发者可以通过DriverManager.getConnection()
方法获取连接。 - 执行 SQL 命令:通过
Statement
、PreparedStatement
或CallableStatement
对象,可以发送 SQL 语句给数据库并处理返回的结果。 - 结果集处理:执行查询后,会返回一个
ResultSet
对象,它包含了查询结果的数据。可以通过迭代ResultSet
来读取每一行数据。 - 事务管理:JDBC 支持事务控制,包括提交(
commit
)和回滚(rollback
)操作,这使得可以在一组相关联的操作完成后作为一个整体来提交或撤销更改。 - 性能优化:如使用预编译的 SQL 语句(
PreparedStatement
)可以提高性能,减少 SQL 注入风险;批量更新(Batch Updates
)可以一次性执行多个插入、更新或删除操作。 - 资源清理:在完成数据库操作后,必须正确关闭所有打开的资源(如
Connection
、Statement
和ResultSet
),以防止内存泄漏。
随着 Java 的发展,JDBC 也不断演进,增加了新的特性和改进了性能。例如,JDBC 4.0引入了自动加载驱动程序的功能,简化了开发过程。此外,JDBC 还支持分布式事务、XA资源管理和更多高级特性,以满足企业级应用的需求。
jdbc 的核心组成
- 接口规范:
- 为了项目代码的可移植性,可维护性,SUN公司从最初就制定了 java 程序连接各种数据库的统一接口规范,这样的话,不管是连接哪一种 DBMS 软件,java 代码都可以保持一致性。
- 接口存储在
java.sql
和javax.sql
包下。
- 实现规范:
- 因为各个数据库厂商的 DBMS 软件各有不同,那么各自的内部如何通过 sql 实现增删改查等操作管理数据,只有这个数据库厂商自己清楚,因此把接口规范的内部实现由各个数据库厂商自己实现。
- 厂商将实现内容和过程封装成 jar 文件包,我们程序员只需要将 jar 包引入到项目中集成即可,就可以开发调用实现过程操作数据库了。
jdbc 实现的常用接口和类
- DriverManager:
DriverManager
类管理一组JDBC驱动程序,并选择适当的驱动程序来建立到给定数据库URL的连接。它还处理加载和注册JDBC驱动程序的任务。
- Driver接口:
- 每个JDBC驱动程序必须实现
java.sql.Driver
接口。该接口定义了用于与数据库通信的方法。当DriverManager
尝试建立连接时,它会使用这些方法。
- 每个JDBC驱动程序必须实现
- Connection接口:
Connection
对象代表与特定数据库的连接。通过这个对象可以创建Statement
、PreparedStatement
或CallableStatement
对象来执行SQL命令,并且可以管理事务。
- Statement接口:
Statement
接口用于执行静态的SQL语句并返回结果。它是执行SQL语句的基础,包括简单的查询和更新操作。
- PreparedStatement接口:
PreparedStatement
是Statement
的子接口,用于执行预编译的SQL语句。它允许设置参数化查询,这有助于防止SQL注入攻击,并可能提高性能。
- CallableStatement接口:
CallableStatement
也是Statement
的子接口,专门用于调用数据库中的存储过程。它可以处理输入和输出参数。
- ResultSet接口:
ResultSet
对象封装了执行SQL查询后得到的结果表。它提供了遍历表格数据的方法以及获取每一列数据值的方法。
- SQLException类:
SQLException
是一种受检异常,用于报告数据库访问错误。它包含了有关数据库错误的信息,如错误代码和消息文本。
- DataSource接口(自JDBC 2.0引入):
DataSource
提供了一种更灵活的获取数据库连接的方式,特别是在容器环境中。它不仅支持标准的用户名/密码认证,还可以支持分布式事务和其他高级特性。
- RowSet接口(自JDBC 2.0扩展包引入):
RowSet
是一个特殊的ResultSet
,它增加了滚动和更新能力,并且可以在断开连接的情况下工作。它分为连接型(例如JdbcRowSet
)和非连接型(例如CachedRowSet
)两种类型。
这些组件一起工作,使得Java应用程序能够以一种标准化的方式连接到不同的数据库系统,执行SQL查询和更新,并处理返回的数据。随着JDBC规范的发展,新的功能被添加进来以支持更多的特性和改进性能。
jdbc 快速开始
搭建 jdbc
- 准备数据库。
- 官网下载数据库连接驱动 jar 包。
- 创建 java 项目,在项目下创建 lib 文件夹,将下载的驱动 jar 包复制到文件夹里。
- 选中 lib 文件夹右键 -> Add as Library,与项目集成。
- 编写代码。
代码实现
数据库
CREATE DATABASE `myjdbc`;USE `myjdbc`;DROP TABLE IF EXISTS `student`;CREATE TABLE `student` (`id` int NOT NULL AUTO_INCREMENT COMMENT '学生编号',`name` varchar(10) NOT NULL COMMENT '学生姓名',`age` int NOT NULL COMMENT '学生年龄',`score` double(10,5) NOT NULL COMMENT '学生成绩',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;INSERT INTO `student` (`id`, `name`, `age`, `score`) VALUES (1, '张三', 18, 59.50000);
INSERT INTO `student` (`id`, `name`, `age`, `score`) VALUES (2, '李四', 3, 70.00000);
INSERT INTO `student` (`id`, `name`, `age`, `score`) VALUES (3, '王五', 66, 30.00000);
INSERT INTO `student` (`id`, `name`, `age`, `score`) VALUES (4, '赵六', 100, 22.33333);
INSERT INTO `student` (`id`, `name`, `age`, `score`) VALUES (5, '田七', 28, 30.00000);
编写 java 代码
步骤:
- 注册驱动。
- 获取连接对象。
- 获取执行 sql 语句的对象。
- 编写 sql 语句并执行。
- 处理结果。
- 释放资源。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;public class Demo01 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.获取执行sql语句的对象Statement statement = connection.createStatement();// 4.编写sql语句并执行String sql = "select * from student";ResultSet result = statement.executeQuery(sql);// 5.处理结果while (result.next()) {int id = result.getInt("id");String name = result.getString("name");int age = result.getInt("age");double score = result.getDouble("score");System.out.println(id + "\t" + name + "\t" + age + "\t" + score);}// 6.释放资源result.close();statement.close();connection.close();}
}
结果:
1 张三 18 59.5
2 李四 3 70.0
3 王五 66 30.0
4 赵六 100 22.33333
5 田七 28 30.0
核心 API 理解
注册驱动
-
Class.forName("com.mysql.cj.jdbc.Driver");
学习过反射的可以看出,这段代码是在加载 com.mysql.cj.jdbc
包下的 Driver
,我们进入这个类,可以看到有这样一段代码:
static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}
}
这个类在加载时会执行 DriverManager.registerDriver(new Driver());
,这就是注册驱动
- 在Java 中,当使用 JDBC (Java Database Connectivity)连接数据库时,需要加载数据库特定的驱动程序,以便与数据库进行通信。加载驱动程序的目的是为了注册驱动程序,使得 JDBC API 能够多识别并与特定的数据库进行交互。
- 从 JDK6 开始,不再需要显示地调用
Class.forName()
来加载 JDBC 驱动程序,只要在类路径中集成了对应的 jar 文件,会自动在初始化时注册驱动程序。
DriverManager
DriverManager
类管理一组 JDBC 驱动程序,并选择适当的驱动程序来建立到给定数据库 URL 的连接。它负责加载和注册 JDBC 驱动程序,以及创建 Connection
对象。
getConnection(String url, String user, String password)
:尝试根据提供的数据库URL、用户名和密码建立连接。registerDriver(Driver driver)
和deregisterDriver(Driver driver)
:显式地注册或注销驱动程序
Connection
Connection
接口是 JDBC API 的重要接口,用于建立与数据库的通信通道,换而言之,Connection
对象不为空,则代表一次数据库连接。- 在建立连接时,需要指定数据库 url,用户名,密码参数。格式:
# jdbc:mysql://IP地址:端口号/数据库名称?参数键值对1&参数键值对2&...
jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
- 负责管理事务,提供了
commit
和rollback
方法,用于提交事务和回滚事务。 - 可以创建
Statement
对象,用于执行 sql 语句并与数据库进行交互。 - 在使用 jdbc 技术时,必须要先获取
Connection
对象,在使用完毕后,要释放资源,避免资源占用浪费及泄露。
常用方法
createStatement()
:创建一个Statement
对象用于发送SQL语句。prepareStatement(String sql)
:创建一个PreparedStatement
对象,它可以包含IN参数。prepareCall(String sql)
:创建一个CallableStatement
对象用于调用存储过程。setAutoCommit(boolean autoCommit)
:设置是否自动提交更改。commit()
和rollback()
:手动提交或回滚事务。close()
:关闭连接并释放资源。
Statement
Statement
接口用于执行 sql 语句并与数据库进行交互,通过Statement
对象,可以向数据库发送 sql 语句并获取执行结果。- 结果可以是一个或多个结果。
- 增删改:受影响行数单个结果。
- 查询:单行单列、多行多列、单行多列等结果。
- 但是
Statement
接口在执行 sql 语句时,会产生 sql 注入问题:- 因为它是将查询条件与 sql 语句直接拼在一起,不会验证,这时候黑客可以钻漏洞在查询条件里加上 sql 语句,让 sql 的查询条件始终为 true,例如查询条件为
where user = 'root'
,黑客在查询条件输入xxx' or '1'='1
,这样最后拼接的 sql 为where user = 'xxx' or '1'='1'
,这样结果也为 true。
- 因为它是将查询条件与 sql 语句直接拼在一起,不会验证,这时候黑客可以钻漏洞在查询条件里加上 sql 语句,让 sql 的查询条件始终为 true,例如查询条件为
常用方法
executeQuery(String sql)
:执行查询语句并返回结果集。executeUpdate(String sql)
:执行插入、更新或删除语句,并返回受影响的行数。execute(String sql)
:执行任意SQL语句,对于复杂的操作非常有用。addBatch(String sql)
和executeBatch()
:用于批量执行多个SQL语句。
PreparedStatement
PreparedStatement
是Statement
接口的子接口,用于执行预编译的 sql 查询,作用如下:- 预编译 sql 语句:在创建
PreparedStatement
时,就会预编译 sql 语句,也就是 sql 语句已经固定。 - 防止 sql 注入:
PreparedStatement
支持参数化查询,将数据作为参数传递到 sql 语句中,采用?
占位符的方式,将传入的参数用一对单引号包裹起来,无论传递什么都只作为值,可以有效防止传入关键字或值导致 sql 注入问题。 - 性能提升:
PreparedStatement
是预编译 sql 语句,同一 sql 语句多次执行的情况下,可以复用,不比每次重新编译和解析。
- 预编译 sql 语句:在创建
- 更加安全,效率更高
常用方法
setString(int parameterIndex, String x)
等方法:为SQL语句中的参数占位符设置值。executeQuery()
和executeUpdate()
:与Statement
类似,但针对预编译的SQL语句。
代码示例
// 3.获取执行sql语句的对象
PreparedStatement statement = connection.prepareStatement("select * from student where name = ?");// 4.编写sql语句并执行
statement.setString(1, "张三");
ResultSet result = statement.executeQuery();
ResultSet
ResultSet
用于表示从数据库中执行 sql 语句所返回的结果集,它提供了一种用于遍历和访问查询结果的方式。- 遍历结果:
ResultSet
可以使用next()
方法将游标移动到结果集的下一行,逐行遍历数据库查询的结果,返回值为boolean
,true
代表有下一行结果,false
则代表没有。 - 获取单列结果:可以通过
getXxx()
的方法获取单列数据库,Xxx
代表数据类型,支持索引和列名进行获取。
常用方法
next()
:将游标移动到下一行。getXxx(int columnIndex)
或getXxx(String columnName)
:获取当前行中指定列的数据值,其中XXX
代表数据类型。beforeFirst()
、afterLast()
、absolute(int row)
等方法:控制游标的移动位置。
基于 PreparedStatement 实现 crud
查询单行单列
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class Demo02 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) AS count FROM student");// 4.执行sql语句ResultSet result = statement.executeQuery();// 5.获取结果(如果明确只有一个结果,也要进行一次next()方法判断)if (result.next()) {// 根据列名获取,上面sql设置了别名,所以这里取countint count = result.getInt("count");System.out.println("总数为:" + count);}// 6.释放资源result.close();statement.close();connection.close();}
}
查询单行多列
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class Demo03 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("SELECT id,`name`,age,score FROM student WHERE id = ?");// 4.补充占位符,执行sql语句statement.setInt(1, 5);ResultSet result = statement.executeQuery();// 5.获取结果while (result.next()) {int id = result.getInt("id");String name = result.getString("name");int age = result.getInt("age");double score = result.getDouble("score");System.out.println(id + "\t" + name + "\t" + age + "\t" + score);}// 6.释放资源result.close();statement.close();connection.close();}
}
查询多行多列
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class Demo04 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("SELECT id,`name`,age,score FROM student WHERE age > ?");// 4.补充占位符,执行sql语句statement.setInt(1, 10);ResultSet result = statement.executeQuery();// 5.获取结果while (result.next()) {int id = result.getInt("id");String name = result.getString("name");int age = result.getInt("age");double score = result.getDouble("score");System.out.println(id + "\t" + name + "\t" + age + "\t" + score);}// 6.释放资源result.close();statement.close();connection.close();}
}
新增数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo05 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("INSERT INTO student(`name`,age,score) VALUES (?,?,?)");// 4.补充占位符,执行sql语句statement.setString(1, "钱八");statement.setInt(2, 9);statement.setDouble(3, 99.9);int result = statement.executeUpdate();// 5.结果result是受影响的行数if (result > 0) {System.out.println("添加成功");} else {System.out.println("添加失败");}// 6.释放资源statement.close();connection.close();}
}
修改数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo06 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("UPDATE student SET score = ? WHERE id = ?");// 4.补充占位符,执行sql语句statement.setDouble(1, 66.66);statement.setInt(2, 6);int result = statement.executeUpdate();// 5.结果result是受影响的行数if (result > 0) {System.out.println("修改成功");} else {System.out.println("修改失败");}// 6.释放资源statement.close();connection.close();}
}
删除数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo07 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("DELETE FROM student WHERE id = ?");// 4.补充占位符,执行sql语句statement.setInt(1, 6);int result = statement.executeUpdate();// 5.结果result是受影响的行数if (result > 0) {System.out.println("删除成功");} else {System.out.println("删除失败");}// 6.释放资源statement.close();connection.close();}
}
实体类和 orm 思想
- 在使用 jdbc 操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了 java 中变成了一个一个的变量,不利于维护和管理,由于 java 是面向对象的,所以一个表应该对应的是一个类,一行数据就对应的是 java 中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类。
- orm(Object Relational Mapping,对象关系映射)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性。
- jdbc 的这种过程我们称其为手动 orm,后续会升级为 orm 框架,例如 Mybatis 等。
实体类代码示例
package pojo;public class Student {private int id;private String name;private int age;private double score;public Student() {}public Student(int id, String name, int age, double score) {this.id = id;this.name = name;this.age = age;this.score = score;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
我们通常会把所有的实体类放在同一个包下。
查询单个数据
import pojo.Student;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class Demo08 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("SELECT id,`name`,age,score FROM student WHERE id = ?");// 4.补充占位符,执行sql语句statement.setInt(1, 1);ResultSet result = statement.executeQuery();// 创建一个student对象Student student = new Student();// 5.获取结果while (result.next()) {int id = result.getInt("id");String name = result.getString("name");int age = result.getInt("age");double score = result.getDouble("score");// 将数据映射到对象上student.setId(id);student.setName(name);student.setAge(age);student.setScore(score);System.out.println(student);}// 6.释放资源result.close();statement.close();connection.close();}
}
查询多个数据,使用集合封装
import pojo.Student;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;public class Demo09 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象PreparedStatement statement = connection.prepareStatement("SELECT id,`name`,age,score FROM student");// 4.补充占位符,执行sql语句ResultSet result = statement.executeQuery();// 创建一个student集合List<Student> studentList = new ArrayList<>();// 5.获取结果while (result.next()) {Student student = new Student();int id = result.getInt("id");String name = result.getString("name");int age = result.getInt("age");double score = result.getDouble("score");// 将数据映射到对象上student.setId(id);student.setName(name);student.setAge(age);student.setScore(score);studentList.add(student);}studentList.forEach(System.out::println);// 6.释放资源result.close();statement.close();connection.close();}
}
主键回显
在数据中,执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在 java 程序中,我们执行完新增后,只能得到受影响行数,无法得知当前新增数据的主键值。在 java 程序中获取数据库中插入新数据后的主键值,并赋值给 java 对象,此操作为主键回显。
代码示例
只要在预编译 sql 时添加一个参数 Statement.RETURN_GENERATED_KEYS
即可。
import pojo.Student;import java.sql.*;public class Demo10 {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2.获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 3.预编译sql语句获取对象,告知数据库,返回新增数据主键的值String sql = "INSERT INTO student(`name`,age,score) VALUES (?,?,?)";PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);// 4.补充占位符,执行sql语句Student student = new Student(0, "钱八", 9, 99.9);statement.setString(1, student.getName());statement.setInt(2, student.getAge());statement.setDouble(3, student.getScore());int result = statement.executeUpdate();// 5.结果result是受影响的行数if (result > 0) {System.out.println("添加成功");// 获取新增数据的主键值,返回到student对象的id属性// 返回的主键值是一个单行单列的结果集ResultSet resultSet = statement.getGeneratedKeys();while (resultSet.next()) {int id = resultSet.getInt(1);student.setId(id);}System.out.println(student);resultSet.close();} else {System.out.println("添加失败");}// 6.释放资源statement.close();connection.close();}
}
批量操作
如果想要一次性插入多条数据,常用的方法就是使用循环,但是循环本质是执行多次插入操作,循环每进行一次就会执行一次 insert
插入,也就要与数据库交互一次,非常消耗时间。
批量操作的本质是对 sql 语句的拼接,将要执行的多条 insert ... value ()
语句拼接成 insert ... values (),(),.....
一条 sql 语句,这样可以减少与数据库的交互次数,节省时间。
循环插入代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo11 {public static void main(String[] args) throws Exception {// 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 预编译sql语句String sql = "INSERT INTO student(`name`,age,score) VALUES (?,?,?)";PreparedStatement statement = connection.prepareStatement(sql);// 获取开始执行,执行循环插入10000条数据long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {statement.setString(1, "赵六"+i);statement.setInt(2, 55);statement.setDouble(3, 60);statement.executeUpdate();}// 获取结束时间,查看耗费时间long end = System.currentTimeMillis();System.out.println("耗费时间:"+(end-start));// 释放资源statement.close();connection.close();}
}
批量插入代码
想要执行批量插入,需要注意以下几点:
- 必须在连接数据库的 url 的
?
后面追加rewriteBatchedStatements=true
,允许批量操作。 - 新增 sql 语句必须使用 values,且语句最后不要追加
;
结束。 - 调用
addBatch()
方法,将 sql 语句进行批量添加操作。 - 统一执行批量操作,调用
executeBatch()
方法。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo12 {public static void main(String[] args) throws Exception {// 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 获取连接对象String url = "jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";String username = "root";String password = "123456";Connection connection = DriverManager.getConnection(url, username, password);// 预编译sql语句String sql = "INSERT INTO student(`name`,age,score) VALUES (?,?,?)";PreparedStatement statement = connection.prepareStatement(sql);// 获取开始执行,执行循环插入10000条数据long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {statement.setString(1, "赵六"+i);statement.setInt(2, 55);statement.setDouble(3, 60);statement.addBatch();}statement.executeBatch();// 获取结束时间,查看耗费时间long end = System.currentTimeMillis();System.out.println("耗费时间:"+ (end-start));// 释放资源statement.close();connection.close();}
}
可以对比,批量插入的时间有了显著降低。
连接池
现有问题
- 每次操作数据库都要获取新连接,使用完毕后就 close 释放,频繁的创建和销毁造成资源浪费。
- 连接的数量无法把控,对服务器来说压力巨大。
连接池介绍
连接池本质上就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接,管理连接,释放连接等操作。
预先创建数据库连接放入连接池,用户在请求时,通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了创建的效率。
当池中无连接可用,且未达到上限时,连接池会新建连接。
池中连接达到上限,用户请求会等待,可以设置超时时间。
常见连接池
Druid
Druid 是阿里巴巴开源的一个综合数据库连接池解决方案,除了连接池功能外,还提供了 SQL 解析、监控等功能。
- 特点:
- 高性能,经过大规模生产环境验证。
- 内置监控面板,可以实时查看连接池状态和性能指标。
- 支持多种数据库类型。
- 提供了 SQL 注入防护机制。
- 可以动态调整连接池参数。
- 适用场景:特别适合于中国开发者,因为它是国内广泛使用的连接池之一,并且有中文文档和支持。
Hikari
HikariCP 是一个高性能、轻量级的连接池库,因其出色的性能而广受欢迎。它的设计目标是成为最快的 Java 连接池之一。
- 特点:
- 极高的性能。
- 简单配置,易于使用。
- 支持自动加载驱动程序。
- 提供详细的监控和统计信息。
- 兼容多种数据库(如 MySQL、PostgreSQL 等)。
- 适用场景:适用于追求极致性能的应用,特别是在高并发环境下。
Apache DBCP (Commons DBCP)
Apache DBCP 是由 Apache Commons 项目提供的一个成熟的连接池解决方案。
- 特点:
- 功能丰富,支持广泛的配置选项。
- 集成了与 Apache Tomcat 的紧密协作。
- 提供了两种不同的实现:BasicDataSource 和 PoolingDriver。
- 包含了对 JMX 的支持,便于管理和监控。
- 适用场景:适合那些希望使用稳定、功能齐全的连接池的应用程序,尤其是在 Web 应用中。
C3P0
C3P0 是另一个流行的开源连接池库,以其灵活性和可配置性著称。
- 特点:
- 强大的配置选项,包括自动测试连接、预加载连接等功能。
- 支持多数据源配置。
- 内置了对 Hibernate 的支持。
- 提供了良好的文档和社区支持。
- 适用场景:适用于需要高度定制化配置的应用程序,特别是当您需要复杂的连接管理策略时。
在目前的开发中,Druid 和 Hikari 是使用最多的两个连接池。
Druid 连接池使用
使用步骤
- 引入 jar 包。
- 创建
DruidDataSource
连接池对象 - 设置连接池的配置信息(包含必要信息和非必要信息)
- 通过连接池获取连接对象
- 回收连接(不是释放连接,而是将连接归还给连接池,给其它线程进行复用)
连接池配置信息
基本连接属性(必要信息)
url
:数据库的 JDBC URL。username
:用于连接数据库的用户名。password
:用于连接数据库的密码。driverClassName
:JDBC驱动程序类名(通常可以通过URL自动检测)。
以上4种属性是创建连接池时必须配置的属性,下面几种是非必要配置的属性,根据实际需求进行相关配置即可。
连接池大小配置
initialSize
:初始化时创建的连接数。minIdle
:最小空闲连接数。maxActive
:最大活跃连接数,即同时可用的最大连接数。maxWait
:当没有可用连接时,等待获取连接的最大时间(毫秒),默认值为-1表示无限期等待。
连续回收策略
timeBetweenEvictionRunsMillis
:检测连接是否空闲的时间间隔(毫秒),用于回收空闲连接,默认是60秒。minEvictableIdleTimeMillis
:连接在池中最小生存时间(毫秒),超过这个时间如果空闲则被回收,默认是1800秒(30分钟)。validationQuery
:用来验证连接是否有效的SQL查询语句,例如SELECT 'x'
或SELECT 1
。testWhileIdle
:建议设置为true
,表示在空闲时测试连接的有效性。testOnBorrow
和testOnReturn
:分别表示在从池中借用连接前和归还连接后是否进行有效性测试,默认都是false
。
PreparedStatement 缓存
poolPreparedStatements
:是否开启PSCache,默认为false
。对于支持的数据库,如Oracle,可以显著提高性能。maxPoolPreparedStatementPerConnectionSize
:每个连接上最大的PSCache数量。
监控与统计
filters
:指定要启用的过滤器,多个过滤器用逗号分隔。常见的过滤器包括:stat
:用于收集和展示连接池的状态信息。log4j
或common-log
:用于记录SQL日志。wall
:提供简单的SQL防火墙功能,防止某些类型的SQL注入攻击。
事务相关配置
defaultAutoCommit
:设置默认的自动提交模式,默认为null
,意味着使用数据库的默认设置。defaultTransactionIsolation
:设置默认的事务隔离级别,默认为null
,同样表示使用数据库的默认设置。
其它配置
removeAbandoned
:是否移除长时间未关闭的物理连接,默认为false
。removeAbandonedTimeout
:移除长时间未关闭的物理连接之前等待的超时时间(秒),默认为60秒。logAbandoned
:是否记录移除长时间未关闭的物理连接事件,默认为false
。
代码示例
硬编码实现(不推荐)
硬编码:将连接池的配置信息与 java 程序耦合在一起。
import com.alibaba.druid.pool.DruidDataSource;import java.sql.Connection;public class Demo12 {public static void main(String[] args) throws Exception {// 1.创建 DruidDataSource 连接池对象DruidDataSource dataSource = new DruidDataSource();// 2.设置连接池的配置信息(包含必要信息和非必要信息)// 2.1 设置必要信息dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("123456");// 2.2 设置非必要信息dataSource.setInitialSize(10);dataSource.setMaxActive(20);// 3.通过连接池获取连接对象Connection connection = dataSource.getConnection();// 实现crud等操作System.out.println(connection);// 4.回收连接(不是释放连接,而是将连接归还给连接池,给其它线程进行复用)connection.close();}
}
软编码方式(推荐)
软编码:是指在项目目录下创建 resources
文件夹,标识该文件夹为资源目录,创建 db.properties
配置文件,将连接信息定义在该文件中。
步骤:
- 创建
Properties
集合,用于存储外部配置文件的 key 和 values 值。 - 读取外部配置文件,获取输入流,加载到
Properties
集合里。 - 基于
Properties
集合构建DruidDataSource
连接池。 - 通过连接池获取连接对象。
- 回收连接。
代码实现:
db.properties
代码:
# 必要信息
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/myjdbc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username=root
password=root# 非必要信息
initialSize=10
maxActive=20
main
代码:
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class Demo13 {public static void main(String[] args) throws Exception {// 1.创建Properties集合,用于存储外部配置文件的key和values值。Properties properties = new Properties();// 2.读取外部配置文件,获取输入流,加载到Properties集合里。InputStream inputStream = Demo13.class.getClassLoader().getResourceAsStream("db.properties");properties.load(inputStream);// 3.基于Properties集合构建DruidDataSource连接池。DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);// 4.通过连接池获取连接对象。Connection connection = dataSource.getConnection();// 实现crud等操作System.out.println(connection);// 5.回收连接。connection.close();}
}
Hikari 连接池使用
连接池配置信息
基本连接属性
jdbcUrl
:JDBC URL,指定要连接的数据库。username
:用于连接数据库的用户名。password
:用于连接数据库的密码。driverClassName
(可选):JDBC驱动程序类名,通常不需要设置,因为HikariCP会自动检测。
连接池大小配置
minimumIdle
:最小空闲连接数,默认为idleTimeout
和maximumPoolSize
中的较小者。maximumPoolSize
:最大活跃连接数,默认是10。idleTimeout
:空闲连接被关闭之前等待的时间(毫秒),默认是10分钟(600,000毫秒)。connectionTimeout
:获取连接的最大等待时间(毫秒),默认是30秒(30,000毫秒)。maxLifetime
:连接的最大生命周期(毫秒),超过这个时间将被关闭并替换,默认是30分钟(1800,000毫秒)。
连接测试
connectionTestQuery
:用于验证连接是否有效的SQL查询语句。对于大多数现代数据库驱动,此属性可以省略,因为HikariCP使用了更高效的“connection init SQL”方法。validationTimeout
:验证连接有效性的超时时间(毫秒),默认是5秒(5000毫秒)。
初始化与清理
initSQL
:在每个新连接创建时执行的SQL语句,可用于设置会话级别的参数或模式。poolName
:为连接池指定一个名称,便于监控和调试。
泄露检测
leakDetectionThreshold
:当连接从池中借出的时间超过给定的毫秒数时,记录警告日志。默认是0,表示禁用。
其他配置
autoCommit
:设置连接的自动提交模式,默认为true
。transactionIsolation
:设置事务隔离级别,默认为null
,即使用数据库的默认隔离级别。dataSourceProperties
:传递给数据源的其他属性,例如读取副本地址等。
代码实现(使用软编码方式)
步骤:
- 创建
Properties
集合,用于存储外部配置文件的 key 和 values 值。 - 读取外部配置文件,获取输入流,加载到
Properties
集合里。 - 创建
HikariConfig
连接池配置对象,将Properties
集合加载到HikariConfig
配置对象中。 - 基于
HikariConfig
连接池配置对象,构建HikariDataSource
连接池。 - 获取连接。
- 回收连接。
代码如下:
db.properties
:
# 必要信息
driverClassName=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://127.0.0.1:3306/myjdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username=root
password=123456# 非必要信息
minimumIdle=10
maximumPoolSize=20
main
:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class Demo14 {public static void main(String[] args) throws Exception {// 1.创建Properties集合,用于存储外部配置文件的key和values值。Properties properties = new Properties();// 2.读取外部配置文件,获取输入流,加载到Properties集合里。InputStream inputStream = Demo14.class.getClassLoader().getResourceAsStream("db.properties");properties.load(inputStream);// 3.创建HikariConfig连接池配置对象,将Properties集合加载到HikariConfig配置对象中。HikariConfig hikariConfig = new HikariConfig(properties);// 4.基于HikariConfig连接池配置对象,构建HikariDataSource连接池。HikariDataSource dataSource = new HikariDataSource(hikariConfig);// 5.获取连接Connection connection = dataSource.getConnection();System.out.println(connection);// 6.回收连接。connection.close();}
}
相关文章:

java基础学习——jdbc基础知识详细介绍
引言 数据的存储 我们在开发 java 程序时,数据都是存储在内存中的,属于临时存储,当程序停止或重启时,内存中的数据就会丢失,我们为了解决数据的长期存储问题,有以下解决方案: 通过 IO流书记&…...

联想电脑怎么设置u盘启动_联想电脑设置u盘启动方法(支持新旧机型)
有很多网友问联想电脑怎么设置u盘启动,联想电脑设置u盘启动的方法有两种,一是通过bios进行设置。二是通过快捷方式启动进入u盘启动。但需要注意有两种引导模式是,一种是uefi引导,一种是传统的leacy引导,所以需要注意制…...

C# 解析 HTML 实战指南
在网页开发和数据处理的场景中,经常需要从 HTML 文档里提取有用的信息。C# 作为一门强大的编程语言,提供了丰富的工具和库来实现 HTML 的解析。这篇博客就带你深入了解如何使用 C# 高效地解析 HTML。 一、为什么要在 C# 中解析 HTML 在实际项目中&…...

光谱相机在智能冰箱的应用原理与优势
食品新鲜度检测 详细可点击查看汇能感知团队实验报告:高光谱成像技术检测食物新鲜度 检测原理:不同新鲜程度的食品,其化学成分和结构会有所不同,在光谱下的反射、吸收等特性也存在差异。例如新鲜肉类和蔬菜中的水分、蛋白质、叶…...

编写0号中断的处理程序
实验内容、程序清单及运行结果 编写0号中断的处理程序(课本实验12) 解: assume cs:code code segment start: mov ax,cs mov ds,ax mov si,offset do mov ax,0 mov es,ax mov di,200h mov cx,offset doend-offset do ;安装中断例…...

“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““
主题与背景 本文主要介绍了如何在R语言中使用predict函数对已拟合的Cox比例风险模型进行新数据的预测和推理。Cox模型是一种常用的生存分析方法,用于评估多个因素对事件发生时间的影响。文章通过具体的代码示例展示了如何使用predict函数的不同参数来获取生存概率和…...

群晖docker获取私有化镜像http: server gave HTTP response to HTTPS client].
群晖docker获取私有化镜像提示http: server gave HTTP response to HTTPS clien 问题描述 层级时间用户事件Information2023/07/08 12:47:45cxlogeAdd image from xx.xx.31.240:1923/go-gitea/gitea:1.19.3Error2023/07/08 12:47:48cxlogeFailed to pull image [Get "http…...

使用 C++ 在深度学习中的应用:如何通过 C++20 构建高效神经网络
深度学习已经成为现代人工智能的核心技术,在图像识别、自然语言处理、语音识别等多个领域广泛应用。尽管 Python 因其简便易用和强大的深度学习框架(如 TensorFlow 和 PyTorch)而在这一领域占据主导地位,但 C 作为一门高性能语言&…...

当 Facebook 窥探隐私:用户的数字权利如何捍卫?
随着社交平台的普及,Facebook 已经成为全球用户日常生活的一部分。然而,伴随而来的隐私问题也愈发严峻。近年来,Facebook 频频被曝出泄露用户数据、滥用个人信息等事件,令公众对其隐私保护措施产生质疑。在这个信息化时代…...

Spring MVC中HandlerInterceptor和Filter的区别
目录 一、处理阶段 二、功能范围 三、参数访问 四、配置方式 五、使用场景说明 在Spring MVC中,HandlerInterceptor和Filter都是用于拦截请求的重要组件,但它们在多个方面存在显著的差异。本文将详细解析这两种拦截机制的区别,并结合使用…...

Android多语言开发自动化生成工具
在做 Android 开发的过程中,经常会遇到多语言开发的场景,尤其在车载项目中,多语言开发更为常见。对应多语言开发,通常都是在中文版本的基础上开发其他国家语言,这里我们会拿到中-外语言对照表,这里的工作难…...

回首2024,展望2025
2024年,是个充满挑战与惊喜的年份。在这366个日夜里,我站在编程与博客的交汇点,穿越了无数的风景与挑战,也迎来了自我成长的丰收时刻。作为开发者的第十年,我依然步伐坚定,心中始终带着对知识的渴望与对自我…...

Android SystemUI——快捷面板的显示(十五)
上一篇文章我们分析了 QSTileHost 初始化以及快捷设置面板的创建流程,这里我们继续来看一下快捷设置面板显示流程。 一、QS显示 对于界面的显示,我们同样从 Fragment 的 onViewCreated() 方法开始分析。 1、QSFragment 源码位置:/frameworks/base/packages/SystemUI/src/…...

放弃使用Dockerfiles 平替 docker init
您是那种觉得编写 Dockerfile 和 docker-compose.yml 文件很痛苦的人之一吗? 我承认,我就是其中之一。 我总是想知道我是否遵循了 Dockerfile、 docker-compose 文件的最佳编写实践,我害怕在不知不觉中引入了安全漏洞。 但是现在,…...

前端jquery 实现文本框输入出现自动补全提示功能
git仓库:web_study/some-demos/inputAutoFit at main Cong0925/web_study (github.com) 压缩包:已绑定到指定资源 示例图: 实现说明: 1.首先,html部分设置好相关的定位标签如图: 2.主要函数 3.默认数据...

vulfocus/fastjson-cnvd_2017_02833复现
漏洞概述 Fastjson 是阿里巴巴开发的一个高性能的 Java 库,用于将 Java 对象转换成 JSON 格式(序列化),以及将 JSON 字符串转换回 Java 对象(反序列化)。 fastjson在解析json的过程中,支持使用type字段来指…...

华为支付接入规范
为了确保用户获得良好的支付体验,Payment Kit制定了相关接入设计规范,请开发者遵照执行,具体要求(非强制性)如下: 一、支付方式呈现 涉及支付公司名称,请统一使用:花瓣支付ÿ…...

MySQL训练营-慢查询诊断问题
慢查询相关参数和建议配置 slow_query_log long_query_time 日志开关,是否记慢查询日志以及超过多长时间判定为慢查询。 查看参数设置: SHOW VARIABLES LIKE ‘slow_query_log’;SHOW VARIABLES LIKE ‘long_query_time’; 实践建议: …...

如何给自己的域名配置免费的HTTPS How to configure free HTTPS for your domain name
今天有小伙伴给我发私信,你的 https 到期啦 并且随手丢给我一个截图。 还真到期了。 javapub.net.cn 这个网站作为一个用爱发电的编程学习网站,用来存编程知识和面试题等,平时我都用业余时间来维护,并且还自费买了服务器和阿里云…...

.Net Core微服务入门全纪录(六)——EventBus-事件总线
系列文章目录 1、.Net Core微服务入门系列(一)——项目搭建 2、.Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上) 3、.Net Core微服务入门全纪录(三)——Consul-服务注…...

1/20赛后总结
1/20赛后总结 T1『讨论区管理员』的旅行 - BBC编程训练营 算法:IDA* 分数:0 damn it! Ac_code走丢了~~(主要是没有写出来)~~ T2华强买瓜 - BBC编程训练营 算法:双向DFS或者DFS剪枝 分数:0 Ac_code…...

PVE 虚拟机安装 Debian 无图形化界面服务器
Debian 安装 Debian 镜像下载 找一个Debian镜像服务器,根据需要的版本和自己硬件选择。 iso-cd/:较小,仅包含安装所需的基础组件,可能需要网络访问来完成安装。有镜像 debian-12.9.0-amd64-netinst.isoiso-dvd/:较…...

第17篇:python进阶:详解数据分析与处理
第17篇:数据分析与处理 内容简介 本篇文章将深入探讨数据分析与处理在Python中的应用。您将学习如何使用pandas库进行数据清洗与分析,掌握matplotlib和seaborn库进行数据可视化,以及处理大型数据集的技巧。通过丰富的代码示例和实战案例&am…...

三天急速通关Java基础知识:Day1 基本语法
三天急速通关JAVA基础知识:Day1 基本语法 0 文章说明1 关键字 Keywords2 注释 Comments2.1 单行注释2.2 多行注释2.3 文档注释 3 数据类型 Data Types3.1 基本数据类型3.2 引用数据类型 4 变量与常量 Variables and Constant5 运算符 Operators6 字符串 String7 输入…...

Python的进程和线程
ref 接受几个设定: 进程是一家almost密不透风的公司,缅甸KK园区 线程里面工作的…人 进程**[园区]**内公共资源对于进程来说,可以共享. 别的园区[进程],一般不能和自己的园区共享人员资源,除非… 好的,现在再接受设定: 单个CPU在任一时刻只能执行单个线程,只有…...

【Mysql】记录锁、间隙锁和临键锁的区别
InnoDB通过MVCCNext-Key Locks,解决了可重复读的事务隔离级别出现的幻读问题。 记录锁 记录锁就是为某行数据进行加锁,它封锁该行的索引记录 SELECT * FROM table WHERE id 1 FOR UPDATE id为1的记录行会被锁住。需要注意的的:id列必须为…...

神经网络|(二)sigmoid神经元函数
【1】引言 在前序学习进程中,我们已经了解了基本的二元分类器和神经元的构成,文章学习链接为: 神经网络|(一)加权平均法,感知机和神经元-CSDN博客 在此基础上,我们认识到神经元本身在做二元分类,是一种非…...

w-form-select.vue(自定义下拉框组件)(与后端字段直接相关性)
文章目录 1、w-form-select.vue 组件中每个属性的含义2、实例3、源代码 1、w-form-select.vue 组件中每个属性的含义 好的,我们来详细解释 w-form-select.vue 组件中每个属性的含义,并用表格列出它们是否与后端字段直接相关: 属性解释表格&…...

【JVM】垃圾收集器详解
你将学到 1. Serial 收集器 2. ParNew 收集器 3. Parallel Scavenge 收集器 4. Serial Old 收集器 5. Parallel Old 收集器 6. CMS 收集器 7. G1 收集器 在 Java 中,垃圾回收(GC)是自动管理内存的一个重要机制。HotSpot JVM 提供了多种…...

python创建一个httpServer网页上传文件到httpServer
一、代码 1.server.py import os from http.server import SimpleHTTPRequestHandler, HTTPServer import cgi # 自定义请求处理类 class MyRequestHandler(SimpleHTTPRequestHandler):# 处理GET请求def do_GET(self):if self.path /:# 响应200状态码self.send_response(2…...