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

《Spring源码深度分析》第8章 数据库连接JDBC

目录标题

  • 前言
  • 一、数据库连接方式
    • 1.JDBC连接数据库
    • 2.Spring Jdbc连接数据库(JdbcTemplate)
  • 二、JdbcTemplate源码分析
    • 1.update/save功能的实现
      • 源码分析入口(关键)
      • 基础方法execute
        • 1.获取数据库连接池
        • 2.应用用户设定的输入参数
        • 3. 调用回调函数处理
        • 4. 资源释放
      • Update中的回调函数
    • 2.query 功能的实现
      • 源码分析入口1(关键)
      • 源码分析入口2(关键)
    • 3.queryForObject
  • 总结

前言

汇总:《Spring源码深度分析》持续更新中…

本章主要以Spring提供的模板类’JdbcTemplate‘为例,进行源代码分析。如果有小伙伴对Spring如何支持持久化技术的理论知识感兴趣,可以参考《精通Spring4.x 企业应用开发实战》第10章 Spring对Dao的支持。

一、数据库连接方式

从《精通Spring4.x 企业应用开发实战》第10章 Spring对Dao的支持
文章中,我们知道目前市场上连接MySQL数据库常用的几种持久化方式有五种:JDBC、Mybatis、Hibernate、JTA、JDO。

然而Spring又分别对JDBC、Hibernate、JTA、JDO提供了模板类,以便快速进行持久化连接开发。其中Spring针对JDBC的模板类是’JdbcTemplate‘。
在这里插入图片描述

下面,我们将分别对JDBC和JdbcTemplate数据库连接进行分析。

1.JDBC连接数据库

