当前位置: 首页 > news >正文

手机电脑同步网站开发/推广之家app

手机电脑同步网站开发,推广之家app,企业年金是什么意思,企业网站模板优化目录 前言一、PreparedStatement是什么二、重点理解预编译三、PreparedStatement基本使用四、Statement和PreparedStatement比较1.PreparedStatement效率高2.PreparedStatement无需拼接参数3.PreparedStatement防止SQL注入 总结 前言 📜 本系列教程适用于JavaWeb初学…

目录

  • 前言
  • 一、PreparedStatement是什么
  • 二、重点理解预编译
  • 三、PreparedStatement基本使用
  • 四、Statement和PreparedStatement比较
    • 1.PreparedStatement效率高
    • 2.PreparedStatement无需拼接参数
    • 3.PreparedStatement防止SQL注入
  • 总结


前言

📜 本系列教程适用于JavaWeb初学者、爱好者,小白白。我们的天赋并不高,可贵在努力,坚持不放弃。坚信量最终引发质变,厚积薄发。
🚀 文中白话居多,尽量以小白视角呈现,帮助大家快速入门。
🎅 我是 蜗牛老师,之前网名是 Ongoing蜗牛,人如其名,干啥都慢,所以更新也慢。希望大家多多支持,让我动力十足!

上篇文章我们学习了 JDBC 编程基本步骤,步骤中使用 Statement 执行 SQL 语句。其实还有一个更好的方式,就是 PreparedStatement,预编译语句。与 Statement 相比有诸多优势,目前开发中一般使用 PreparedStatement。所以我们非常有必要进行学习,而且日后的持久层框架底层也是使用 PreparedStatement


一、PreparedStatement是什么

在 JDBC 编程基本步骤中,我们创建 Statement(语句)对象,向数据库发送要执行的 SQL 语句。Statement 一般用于实现不带参数的简单 SQL 语句,也就是说它执行的是静态 SQL 语句。每次执行 SQL 语句时,都会将 SQL 语句编译为数据库可以理解的格式。Statement 的工作原理是将 SQL 语句发送给数据库,然后数据库执行该语句并返回结果。

那么 PreparedStatement 是什么呢?大家应该也发现了,PreparedStatementStatement 多了单词 Prepared,理解的重点就是 Prepared,它是预编译的意思,所以平时被称为预编译的 Statement(语句)。

PreparedStatement 是一个接口,而且它是 Statement 接口的子接口。

// Statement接口
public interface Statement extends Wrapper, AutoCloseable {}// PreparedStatement接口
public interface PreparedStatement extends Statement {}

PreparedStatement 接口用于执行动态 SQL 语句。它允许在 SQL 语句中使用占位符(? 英文格式问号),然后在执行之前将这些占位符(?)替换为实际的值。PreparedStatement 在执行之前会对 SQL 语句进行预编译,这样可以提高执行效率。工作原理是将 SQL 语句发送给数据库之前,先将其编译为可执行的格式,然后将实际的参数值传递给占位符(?)。

具体解决什么问题呢?我们来看一个需求:一个博客网站要根据某个文章编号(id)查询该文章内容进行展示,文章编号100和101分别代表不同的文章。我们来看 SQL 语句:

select * from article where id = 100;
select * from article where id = 101;

在上述需求中,我们会反复执行一条结构相似的 SQL 语句。其实在日常需求当中,经常需要执行 SQL 语句结构基本相似,但是执行时的参数值不同。这种 SQL 语句我们可以叫做动态 SQL 语句,就可以使用 PreparedStatement 接口,SQL 语句中的参数可以使用占位符(?)代替,也就是说占位符(?)的位置参数未知,可以是100,可以是101或是其他。带有占位符(?)SQL 语句如下:

select * from article where id = ?;

需要注意的是 Statement 执行 SQL 语句时是不允许带有占位符(?)参数的,而且 PreparedStatement 执行带有占位符参数的 SQL 语句时,参数必须要传入实际的值才可以。

二、重点理解预编译

大概知道 PreparedStatement 是干什么的之后,我们来重点理解一下预编译。

预编译是指在执行 SQL 语句之前,将 SQL 语句编译为一个预定义的内部格式,以便数据库能够更有效地执行。