JDBC ( Java Data Base Connectivity, Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 为数据库开发人员提供了一个标准的 API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序,并且可跨平台运行,并且不受数据库供应商的限制。
JDBC 连接数据库的流程及其原理如下。
(1)在开发环境中加载指定数据库的驱动程序。接下来的实验中,使用的数据库是 MysQL,所以需要去下载 MySQL 支持 JDBC 的驱动程序(最新的版本是 mysal-conneetorjava-5.1.18-bin.jar),将下载得到的驱动程序加载进开发环境中(开发环境是 MyEclipse,具体示例时会讲解如何加载)。

(2)在Java 程序中加载驱动程序。在 Java 程序中,可以通过“Class.forName(“指定数据库的驱动程序”)”的方式来加载添加到开发环境中的驱动程序,例如加载 MySQL 的数据驱动程序的代码为 Class. forName(“com.mysqljdbc.Driver”)。

(3)创建数据连接对象。通过 DriverManager 类创建数据库连接对象 Conneetion。DriverManager 类作用于 Java 程序和 JDBC 驱动程序之间,用于检查所加载的驱动程序是否可以建立连接,然后通过它的 getConnection 方法根据数据库的 URL、用户名和密码,创建一个JDBC Connection 对象,例如:Connection connection = DriverManager.getConnection(“连接数据库的 URL”,“用户名”,"密码”)。其中,URL-协议名+1P 地址(域名) +端口+数据库名称;用户名和密码是指登录数据库时所使用的用户名和密码。具体示例创建 MySQL 的数据库连接代码如下:

Connection connectMySQL = DriverManager.getConnection (“jdbc:mysql://localhost: 3306/myuser","root","root" );

(4)创建 Statement 对象。Statement 类的主要是用于执行静态 SQL 语句并返回它所生成结果的对象。通过 Connection 对象的 createStatement()方法可以创建一个 Statement 对象。例如:Statement statament = connection.createStatement()。具体示例创建 Statement 对象代码如下:

Statement statamentMySQL = connectMySQL.createStatement ();

(5)调用 Statement 对象的相关方法执行相对应的 SQL 语句。通过 execuUpdate()方法来对数据更新,包括插入和删除等操作,例如向 staff 表中插人一条数据的代码:statement.excuteUpdate( “INSERTINTO staff (name, age, sex,address,depart,worklen, wage) " + " VALUES (‘Tom1’, 321,“M’,‘china’,Personnel’,,3”,,3000’)”);通过调用 Statement 对象的 executeQuery()方法进行数据的查询,而查询结果会得到 ResulSet对象,ResuSet 表示执行查询数据库后返回的数据的集合,ResulSet 对象具有可以指向当前数据行的指针。通过该对象的 next()方法,使得指针指向下一行,然后将数据以列号或者字段名取出。如果当 next()方法返回 null,则表示下一行中没有数据存在。使用示例代码如下:

Resultset resultsel = statement.executeQuery( “select * from staff" );

(6)关闭数据库连接。使用完数据库或者不需要访问数据库时,通过 Connection 的 close()方法及时关闭数据连接。

2.Spring Jdbc连接数据库(JdbcTemplate)

Spring 中的 JDBC 连接与直接使用 JDBC 去连接还是有所差别的,Spring 对 JDBC 做了大量封裝,消除了冗余代码,使得开发量大大减小。下面通过一个小例子让大家简单认识 Spring中的 JDBC 操作。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

二、JdbcTemplate源码分析

1.update/save功能的实现

源码分析入口(关键)

无论对哪种技术进行源码分析,我们都应该先找到入口点,然后顺藤摸瓜式的解读源码。

我们以上面的例子为基础开始分析 Spring 中对JDBC 的支持,首先寻找整个功能的切入点,在示例中我们可以看到所有的数据库操作都封装在了 UserServicelmpl 中,而 UserServicelmpl中的所有数据库操作又以其内部属性 jdbcTemplate 为基础。这个jdbcTemplate 可以作为源码分析的切人点,我们一起看看它是如何实现又是如何被初始化的。

在 UserServicelmpl 中 jdbcTemplate 的初始化是从 setDataSource 函数开始的,DataSource实例通过参数注入,DataSource 的创建过程是引入第三方的连接池,这里不做过多介绍。DataSource 是整个数据库操作的基础,里面封装了整个数据库的连接信息。我们首先以保存实体类为例进行代码跟踪。
在这里插入图片描述
对于保存一个实体类来讲,在操作中我们只需要提供 SQL 语句以及语句中对应的参数和参数类型,其他操作便可以交由 Spring 来完成了,这些工作到底包括什么呢?进入 jdbcTemplate中的 update 方法。

	@Overridepublic int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {return update(sql, newArgTypePreparedStatementSetter(args, argTypes));}@Overridepublic int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {return update(new SimplePreparedStatementCreator(sql), pss);}

进人 update 方法后,我们发现Spring 并不是急于进入核心处理操作,而是先做足准备工作:

  1. 使用 ArgTypePreparedStatementSetter 对参数与参数类型进行封装
  2. 同时又使用 SimplePreparedStatementCreator 对 SQL 语句进行封装
    至于为什么这么封装,暂且留下悬念。

经过了数据封装后便可以进入了核心的数据处理代码了。

   protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)throws DataAccessException {logger.debug("Executing prepared SQL update");// PreparedStatementCallback作为回调函数。execute 方法是最基础的操作,而其他操作比如update、 query 等方法则是传人不同的 PreparedStatementCallback 参数来执行不同的逻辑,return updateCount(execute(psc, ps -> {try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValue: 将参数赋值给对应的参数类型pss.setValues(ps);}// ps.executeUpdate(): JDBC-API的基本操作int rows = ps.executeUpdate();if (logger.isTraceEnabled()) {logger.trace("SQL update affected " + rows + " rows");}return rows;}finally {if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}, true));}

基础方法execute

	/*** 功能描述:作为'公用的method',被具有'个性化'的方法(增删改查)调用。*         	execute 作为数据库操作的核心人口,將大多数数据库操作相同的步骤统一封装,而将个性化的操作使用参数 PreparedStatementCallback 进行回调。** @param psc* @param action* @param closeResources* @return* @param <T>* @throws DataAccessException*/@Nullableprivate <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action, boolean closeResources)throws DataAccessException {Assert.notNull(psc, "PreparedStatementCreator must not be null");Assert.notNull(action, "Callback object must not be null");if (logger.isDebugEnabled()) {String sql = getSql(psc);logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));}// 获取与'数据库事务'相绑定的'数据库连接'。Connection con = DataSourceUtils.getConnection(obtainDataSource());PreparedStatement ps = null;try {ps = psc.createPreparedStatement(con);applyStatementSettings(ps);// 执行数据库操作(CRUD)。处理一些通用方法外的个性化处理,也就是 PreparedStatementCallback 类型的参数的doInPreparedStatement 方法的回调。T result = action.doInPreparedStatement(ps);handleWarnings(ps);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}String sql = getSql(psc);psc = null;JdbcUtils.closeStatement(ps);ps = null;// 数据库的连接释放并不是直接调用了 Connection 的API中的close 方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库连接,这种情况下直接使用 ConnectionHolder 中的 released 方法进行连接数减一,面不是真正的释放连接。DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw translateException("PreparedStatementCallback", sql, ex);}finally {if (closeResources) {if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}JdbcUtils.closeStatement(ps);DataSourceUtils.releaseConnection(con, getDataSource());}}}

下面,我们对execute代码中重要的几个关键点进行分析。

1.获取数据库连接池

获取数据库连接也并非直接使用 dataSource.getConnection()方法那么简单,同样也考虑了诸多情况。

在数据库连接方面,Spring 主要考虑的是关于事务方面的处理。基于事务处理的特殊性,Spring 需要保证线程中的数据库操作都是使用同一个事务连接

public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {conHolder.requested();if (!conHolder.hasConnection()) {logger.debug("Fetching resumed JDBC Connection from DataSource");conHolder.setConnection(fetchConnection(dataSource));}return conHolder.getConnection();}// Else we either got no holder or an empty thread-bound holder here.logger.debug("Fetching JDBC Connection from DataSource");Connection con = fetchConnection(dataSource);// 功能描述:判断'当前线程'是否存在事务。// 目的:spring需要保证线程下的数据库操作,都使用同一个事务连接。那么当事务回滚的时候,可以一次性回滚所有的数据库操作。if (TransactionSynchronizationManager.isSynchronizationActive()) {try {// Use same Connection for further JDBC actions within the transaction.// Thread-bound object will get removed by synchronization at transaction completion.// 在事务中,使用同一个数据库连接。ConnectionHolder holderToUse = conHolder;if (holderToUse == null) {holderToUse = new ConnectionHolder(con);}else {holderToUse.setConnection(con);}holderToUse.requested();TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));holderToUse.setSynchronizedWithTransaction(true);if (holderToUse != conHolder) {TransactionSynchronizationManager.bindResource(dataSource, holderToUse);}}catch (RuntimeException ex) {// Unexpected exception from external delegation call -> close Connection and rethrow.releaseConnection(con, dataSource);throw ex;}}return con;}

2.应用用户设定的输入参数

	protected void applyStatementSettings(Statement stmt) throws SQLException {int fetchSize = getFetchSize();if (fetchSize != -1) {stmt.setFetchSize(fetchSize);}int maxRows = getMaxRows();if (maxRows != -1) {stmt.setMaxRows(maxRows);}DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());}

a: setFetchsize 最主要是为了减少网络交互次数设计的。访问 ResultSet 时,如果它每次只从服务器上读取一行数据,则会产生大量的开销。setFetchSize 的意思是当调用 rs.next 时,ResultSet会一次性从服务器上取得多少行数据回来,这样在下次 rs.next 时,它可以直接从内存中获取数据而不需要网络交互,提高了效率。这个设置可能会被某些 JDBC 驱动忽略,而且设置过大也会造成内存的上升。

b: setMaxRows 将此 Statement对象生成的所有 ResulSet 对象可以包含的最大行数限制设置为给定数。

3. 调用回调函数处理

一些通用方法外的个性化处理,也就是 PreparedStatementCallback 类型的参数的dolnPreparedStatement 方法的回调。

4. 资源释放

数据库的连接释放并不是直接调用了 Connection 的APL中的.close 方法。考虑到存在事务的情况,如果当前线程存在事务,那么说明在当前线程中存在共用数据库连接,这种情况下直接使用 ConnectionHolder 中的 released 方法进行连接数减一,面不是真正的释放连接。

public static void 	doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {if (con == null) {return;}if (dataSource != null) {// 当前线程存在事务的情况下说明存在'共用数据库连接',直接使用 ConnectionHolder 中的released 方法进行连接数减一而不是真正的释放连接。ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && connectionEquals(conHolder, con)) {// It's the transactional Connection: Don't close it.conHolder.released();return;}}// 直接使用Connection的API,调用close方法释放连接doCloseConnection(con, dataSource);}

Update中的回调函数

PreparedStatementCalback 作为一个接口,其中只有一个函数 doInPreparedStatement,这个函数是用于调用通用方法 execute 的时候无法处理的一些个性化处理方法,在 update 中的函数实现:

   protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)throws DataAccessException {logger.debug("Executing prepared SQL update");// PreparedStatementCallback作为回调函数。execute 方法是最基础的操作,而其他操作比如update、 query 等方法则是传人不同的 PreparedStatementCallback 参数来执行不同的逻辑,return updateCount(execute(psc, ps -> {try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValue: 将参数赋值给对应的参数类型pss.setValues(ps);}// ps.executeUpdate(): JDBC-API的基本操作int rows = ps.executeUpdate();if (logger.isTraceEnabled()) {logger.trace("SQL update affected " + rows + " rows");}return rows;}finally {if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}, true));}

其中用于真正执行 SQL 的 ps.executeUpdate 没有太多需要讲解的,因为我们平时在直接使用JDBC 方式进行调用的时候会经常使用此方法。但是,对于设置输人参数的函数 pss.set Values(ps),我们有必要去深人研究一下。在没有分析源码之前,我们至少可以知道其功能,不妨再回顾下 Spring 中使用 SOL 的执行过程,直接使用:
在这里插入图片描述
SQL 语句对应的参数,对应参数的类型清晰明了,这都归功于 Spring 为我们做了封裝,而真正的 JDBC 调用其实非常繁琐,你需要这么做:
在这里插入图片描述那么看看 Spring 是如何做到封装上面的操作呢?首先,所有的操作都是以 pss.setValues(ps)为入口的。还记得我们之前的分析路程吗?这个pss 所代表的当前类正是 ArgPreparedStatementSetter。其中的 setValues 的相关源码分析此处略。

2.query 功能的实现

源码分析入口1(关键)

在之前的章节中我们介绍了 update 方法的功能实现,那么在数据库操作中查找操作也是使用率非常高的函数,同样我们也需要了解它的实现过程。使用方法如下:
在这里插入图片描述

	@Overridepublic <T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException {return result(query(sql, args, argTypes, new RowMapperResultSetExtractor<>(rowMapper)));}@Override@Nullablepublic <T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse) throws DataAccessException {// 与 update 方法中都同样使用了 newArgTypePreparedStatementSetter。return query(sql, newArgTypePreparedStatementSetter(args, argTypes), rse);}@Override@Nullablepublic <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {return query(new SimplePreparedStatementCreator(sql), pss, rse);}

核心代码如下:

	@Nullablepublic <T> T query(PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)throws DataAccessException {Assert.notNull(rse, "ResultSetExtractor must not be null");logger.debug("Executing prepared SQL query");// 此处会调用和update一样的execute方法return execute(psc, new PreparedStatementCallback<T>() {@Override@Nullablepublic T doInPreparedStatement(PreparedStatement ps) throws SQLException {ResultSet rs = null;try {if (pss != null) {// ArgumentTypePreparedStatementSetter.setValuepss.setValues(ps);}// ps.executeQuery():JDBC-API的基本操作rs = ps.executeQuery();// rse.extractData(rsToUse)方法负责将结果进行封装并转换至 POJO, rse 当前代表的类为RowMapperResultSetExtractor,而在构造 RowMapperResultSetExtractor 的时候我们又将自定义的 rowMapper 设置了进去。return rse.extractData(rs);}finally {JdbcUtils.closeResultSet(rs);if (pss instanceof ParameterDisposer) {((ParameterDisposer) pss).cleanupParameters();}}}}, true);}

可以看到整体套路与 update 差不多的,只不过在回调类 PreparedStatementCallback 的:中使用的是 ps.executeQuery()执行查询操作,而且在返回方法上也做了一些额外的处理。

源码分析入口2(关键)

之前讲了 update 方法以及 query 方法,使用这两个附数示例的 SQL 都是带有参数的,也就是带有“?”的,那么还有另一种情况是不带有“?”的,Spring 中使用的是另一种处理方式。例如:

List<user> 1ist = jdbcTemplate.query ("selectfrom user", new UserRowMapper() );

追踪进入:

	@Overridepublic <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));}

核心源码:

    /*** 功能描述:与之前的 query 方法最大的不同是少了参数及参数类型的传递,自然也少了 PreparedStatementSetter 类型的封装。* * @param sql the SQL query to execute* @param rse a callback that will extract all rows of results*/@Override@Nullablepublic <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {Assert.notNull(sql, "SQL must not be null");Assert.notNull(rse, "ResultSetExtractor must not be null");if (logger.isDebugEnabled()) {logger.debug("Executing SQL query [" + sql + "]");}/*** Callback to execute the query.*/class QueryStatementCallback implements StatementCallback<T>, SqlProvider {@Override@Nullablepublic T doInStatement(Statement stmt) throws SQLException {ResultSet rs = null;try {rs = stmt.executeQuery(sql);return rse.extractData(rs);}finally {JdbcUtils.closeResultSet(rs);}}@Overridepublic String getSql() {return sql;}}// 既然少了 PreparedStatementSetter 类型的传入,调用的 execute 方法自然也会有所改变了。return execute(new QueryStatementCallback(), true);}

execute方法:

    /*** 功能描述:这个 exexute 与之前的 execute 并无太大差别,都是做一些常规的处理,诸如获取连接、释连接等。*         但是,有一个地方是不一样的,就是 statement 的创建。这里直接使用connection 创建,而带有参数的 SQL 使用的是 PreparedStatementCreator 类来创建的。一个是普通的 Statement,另一个是 PreparedStatement。** @param <T>* @throws DataAccessException*/@Nullableprivate <T> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = DataSourceUtils.getConnection(obtainDataSource());Statement stmt = null;try {stmt = con.createStatement();applyStatementSettings(stmt);T result = action.doInStatement(stmt);handleWarnings(stmt);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.String sql = getSql(action);JdbcUtils.closeStatement(stmt);stmt = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw translateException("StatementCallback", sql, ex);}finally {if (closeResources) {JdbcUtils.closeStatement(stmt);DataSourceUtils.releaseConnection(con, getDataSource());}}}

PreparedStatement 接口继承 Statement,并与之在两方面有所不同:

  1. PreparedStatement 实例包含已编译的 SQL 语句.这就是使语向“准备好”。包含于PreparedStatement 对象中的 SQL 语句可具有一个或多个IN 参数。IN 参数的值在 SQL语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“2”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的 setXXX 方法来提供。
  2. 由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

3.queryForObject

Spring 中不仅仅为我们提供了 query 方法,还在此基础上做了封装,提供了不同类型的 query方法。此处源码分析略。

总结

待补充。

相关文章:

《Spring源码深度分析》第8章 数据库连接JDBC

目录标题前言一、数据库连接方式1.JDBC连接数据库2.Spring Jdbc连接数据库(JdbcTemplate)二、JdbcTemplate源码分析1.update/save功能的实现源码分析入口(关键)基础方法execute1.获取数据库连接池2.应用用户设定的输入参数3. 调用回调函数处理4. 资源释放Update中的回调函数2.q…...

ModuleNotFoundError的解决方案【已解决】

问题描述 有包却提示ModuleNotFoundError 在正常情况下&#xff0c;你使用pip或者conda检查是否有相应包的时候&#xff0c;显示的是有的。但是一旦运行程序就会报这个ModuleNotFoundError错误。 问题可能是程序运行环境不对。 解决方案 &#xff08;1&#xff09;进入正确…...

Vue驼峰与短横线分割命名中有哪些坑

目录 0.前言 驼峰和短横线分割命名注意事项 组件注册命名 父子组件数据传递时命名 父子组件函数传递 0.前言 Vue驼峰命名法指的是将变量以驼峰形式命名&#xff0c;例如 userName、userId 等&#xff0c;而短横线分隔符法则指的是用短横线分隔变量名&#xff0c;例如 user…...

从文件中加载数据以及异常处理

上期学习了数据的存储&#xff0c;这次学习数据的加载 你可以使用把openpyxl.load_workbook() 来打开一个已经存在的工作簿 >>> from openpyxl import load_workbook >>> wb load_workbook(filename empty_book.xlsx) >>> sheet_ranges wb[ran…...

【JavaSE】方法的使用

方法的使用BIT-5-方法的使用绪论1. 方法概念及使用1.1什么是方法1.2 方法定义1.3 实参和形参的关系&#xff08;重要&#xff09;1.4 没有返回值的方法2. 方法重载2.1 为什么需要方法重载2.2 方法重载概念3. 递归3.1 生活中的故事3.2 递归的概念3.2 递归执行过程分析3.3 递归练…...

ModelScope 垂类检测系列模型介绍

文章目录ModelScope介绍垂类模型介绍调用方式1 Demo Service2 Notebook3 本地使用* 二次开发总结ModelScope介绍 ModelScope 是阿里达摩院推出的 中文版模型即服务&#xff08;MaaS, Model as a Service&#xff09;共享平台。该平台在2022年的云栖大会上发布&#xff0c;之前…...

Linux | Linux卸载和安装MySQL(Ubuntu版)

最近又来到了Linux学习了&#xff0c;原因是要接触云服务器相关知识&#xff0c; 所以博主整理了一些关于Linux的知识&#xff0c; 欢迎各位朋友点赞收藏&#xff0c;天天开心丫&#xff0c;快乐写代码&#xff01; Linux系列文章请戳 Linux教程专栏 目录 一、卸载MySQL 1…...

【C1】数据类型,常量变量,输入输出,运算符,if/switch/循环,/数组,指针,/结构体,文件操作,/编译预处理,gdb,makefile,线程

文章目录1.数据类型&#xff1a;单双引号&#xff0c;char&#xff08;1B&#xff09;&#xff0c;int/float&#xff08;32位系统&#xff0c;大小一样4B&#xff0c;但存储方式不同&#xff09;&#xff0c;double&#xff08;8B&#xff09;&#xff0c;long double&#xf…...

【深度学习】pytorch的基础操作

import torch import numpy as np # 1.1 根据已有的数据创建张量 def test01(): # 1.1 创建标量 data torch.tensor(10) print(data) # 1.2 使用numpy数组来创建张量 data np.random.randn(2,3) data torch.tensor(data) print(data) # 1.3使用list…...

MWORKS--同元软控MWORKS介绍、安装与使用

MWORKS--同元软控MWORKS介绍、安装与使用1 同元软控介绍1.1 同元软控简介1.2 同元软控发展历史2 MWORKS介绍2.1 MWORKS简介2.2 MWORKS产品描述3 装备数字化3.1 发展3.2 内涵3.3 系统模型发展成为产品的一部分3.4 MWORKS系统模型数据管理3.4 MWORKS为装备数字化提供的套件4 下载…...

Python 解决dilb和face_recognition第三方包安装失败

目录 dilb和face_recognition第三方包安装失败 亲测有效的解决方法&#xff1a;whl安装方式 dilb和face_recognition第三方包安装失败 场景复现&#xff1a;因为需要用到dlibface_recognition&#xff0c;基于OpenCV做一些人脸识别的项目&#xff0c;在Pycharm中进行pip清华…...

Mac系统Mysql的8.0.22版本安装笔记和密码重置修改密码等问题方法

忘记密码官网教程地址&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/resetting-permissions.html 5.7数据库安装指南参考&#xff1a;https://jingyan.baidu.com/article/fa4125ac0e3c2928ac709204.html 初次安装8.0.22遇到许多坑&#xff0c;密码修改失败&#xff1b…...

驱动 | Linux | NVMe 不完全总结

本文主要参考这里 1’ 2 的解析和 linux 源码 3。 此处推荐一个可以便捷查看 linux 源码的网站 bootlin 4。 更新&#xff1a;2022 / 02 / 11 驱动 | Linux | NVMe 不完全总结NVMe 的前世今生从系统角度看 NVMe 驱动NVMe CommandPCI 总线从架构角度看 NVMe 驱动NVMe 驱动的文件…...

一个测试人员,在现阶段的环境下如何在测试行业发展和自我价值。

前言周末和几个测试圈子里的大佬饭局上聊了一些职场和测试职业发展相关的话题&#xff0c;我将聊天的内容做了整理和阐述。。朋友圈有测试同学对这篇文章提了比较深刻的建议&#xff0c;下面是他的评价和建议&#xff1a;评价&#xff1a;据说是大佬饭桌总结&#xff0c;有两点…...

pwn手记录题2

fastbin_reverse_into_tcache(2.34) 本题所使用的libc版本为2.34&#xff1b;&#xff08;最新版 libc2.34版本已经没有了所谓的hook函数&#xff0c;甚至exit_hook(实际为某个函数指针)也已经不能够使用&#xff1b;能够利用的手法已经很少了&#xff1b; 高版本glibc堆的几…...

CSS ~ 从入门到入坑。

CSS ~ 从入门到入坑。 文章目录CSS ~ 从入门到入坑。what。css 三种实现方式。选择器。id 选择器 > class 选择器 > 标签选择器。标签选择器。类选择器。id 选择器。层次选择器。后代选择器。子选择器。相邻兄弟选择器。通用选择器。结构伪类选择器。属性选择器。字体风格…...

成都哪家机构的Java培训比较好,求一个不坑的?

关于这个问题&#xff0c;相信你会得到很多条答案&#xff0c;以及很多家机构的自荐。既然如此&#xff0c;不如也了解一下老牌IT职业教育机构&#xff1a;有足够丰富的教学经验&#xff0c;丰富的教学产品资源以及成熟的就业保障体系&#xff0c;还有就是承担风险的能力。 很…...

《爆肝整理》保姆级系列教程python接口自动化(十二)--https请求(SSL)(详解)

简介 本来最新的requests库V2.13.0是支持https请求的&#xff0c;但是一般写脚本时候&#xff0c;我们会用抓包工具fiddler&#xff0c;这时候会 报&#xff1a;requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590) 小编…...

离线数据仓库

1 数据仓库建模 1.1 建模工具 PowerDesigner/SQLYog/EZDML… 1.2 ODS层 &#xff08;1&#xff09;保持数据原貌不做任何修改&#xff0c;起到备份数据的作用。 &#xff08;2&#xff09;数据采用压缩&#xff0c;减少磁盘存储空间&#xff08;例如&#xff1a;压缩采用LZO&…...

【前端】Vue项目:旅游App-(23)detail:房东介绍、热门评论、预定须知组件

文章目录目标过程与代码房东介绍landlord热门评论HotComment预定须知Book效果总代码修改或添加的文件detail.vuedetail-book.vuedetail-hotComment.vuedetail-landlord.vue参考本项目博客总结&#xff1a;【前端】Vue项目&#xff1a;旅游App-博客总结 目标 根据detail页面获…...

JUC并发编程与源码分析

一、本课程前置知识及要求说明 二、线程基础知识复习 三、CompletableFuture 四、说说Java"锁"事 8锁案例原理解释: 五、LockSupport与线程中断 六、 Java内存模型之JMM 七、volatile与JMM 八、CAS 九、原子操作类之18罗汉增强 十、聊聊ThreadLocal 十一、Java对…...

Spark09: Spark之checkpoint

一、checkpoint概述 checkpoint&#xff0c;是Spark提供的一个比较高级的功能。有时候&#xff0c;我们的Spark任务&#xff0c;比较复杂&#xff0c;从初始化RDD开始&#xff0c;到最后整个任务完成&#xff0c;有比较多的步骤&#xff0c;比如超过10个transformation算子。而…...

《剑指offer》:数组部分

一、数组中重复的数字题目描述&#xff1a;在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的&#xff0c;但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如&#xff0c;如果输入长度为7的数组{2,3,1…...

基于微信小程序图书馆座位预约管理系统

开发工具&#xff1a;IDEA、微信小程序服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8项目构建&#xff1a;maven数据库&#xff1a;mysql5.7前端技术&#xff1a;vue、uniapp服务端技术&#xff1a;springbootmybatis本系统分微信小程序和管理后台两部分&#xff0c;项目采用…...

剑指 Offer Day1——栈与队列(简单)

本专栏将记录《剑指 Offer》的刷题&#xff0c;传送门&#xff1a;https://leetcode.cn/study-plan/lcof/。 目录剑指 Offer 09. 用两个栈实现队列剑指 Offer 30. 包含min函数的栈剑指 Offer 09. 用两个栈实现队列 原题链接&#xff1a;09. 用两个栈实现队列 class CQueue { pu…...

详解Python正则表达式中group与groups的用法

在Python中&#xff0c;正则表达式的group和groups方法是非常有用的函数&#xff0c;用于处理匹配结果的分组信息。 group方法是re.MatchObject类中的一个函数&#xff0c;用于返回匹配对象的整个匹配结果或特定的分组匹配结果。而groups方法同样是re.MatchObject类中的函数&am…...

Spring面试重点(三)——AOP循环依赖

Spring面试重点 AOP 前置通知&#xff08;Before&#xff09;&#xff1a;在⽬标⽅法运行之前运行&#xff1b;后置通知&#xff08;After&#xff09;&#xff1a;在⽬标⽅法运行结束之后运行&#xff1b;返回通知&#xff08;AfterReturning&#xff09;&#xff1a;在⽬标…...

计算机网络之HTTP04ECDHE握手解析

DH算法 离散读对数问题是DH算法的数学基础 &#xff08;1&#xff09;计算公钥 &#xff08;2&#xff09;交换公钥&#xff0c;并计算 对方公钥^我的私钥 mod p 离散对数的交换幂运算交换律使二者算出来的值一样&#xff0c;都为K k就是对称加密的秘钥 2. DHE算法 E&#…...

【MySQL数据库】主从复制原理和应用

主从复制和读写分离1. 主从复制的原理2. 主从复制的环境配置2.1 准备好数据库服务器2.2 配置master2.3 配置slave2.4 测试3. 主从复制的应用——读写分离3.1 读写分离的背景3.2 Sharding-JDBC介绍3.3 Sharding-JDBC使用步骤1. 主从复制的原理 MySQL主从复制是一个异步的过程&a…...

复现随记~

note(美团2022) 比较简单的越界漏洞&#xff0c;堆本身并没有什么漏洞&#xff0c;而且保护并没全开&#xff0c;所以逆向思维。必然是ROP类而非指针类&#xff0c;故我们着重注意unsigned int等无符号数前后是否不一致 int __fastcall edit(__int64 a1) {int idx; // [rsp14…...

英语网站大全免费/市场调研报告怎么写的

使用JavaScript开发IE浏览器本地插件实例 投稿&#xff1a;junjie 字体&#xff1a;[增加 减小] 类型&#xff1a;转载 时间&#xff1a;2015-02-18 我要评论 这篇文章主要介绍了使用JavaScript开发IE浏览器本地插件实例,本文讲解使用JS注册表的方式开发一个IE浏览器本地插件,需…...

网站做支付要多少钱/艺人百度指数排行榜

大家好&#xff0c;刚刚接触powershell&#xff0c;写了小脚本&#xff0c;各位大牛勿喷啊。小弟接触powershell 还没有一个星期。Get-EventLog application -after (get-date).adddays(-1) | Where-Object{($_.EntryType -eq "error") -or ($_.EntryType -eq "…...

美国主机网站建设/百度95099如何转人工

这里使用的是淘宝的接口 public class AddressUtil{ /** * * param content * 请求的参数 格式为&#xff1a;namexxx&pwdxxx * param encodingString * 服务器端请求编码。如GBK,UTF-8等 * return * throws UnsupportedEncodingException */ public static String getAddr…...

长沙产品网站建设/单个药品营销策划方案

一、昨天完成的 因为昨天课程较满&#xff0c;所以没有写太多的代码&#xff0c;在功能实现的方面并没有实质性的进展。 二、今天做的 继续Text文本框添加文字&#xff0c;解决不能显示的问题&#xff0c;添加文本框可以出现在截图区域任意位置的功能。 三、出现的问题 添加的文…...

香港网站服务器/91永久免费海外地域网名

假设我们有一个0和1的数组A&#xff0c;考虑N [i]是从索引A [0]到A [i]的第i个子数组&#xff0c;被解释为二进制数。我们必须找到一个布尔答案列表&#xff0c;其中且仅当N [i]被5整除时&#xff0c;答案[i]为真。因此&#xff0c;如果输入类似于[0,1,1,1,1,1,1]&#xff0c;则…...

wordpress 访问限制/域名批量查询工具

下面有这样一个场景&#xff1a; 数据库的某些字段是自增产生的&#xff08;比如说mysql&#xff0c;他就有自增的功能&#xff09; 我在插入数据的时候就没插入自增的那一段数据&#xff0c;但在插入数据后&#xff0c;我想看看自增的这条数据是什么&#xff08;回写操作&…...