预编译的过程包括以下几个步骤:

  • 语法分析:数据库系统会对传入的 SQL 语句进行语法分析,检查其是否符合语法规则。

  • 语义分析:数据库系统会对 SQL 语句进行语义分析,检查表和列是否存在、权限是否足够等。

  • 优化和执行计划生成:数据库系统会对 SQL 语句进行优化,生成一个最佳的执行计划,以便在执行时能够高效地获取数据。

在预编译完成后,数据库会将编译后的执行计划存储在缓存中,以便下次执行相同的预编译语句时可以直接使用执行计划,从而节省了编译的时间和资源。这也是 PreparedStatement 相较于 Statement 的一个优势所在。

当多次执行相同的预编译语句时,由于已经完成了编译和优化的步骤,预编译的语句可以更快速地执行,因为只需传递参数值并执行执行计划,而不需要再进行语法分析、语义分析和执行计划生成等步骤。

需要注意的是,预编译功能主要适用于需要多次执行相同的 SQL 语句的场景,因为预编译的语句在编译时会占用一定的资源。如果只需要执行一次或是每次 SQL 语句都不同,那么使用 Statement 可能会更合适。

三、PreparedStatement基本使用

在对 PreparedStatement 有了基本了解后,我们进行简单使用。在 JDBC 编程基本步骤中查询了 teacher 表的全部数据,在这里将再次使用 teacher 表,这次我们往表中添加数据。

test-jdbc 项目中新建类 TestPreparedStatement,仍然生成 main() 方法。方法中重新编写 JDBC 代码,顺带巩固。

在这里插入图片描述
JDBC 编程的第一步就是加载驱动,第二步是连接 Connection 连接。大家还记得代码如何编写吗?

/*** 敲入main,根据提示自动生成主函数main()方法* @param args*/
public static void main(String[] args) {try {// ①动态加载指定路径下的MySQL JDBC驱动,将其注册到DriverManager中。Class.forName("com.mysql.cj.jdbc.Driver");// ②建立到给定数据库URL的连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}

之前第三步是创建一个 Statement 对象,用于向数据库发送 SQL 语句。现在我们改用 PreparedStatement。和 Statement 一样我们需要先拿到 PreparedStatement 对象,使用 Connection 中的 prepareStatement(String) 方法获得。我们来看 API:

/*** 创建一个PreparedStatement对象,用于向数据库发送参数化的SQL语句。* 可以预编译带有或不带有IN参数的SQL语句,并将其存储在PreparedStatement对象中。然后可以使用该对象多次有效地执行该语句。 */
PreparedStatement prepareStatement(String sql) throws SQLException;

接下来我们来编写第三步:使用 Connection 来创建 PreparedStatement 对象。

/*** 敲入main,根据提示自动生成主函数main()方法* @param args**/
public static void main(String[] args) {try {// ①动态加载指定路径下的MySQL JDBC驱动,将其注册到DriverManager中。Class.forName("com.mysql.cj.jdbc.Driver");// ②建立到给定数据库URL的连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// ③使用Connection来创建PreparedStatement对象,将把语句发送到数据库进行预编译。PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}

上述③代码处,使用 ConnectionprepareStatement(String sql) 方法来创建 PreparedStatement 对象,该方法需要传入一个 SQL 语句字符串,可以包含占位符。上述 SQL 语句向 teacher 表中插入一条数据,由于表中 id 为自增,所以插入时不需要给 id 指定值。对于 namesexage 三个字段的值使用占位符(?)进行占位。

PreparedStatement 也提供了 execute()executeUpdate()executeQuery()executeLargeUpdate()(1.8版本新增)方法去执行 SQL 语句。这里我们使用 executeUpdate() 方法去执行 insert 语句。由于执行的 SQL 语句带有占位符参数,因此在执行语句前必须为这些参数传入参数值,PreparedStatement 提供了一系列的 setXxx(int index, Xxx value) 方法来传入参数值。

在这里插入图片描述
我们来看具体代码实现:

/*** 敲入main,根据提示自动生成主函数main()方法* @param args**/
public static void main(String[] args) {try {// ①动态加载指定路径下的MySQL JDBC驱动,将其注册到DriverManager中。Class.forName("com.mysql.cj.jdbc.Driver");// ②建立到给定数据库URL的连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// ③使用Connection来创建PreparedStatement对象,将把语句发送到数据库进行预编译。PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");// ④设置参数并执行SQL语句preparedStatement.setString(1, "赵六");preparedStatement.setString(2, "女");preparedStatement.setInt(3, 20);// 执行SQL语句preparedStatement.executeUpdate();} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}

SQL 语句中的第一个占位符(?),是表中 name 字段的值,为字符串,所以这里使用 setString() 方法,方法中需要两个参数,一个是占位符(?)的位置,从1开始,第二个参数是具体的值。其他占位符(?)操作一样,需要注意的是占位符(?)的位置及该位置传入的参数类型。如果编程时不清楚预编译 SQL 语句中各参数的类型,我们可以使用 setObject() 方法传入参数,然后由 PreparedStatement 负责类型转换。

最后一步就是关闭资源,使用到哪些资源就关闭哪些资源,注意当前代码中我们没有使用 StatementResultSet

/*** 敲入main,根据提示自动生成主函数main()方法* @param args**/
public static void main(String[] args) {try {// ①动态加载指定路径下的MySQL JDBC驱动,将其注册到DriverManager中。Class.forName("com.mysql.cj.jdbc.Driver");// ②建立到给定数据库URL的连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// ③使用Connection来创建PreparedStatement对象,将把语句发送到数据库进行预编译。PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");// ④设置参数并执行SQL语句preparedStatement.setString(1, "赵六");preparedStatement.setString(2, "女");preparedStatement.setInt(3, 20);// 执行SQL语句preparedStatement.executeUpdate();// ⑤ 关闭资源preparedStatement.close();connection.close();} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}

我们运行程序,控制台无报错,通过客户端查看表中记录,是否有刚刚插入的赵六数据。

在这里插入图片描述

四、Statement和PreparedStatement比较

StatementPreparedStatement 的相同之处在于它们都是用于执行 SQL 语句的接口。它们都可以执行查询和更新操作,并且都可以接收参数。不同之处在于 PreparedStatement 可以预编译 SQL 语句并使用占位符(?),这样可以提高执行效率,并且可以防止 SQL 注入攻击。

1.PreparedStatement效率高

我们进行简单测试,向 teacher 表分别插入1000条记录进行对比。

import java.sql.*;/*** PreparedStatementVsStatement PreparedStatement和Statement比较** @author Ongoing蜗牛* @since 2023/8/24 14:25*/
public class PreparedStatementVsStatement {/*** 使用Statement执行SQL语句** @param connection 数据库连接对象*/public void insertByStatement(Connection connection){// 以毫秒为单位返回当前时间,记录开始时间long start = System.currentTimeMillis();try {// 创建Statement对象Statement statement = connection.createStatement();// 使用for循环执行插入1000条记录for (int i = 0; i < 1000; i++) {statement.executeUpdate("insert into teacher (name, sex, age) value ('张某" + i + "', '男', 20)");}} catch (SQLException throwables) {throwables.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用Statement执行耗时:" + (end - start));}/*** 使用PreparedStatement执行SQL语句** @param connection 数据库连接对象*/public void insertByPreparedStatement(Connection connection){// 以毫秒为单位返回当前时间,记录开始时间long start = System.currentTimeMillis();try {// 创建Statement对象PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");// 使用for循环设置参数值并执行for (int i = 0; i < 1000; i++) {preparedStatement.setString(1, "李某" + i);preparedStatement.setString(2, "女");preparedStatement.setInt(3, 23);preparedStatement.executeUpdate();}} catch (SQLException throwables) {throwables.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用PreparedStatement执行耗时:" + (end - start));}/*** 测试** @param args*/public static void main(String[] args) {PreparedStatementVsStatement psvs = new PreparedStatementVsStatement();try {// 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 建立数据库连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// 调用 使用Statement执行SQL语句 方法psvs.insertByStatement(connection);// 调用 使用PreparedStatement执行SQL语句 方法psvs.insertByPreparedStatement(connection);} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}}
}

执行结果如下:

使用Statement执行耗时:751
使用PreparedStatement执行耗时:637

通过上述测试代码可知,同样是插入1000条记录,使用 Statement 需要传入1000条 SQL 语句,而使用 PreparedStatement 其实只需要传入一条预编译的 SQL 语句,然后对其进行1000次设置参数。从执行多用时间也可以看出 PreparedStatement 的执行效率要高于 Statement

2.PreparedStatement无需拼接参数

其实 PreparedStatement 还有一个优势是在使用参数时,不需要拼接,减少 SQL 语句的复杂的。我们看下面的伪代码:

// 教师信息
String name = "张三";
String sex = "男";
int age = 20;// 使用Statement执行,需要拼接参数
statement.executeUpdate("insert into teacher (name, sex, age) value ('"+name+"','"+sex+"',"+age+")");// 使用PreparedStatement执行,不需要拼接参数
PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");
preparedStatement.setString(1, name);
preparedStatement.setString(2, sex);
preparedStatement.setInt(3, age);
preparedStatement.executeUpdate();

3.PreparedStatement防止SQL注入

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

经典的例子就是登录操作,一般正常用户会输入用户和密码进行登录操作,系统根据用户输入到数据库表中进行匹配,如果找到相应的记录则登录成功,否则登录失败。那么我们要通过 SQL 语句的执行去进行匹配操作。这里还是使用 teacher 表简单模拟,要求输入的姓名在表中存在就登录成功。正常用户输入姓名,系统执行 SQL 匹配。非正常用户会输入特殊字符串匹配。代码如下:

import java.sql.*;/*** TestSqlInjection SQL注入** @author Ongoing蜗牛* @since 2023/8/24 15:22*/
public class TestSqlInjection {/*** 使用姓名登录,Statement执行SQL语句* @param name 姓名*/public void loginByStatement(String name){// 打印输入的姓名System.out.println("登录姓名:" + name);// 登录标识boolean flag = false;try {// 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 建立数据库连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// 创建Statement对象Statement statement = connection.createStatement();// 执行SQL语句并返回结果ResultSet resultSet = statement.executeQuery("select * from teacher where name = '"+ name +"'");// 打印执行的SQL语句System.out.println("select * from teacher where name = '"+ name +"'");// 处理结果while (resultSet.next()){// 返回至少一条记录就登录成功flag = true;break;}// 关闭资源resultSet.close();statement.close();connection.close();// 打印输出结果if (flag){System.out.println("登录成功!");}else{System.out.println("登录失败!");}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}}/*** 主函数测试* @param args*/public static void main(String[] args) {TestSqlInjection testSqlInjection = new TestSqlInjection();//String loginName = "张三"; //数据库中可以匹配,登录成功//String loginName = "某某"; //数据库中匹配不到,登录失败String loginName = "' or true or '"; //数据库中匹配不到,但是登录成功testSqlInjection.loginByStatement(loginName);}
}

执行结果如下:

登录姓名:' or true or '
select * from teacher where name = '' or true or ''
登录成功!

这回大家明白什么是 SQL 注入了吧。那么使用 PreparedStatement 进行同样的登录操作,也会遭遇 SQL 注入登录成功吗?

/*** 使用姓名登录,PreparedStatement执行SQL语句* @param name 姓名*/
public void loginByPreparedStatement(String name){// 打印输入的姓名System.out.println("登录姓名:" + name);// 登录标识boolean flag = false;try {// 注册驱动Class.forName("com.mysql.cj.jdbc.Driver");// 建立数据库连接。Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai", "root", "root");// 创建PreparedStatement对象,预编译SQL语句PreparedStatement preparedStatement = connection.prepareStatement("select * from teacher where name = ?");// 设置参数preparedStatement.setString(1, name);// 执行SQL语句ResultSet resultSet = preparedStatement.executeQuery();// 打印执行的SQL语句System.out.println(preparedStatement.toString());// 处理结果while (resultSet.next()){// 返回至少一条记录就登录成功flag = true;break;}// 关闭资源resultSet.close();preparedStatement.close();connection.close();// 打印输出结果if (flag){System.out.println("登录成功!");}else{System.out.println("登录失败!");}} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}
}/*** 主函数测试* @param args*/
public static void main(String[] args) {TestSqlInjection testSqlInjection = new TestSqlInjection();//String loginName = "张三"; //数据库中可以匹配,登录成功//String loginName = "某某"; //数据库中匹配不到,登录失败String loginName = "' or true or '"; //数据库中匹配不到,但是登录成功//testSqlInjection.loginByStatement(loginName);testSqlInjection.loginByPreparedStatement(loginName);
}

我们查看执行结果,为登录失败,也就是说 PreparedStatement 可以防止 SQL 注入。究其原因很简单,PreparedStatement' or true or ' 作为一个参数值传给 name,数据库中当然没有该姓名了。

登录姓名:' or true or '
com.mysql.cj.jdbc.ClientPreparedStatement: select * from teacher where name = '' or true or ''
登录失败!

总结

PreparedStatement 接口用于执行动态SQL语句。 它允许在 SQL 语句中使用占位符,然后在执行之前将这些占位符替换为实际的值。

预编译是指在执行 SQL 语句之前,将 SQL 语句编译为一个预定义的内部格式,以便数据库能够更有效地执行。

PreparedStatement 关键步骤:

// 使用Connection来创建PreparedStatement对象,将把语句发送到数据库进行预编译。
PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name, sex, age) value (?, ?, ?)");
// 设置参数并执行SQL语句
preparedStatement.setString(1, "赵六");
preparedStatement.setString(2, "女");
preparedStatement.setInt(3, 20);
// 执行SQL语句        
preparedStatement.executeUpdate();

PreparedStatement 优势:

  • 预编译 SQL 语句,效率高。
  • 无需拼接参数,更简单。
  • 防止 SQL 注入,更安全。

日后 JDBC 编程使用 PreparedStatement 执行 SQL 语句。

相关文章:

【JavaEE基础学习打卡06】JDBC之进阶学习PreparedStatement

目录 前言一、PreparedStatement是什么二、重点理解预编译三、PreparedStatement基本使用四、Statement和PreparedStatement比较1.PreparedStatement效率高2.PreparedStatement无需拼接参数3.PreparedStatement防止SQL注入 总结 前言 &#x1f4dc; 本系列教程适用于JavaWeb初学…...

Postgresql12基于时间点恢复

1、环境 centos 7系统 postgresql 12 docker 20.10.6 2、整体思路 1&#xff09;进行一个pgdata目录的全量备份 2&#xff09;通过wal日志恢复到故障发生之前某个时间点 3、操作步骤 配置postgresql.conf文件 #日志级别 wal_level replica #归档开关 archive_mode on …...

【MySQL系列】Select语句单表查询详解(二)ORDERBY排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …...

C++学习第十九天----简单文件输入/输出和今日工作问题

1.写入到文本文件中 cout用于控制台输出&#xff1b; 必须包含头文件iostream&#xff1b; 头文件iostream定义了一个用于处理输出的ostream类&#xff1b; 头文件iostream声明了一个名为cout的ostream变量&#xff08;对象&#xff09;&#xff1b; 必须指明名称空间std&…...

基于风险的漏洞管理

基于风险的漏洞管理涉及对即将被利用的漏洞的分类响应&#xff0c;如果被利用&#xff0c;可能会导致严重后果。本文详细介绍了确定漏洞优先级时要考虑的关键风险因素&#xff0c;以及确保基于风险的漏洞管理成功的其他注意事项。 什么是基于风险的漏洞管理对基于风险的漏洞管…...

命令行——Git基本操作总结

介绍 我们的操作使用的是客户端TortoiseGit 操作的git ,实际上底层依旧是使用的命令行帮我们执行, 在早期 git 并没有窗口化工具,开发人员只能使用命令行模式 实际上,如果你掌握并熟练使用了命令行模式操作git 的话,你会发现某些操作命令行比窗口化操作要简单 所有你在工作中…...

验证评估守护关基安全 赛宁数字孪生靶场创新实践

​​近日&#xff0c;由赛宁网安主办&#xff0c;ISC互联网安全大会组委会协办的第十一届互联网安全大会&#xff08;ISC 2023&#xff09;安全运营实践论坛圆满结束。赛宁网安产品总监史崯出席并作出主题演讲&#xff1a;《基于数字孪生靶场如何开展验证评估》&#xff0c;同时…...

R语言09-R语言中的字符函数和分布相关函数

字符函数 paste() 和 paste0(): 将多个字符向量连接成一个字符串&#xff0c;paste0() 直接连接&#xff0c;而 paste() 可以通过 sep 参数指定分隔符。 vector1 <- c("Hello", "world") vector2 <- c("R", "programming") re…...

pnpm无法加载文件 (解决方法 )

现在要运行一个TS的项目&#xff0c;我的电脑上没有安装pnpm&#xff0c;导致我的vscode一直报错无法加载。 pnpm安装&#xff1a; npm install -g pnpm pnpm : 无法加载文件 pnpm : 无法加载文件 C:\Users\HP\AppData\Roaming\npm\pnpm.ps1&#xff0c;因为在此系统上禁止运…...

做一个蛋糕店小程序需要哪些步骤?

对于一些不懂技术的新手来说&#xff0c;创建蛋糕店小程序可能会感到有些困惑。但是&#xff0c;有了乔拓云平台的帮助&#xff0c;你可以轻松地创建自己的蛋糕店小程序。下面&#xff0c;我将为大家详细介绍一下具体的操作步骤。 首先&#xff0c;登录乔拓云平台并进入后台管理…...

Docker的革命:容器技术如何重塑软件部署之路

引言 在过去的几年中&#xff0c;容器技术已经从一个小众的概念发展成为软件开发和部署的主流方法。Docker&#xff0c;作为这一变革的先驱&#xff0c;已经深深地影响了我们如何构建、部署和运行应用程序。本文将探讨容器技术的起源&#xff0c;Docker如何崛起并改变了软件部…...

【ARM-Linux】项目,语音刷抖音项目

文章目录 所需器材装备操作SU-03T语音模块配置代码&#xff08;没有用wiring库&#xff0c;自己实现串口通信&#xff09;结束 所需器材 可以百度了解以下器材 orangepi-zero2全志开发板 su-03T语音识别模块 USB-TTL模块 一个安卓手机 一根可以传输的数据线 装备操作 安…...

Linux驱动开发:技术、实践与Linux的历史

一、引言 Linux&#xff0c;这个开源的操作系统&#xff0c;已经在全球范围内赢得了开发者和企业的广泛支持。它的强大之处在于其内核以及无数的驱动程序&#xff0c;这些驱动程序使得各种硬件设备可以在Linux操作系统上运行。本篇文章将深入探讨Linux驱动开发&#xff0c;包括…...

# Go学习-Day5

文章目录 map增加和更新删除查询遍历&#xff08;for-range&#xff09;map切片关于哈希表遍历的一点看法对map的key排序 结构体与OOP声明、初始化、序列化方法工厂模式 个人博客&#xff1a;CSDN博客 map map是一个key-value的数据结构&#xff0c;又称为字段或关联数组 Gol…...

创建型(二) - 单例模式

一、概念 单例设计模式&#xff08;Singleton Design Pattern&#xff09;&#xff1a;一个类只允许创建一个对象&#xff08;或者实例&#xff09;&#xff0c;那这个类就是一个单例类。 优点&#xff1a;在内存里只有一个实例&#xff0c;减少了内存的开销&#xff0c;避免…...

基于swing的图书借阅管理系统java jsp书馆书籍信息mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的图书借阅管理系统 系统有2权限&#xff1…...

Android相机-HAL-Rockchip-hal3

引言&#xff1a; 对于Android相机的 HAL层而言对上实现一套Framework的API接口&#xff0c;对下通过V4L2框架实现与kernel的交互。不同的平台会有不同的实现方案。主要是对Android HAL3的接口的实现。看看rockchip是怎么支持hal3的&#xff1f; 代码目录&#xff1a; hardw…...

基于Java+SpringBoot+vue前后端分离华强北商城二手手机管理系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…...

【论文阅读】HOLMES:通过关联可疑信息流进行实时 APT 检测(SP-2019)

HOLMES: Real-time APT Detection through Correlation of Suspicious Information Flows S&P-2019 伊利诺伊大学芝加哥分校、密歇根大学迪尔伯恩分校、石溪大学 Milajerdi S M, Gjomemo R, Eshete B, et al. Holmes: real-time apt detection through correlation of susp…...

HTML 网页中 自定义图像单击或鼠标悬停时放大

HTML 网页中 自定义图像单击或鼠标悬停时放大 一&#xff1a;在悬停时更改 HTML 图像的大小 例子中&#xff0c;使用 CSS 样式&#xff1b;来设置每个图像元素的高宽 200px&#xff1b;以及 10px 边距&#xff0c;以便在图像周围留出空间。 使用 CSS 的 :hover 属性来添加悬停效…...

从程序员进阶到架构师再到CTO,该如何破解焦虑?

引言 我们生活的时代&#xff0c;变化太快&#xff0c;许多人在职业发展的道路上都会面临焦虑与迷茫。这种焦虑源自我们内心的不安&#xff0c;也来自于外部形势的变化。 对于技术从业者来说&#xff0c;焦虑并不会随着职业发展而自动消失&#xff0c;不同职场阶段会面临不同的…...

批量将excel文件转csv文件

要将Excel文件批量转换为CSV文件&#xff0c;并按照关键词汇总&#xff0c;可以使用Python中的pandas库来实现。下面是示例代码&#xff1a; import pandas as pd import os def excel_to_csv(file_path, output_folder): # 读取Excel文件 df pd.read_excel(file_pat…...

实现 CSS 文字渐变色效果

实现 当涉及到文字渐变色时,以下是一个更详细的用法示例。你可以使用 CSS 的 background-image,background-clip 和 text-fill-color 属性来实现: h1 {background-image: linear-gradient(to right, #ff0000, #00ff00, #0000ff);background-clip: text;-webkit-background-c…...

C++信息学奥赛1148:连续出现的字符

代码题解&#xff1a; #include <iostream> #include <string> using namespace std; int main() {int n;// 输入一个整数ncin>>n;cin.ignore();string str1;// 输入一行字符串getline(cin,str1);for(int i0;i<str1.length();i){int a0;for(int ji;j<…...

【笔记】岂不怀归:三和青年调查

三和青年的遭遇绝非孤例&#xff0c;他们是中国现代化和城市化进程中一些难以被城市容纳的群体的缩影。三和青年的“后备军”&#xff0c;是整整一代没有知识与技能的农村青年。本书对三和青年抱以人道主义的关怀与同情&#xff0c;并鼓励各界关注社会发展过程中被抛下的那一群…...

使用Mavon-Editor编辑器上传本地图片到又拍云云存储(Vue+SpringBoot)

需求&#xff1a;将本地的图片上传到服务器或者云存储中&#xff0c;考虑之后&#xff0c;这里我选的是上传到又拍云云存储。 技术背景&#xff1a; 前端&#xff1a;VueAjax 后端&#xff1a;SpringBoot 存储&#xff1a;又拍云云存储原理&#xff1a;Mavon-Editor编辑器有两个…...

QT使用QXlsx实现对Excel的创建与文字的存取 QT基础入门【Excel的操作】

准备:搭建环境引用头文件QT中使用QtXlsx库的三种方法 QT基础入门【Excel的操作】_吻等离子的博客-CSDN博客 #include "xlsxdocument.h"const QString ExcelName="./test.xlsx"; QTXLSX_USE_NAMESPACE // 添加Xlsx命名空间 1、初始化excel表格 注意!两…...

前端遇到困扰怎么办?10年前端在线帮您解决问题,只需一杯下午茶

前端遇到困扰怎么办&#xff1f;10年前端在线帮您解决问题&#xff0c;只需一杯下午茶...

c#值类型和引用类型

在C#中&#xff0c;变量可以是值类型或引用类型。下面是一些常见的值类型和引用类型 值类型&#xff1a; 基本数据类型&#xff1a;bool、byte、sbyte、char、short、ushort、int、uint、long、ulong、float、double、decimal 枚举类型&#xff1a;enum 结构体类型&#xff1…...

机器学习算法示例的收集;MetaAI编码工具Code Llama;“天工AI搜索”首发实测

&#x1f989; AI新闻 &#x1f680; Meta推出新一代AI编码工具Code Llama&#xff0c;助力程序员提高开发效率 摘要&#xff1a;Meta推出Code Llama&#xff0c;这是一个基于Llama 2语言模型打造的AI编码工具&#xff0c;能够生成新的代码并调试人类编写的工作。Code Llama可…...