Qt实战:使用QSqlDatabase连接MySQL,并实现增删改查

文章目录

    • 一、创建数据表
    • 二、连接MySQL数据库
    • 三、封装成一个完整的轻量级 ORM 风格类
    • 四、实现派生具体模型类
    • 五、支持多线程连接池 + ORM + 事务封装

一、创建数据表

数据库名:
我们先创建一个数据库,名字叫 game_db:

CREATE DATABASE IF NOT EXISTS game_db DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
USE game_db;

创建玩家表:players

CREATE TABLE players (player_id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(32) NOT NULL UNIQUE,password_hash VARCHAR(64) NOT NULL,nickname VARCHAR(32),level INT DEFAULT 1,experience INT DEFAULT 0,gold INT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

插入 20 条玩家信息 SQL 语句:

INSERT INTO players (username, password_hash, nickname, level, experience, gold) VALUES
('user001', MD5('password1'), 'Knight001', 5, 1200, 500),
('user002', MD5('password2'), 'Mage002', 3, 800, 300),
('user003', MD5('password3'), 'Archer003', 7, 1800, 750),
('user004', MD5('password4'), 'Rogue004', 2, 400, 100),
('user005', MD5('password5'), 'Cleric005', 10, 3200, 1200),
('user006', MD5('password6'), 'Paladin006', 4, 950, 400),
('user007', MD5('password7'), 'Hunter007', 6, 1400, 600),
('user008', MD5('password8'), 'Druid008', 8, 2200, 900),
('user009', MD5('password9'), 'Monk009', 9, 2500, 1000),
('user010', MD5('password10'), 'Barbarian010', 1, 100, 50),
('user011', MD5('password11'), 'Warrior011', 5, 1300, 550),
('user012', MD5('password12'), 'Sorcerer012', 3, 700, 300),
('user013', MD5('password13'), 'Assassin013', 6, 1600, 650),
('user014', MD5('password14'), 'Priest014', 7, 1900, 800),
('user015', MD5('password15'), 'Ranger015', 2, 500, 200),
('user016', MD5('password16'), 'Berserker016', 4, 1100, 450),
('user017', MD5('password17'), 'Necromancer017', 8, 2300, 950),
('user018', MD5('password18'), 'Templar018', 9, 2700, 1100),
('user019', MD5('password19'), 'Shaman019', 10, 3000, 1250),
('user020', MD5('password20'), 'Witch020', 1, 200, 100);

表中结果如下所示:
在这里插入图片描述

二、连接MySQL数据库

database_manager.h

#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H#include <QObject>
#include <QMutex>
#include <QSqlDatabase>
#include <QSqlError>enum DatabaseType {MySQL = 0,SQLite,PostgreSQL,// 可扩展更多类型
};typedef struct stConnectParams {DatabaseType dbType;QString strHostName;QString strDbName;QString strUserName;QString strPassword;QString strConnectionName;int port;
} ConnectParams;class DatabaseManager : public QObject
{Q_OBJECT
public:static DatabaseManager& instance();void initConnectionParams(const ConnectParams& connectParams);void initializeConnection();private:explicit DatabaseManager(QObject *parent = nullptr);~DatabaseManager();DatabaseManager(const DatabaseManager&) = delete;DatabaseManager& operator =(const DatabaseManager&) = delete;DatabaseManager(const DatabaseManager&&) = delete;DatabaseManager& operator =(const DatabaseManager&&) = delete;signals:private:ConnectParams m_connectParams;
};

database_manager.cpp

#include "database_manager.h"#include <QDebug>DatabaseManager::DatabaseManager(QObject *parent) : QObject(parent)
{}DatabaseManager::~DatabaseManager()
{}DatabaseManager& DatabaseManager::instance()
{static DatabaseManager instance;return instance;
}void DatabaseManager::initConnectionParams(const ConnectParams& connectParams)
{m_connectParams.dbType = connectParams.dbType;m_connectParams.strHostName = connectParams.strHostName;m_connectParams.strDbName = connectParams.strDbName;m_connectParams.strUserName = connectParams.strUserName;m_connectParams.strPassword = connectParams.strPassword;m_connectParams.strConnectionName = connectParams.strConnectionName;m_connectParams.port = connectParams.port;
}void DatabaseManager::initializeConnection()
{if (QSqlDatabase::contains(m_connectParams.strConnectionName)) {qDebug() << "db:" << m_connectParams.strConnectionName << "already connected";return;}QString strDriver;switch (m_connectParams.dbType) {case DatabaseType::SQLite:strDriver = "QSQLITE";break;case DatabaseType::MySQL:strDriver = "QMYSQL";break;case DatabaseType::PostgreSQL:strDriver = "QPSQL";break;}if (strDriver.isEmpty()) {qDebug() << "can't find driver";return;}QSqlDatabase db = QSqlDatabase::addDatabase(strDriver, m_connectParams.strConnectionName);if (m_connectParams.dbType == DatabaseType::SQLite) {db.setDatabaseName(m_connectParams.strDbName); // SQLite只需数据库文件路径} else {db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=5;""MYSQL_OPT_READ_TIMEOUT=5;""MYSQL_OPT_WRITE_TIMEOUT=5;");db.setHostName(m_connectParams.strHostName);db.setPort(m_connectParams.port);db.setDatabaseName(m_connectParams.strDbName);db.setUserName(m_connectParams.strUserName);db.setPassword(m_connectParams.strPassword);}if (!db.open()) {qDebug() << "Database connection failed:" << db.lastError().text();} else {qDebug() << "Database connected successfully!";}
}

三、封装成一个完整的轻量级 ORM 风格类

什么是ORM 模型类的?
ORM(Object-Relational Mapping)模型类的作用,是将数据库中的表与 C++(或其他语言)中的类进行映射和封装,让你能像操作普通对象那样操作数据库数据,避免直接拼接 SQL 字符串,提高代码的可读性、可维护性、安全性和抽象性。

ORM 模型类的作用总结如下:
3.1 表结构 → 类结构
ORM 会把数据库表(如 users)映射成一个类(如 User),表字段对应类成员或字段名:

// 数据库表字段
// id | name | age// ORM类字段
QVariantMap row;
row["id"] = 1;
row["name"] = "Alice";
row["age"] = 30;

3.2 简化 SQL 操作
让你写这样的代码:

userModel.insert({{"name", "Tom"}, {"age", 25}});

而不需要写繁琐的 SQL:

INSERT INTO users (name, age) VALUES ('Tom', 25);

目标功能:

  • 封装成类 OrmModel 或基类 OrmBase
  • 支持设置数据库连接、表名
  • 支持 select / insert / update / delete 等基础操作
  • 使用 QVariantMap 传入/返回数据
  • ORM风格:调用 model.select(…) 而不是手写 SQL 字符串

类定义:OrmBase

#ifndef DBORMBASE_H
#define DBORMBASE_H#include <QObject>
#include <QSqlDatabase>
#include <QVariantMap>
#include <QVariantList>class DbOrmBase : public QObject
{Q_OBJECT
public:explicit DbOrmBase(const QString &tableName, const QString &connectionName);~DbOrmBase();// CRUDQVariantList select(const QString &strWhereClause = "", const QStringList &columns = {});bool insert(const QVariantMap &values);bool update(const QVariantMap &values, const QString &strWhereClause);bool remove(const QString &strWhereClause);private:bool isConnected() const;private:QString m_tableName;QString m_connectionName;QSqlDatabase m_db; // 保存数据库实例
};#endif // DBORMBASE_H

类实现:OrmBase.cpp

#include "OrmBase.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QDebug>OrmBase::OrmBase(const QString &tableName, const QString &connectionName): m_tableName(tableName), m_connectionName(connectionName)
{}QVariantList OrmBase::select(const QString &whereClause, const QStringList &columns)
{QVariantList results;QSqlDatabase db = QSqlDatabase::database(m_connectionName);if (!db.isOpen()) {qDebug() << "Database not open";return results;}QString columnStr = columns.isEmpty() ? "*" : columns.join(", ");QString sql = QString("SELECT %1 FROM %2").arg(columnStr, m_tableName);if (!whereClause.trimmed().isEmpty()) {sql += " WHERE " + whereClause;}QSqlQuery query(db);if (!query.exec(sql)) {qDebug() << "Select failed:" << query.lastError().text();return results;}while (query.next()) {QVariantMap row;QSqlRecord rec = query.record();for (int i = 0; i < rec.count(); ++i) {row[rec.fieldName(i)] = query.value(i);}results << row;}return results;
}bool OrmBase::insert(const QVariantMap &values)
{QSqlDatabase db = QSqlDatabase::database(m_connectionName);if (!db.isOpen()) return false;QStringList columns, placeholders;QVariantList bindValues;for (auto it = values.begin(); it != values.end(); ++it) {columns << it.key();placeholders << "?";bindValues << it.value();}QString sql = QString("INSERT INTO %1 (%2) VALUES (%3)").arg(m_tableName).arg(columns.join(", ")).arg(placeholders.join(", "));QSqlQuery query(db);query.prepare(sql);for (const QVariant &val : bindValues) {query.addBindValue(val);}if (!query.exec()) {qDebug() << "Insert failed:" << query.lastError().text();return false;}return true;
}bool OrmBase::update(const QVariantMap &values, const QString &whereClause)
{QSqlDatabase db = QSqlDatabase::database(m_connectionName);if (!db.isOpen()) return false;QStringList sets;QVariantList bindValues;for (auto it = values.begin(); it != values.end(); ++it) {sets << QString("%1 = ?").arg(it.key());bindValues << it.value();}QString sql = QString("UPDATE %1 SET %2").arg(m_tableName, sets.join(", "));if (!whereClause.trimmed().isEmpty()) {sql += " WHERE " + whereClause;}QSqlQuery query(db);query.prepare(sql);for (const QVariant &val : bindValues) {query.addBindValue(val);}if (!query.exec()) {qDebug() << "Update failed:" << query.lastError().text();return false;}return true;
}bool OrmBase::remove(const QString &whereClause)
{QSqlDatabase db = QSqlDatabase::database(m_connectionName);if (!db.isOpen()) return false;QString sql = QString("DELETE FROM %1").arg(m_tableName);if (!whereClause.trimmed().isEmpty()) {sql += " WHERE " + whereClause;}QSqlQuery query(db);if (!query.exec(sql)) {qDebug() << "Delete failed:" << query.lastError().text();return false;}return true;
}

使用示例:

#include <QCoreApplication>
#include <QDebug>
#include <QDateTime>#include "database_manager.h"
#include "db_ormbase.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);ConnectParams connectParams;connectParams.dbType = DatabaseType::MySQL;connectParams.strHostName = "localhost";connectParams.strDbName = "game_db";connectParams.strUserName = "root";connectParams.strPassword = "root";connectParams.strConnectionName = "user_game";connectParams.port = 3306;DatabaseManager::instance().initConnectionParams(connectParams);DatabaseManager::instance().initializeConnection();DbOrmBase userModel("players", "user_game");QVariantList playerList =  userModel.select("experience > 1000");for (const QVariant &playerVar : playerList) {QVariantMap playerMap = playerVar.toMap();int playerId = playerMap.value("player_id").toInt();QString username = playerMap.value("username").toString();QString nickname = playerMap.value("nickname").toString();int level = playerMap.value("level").toInt();int experience = playerMap.value("experience").toInt();int gold = playerMap.value("gold").toInt();QString passwordHash = playerMap.value("password_hash").toString();QDateTime createdAt = playerMap.value("created_at").toDateTime();// 可以进行打印、显示、或保存到类对象中qDebug() << "ID:" << playerId<< "用户名:" << username<< "昵称:" << nickname<< "等级:" << level<< "经验:" << experience<< "金币:" << gold<< "密码Hash:" << passwordHash<< "创建时间:" << createdAt.toString(Qt::ISODate);}return a.exec();
}

输出结果:
在这里插入图片描述

四、实现派生具体模型类

为什么要派生?
虽然 OrmBase 已支持通用 CRUD,但派生后的 UserModel 能实现:

  • 指定固定表名(如 players)
  • 添加和 players 表相关的专属函数(如 getAdultUsers()、findByName())
  • 让调用更具语义:UserModel user; user.insert(…)

创建 UserModel 类
UserModel.h

#ifndef USERMODEL_H
#define USERMODEL_H#include "OrmBase.h"class UserModel : public OrmBase
{
public:explicit UserModel(const QString &connectionName = "default");// 自定义业务函数QVariantList getAdultUsers();QVariantMap findById(int id);QVariantList findByName(const QString &name);
};#endif // USERMODEL_H

UserModel.cpp

#include "UserModel.h"UserModel::UserModel(const QString &connectionName): OrmBase("players", connectionName) // 固定表名为 users
{}// 查询年龄 >= 18 的用户
QVariantList UserModel::getAdultUsers()
{return select("experience >= 1000", {"player_id", "username", "experience"});
}// 查询某个 ID 的用户(返回一行)
QVariantMap UserModel::findById(int id)
{auto results = select(QString("player_id = %1").arg(id));return results.isEmpty() ? QVariantMap() : results.first().toMap();
}// 查询某个名字的所有用户
QVariantList UserModel::findByName(const QString &name)
{QString clause = QString("username = '%1'").arg(name.replace("'", "''")); // 简单防注入return select(clause);
}

五、支持多线程连接池 + ORM + 事务封装

将构建以下组件:

+-------------------------+
| DBConnectionPool       | 线程安全连接池类(单例)
+-------------------------+
| OrmBase                | ORM 基类(封装表操作 + 自动获取线程连接)
+-------------------------+
| OrmManager             | 管理多个模型,支持事务封装
+-------------------------+
| XxxModel : OrmBase     | 表模型(如 UserModel)
+-------------------------+

5.1 线程安全数据库连接池:DBConnectionPool
DBConnectionPool.h

#ifndef DBCONNECTIONPOOL_H
#define DBCONNECTIONPOOL_H#include <QSqlDatabase>
#include <QMutex>
#include <QMap>
#include <QThreadStorage>class DBConnectionPool
{
public:static DBConnectionPool &instance();void init(const QString &driver,const QString &host,int port,const QString &dbName,const QString &user,const QString &password,int maxConn = 10);QSqlDatabase getConnection();void closeAll();private:DBConnectionPool() = default;Q_DISABLE_COPY(DBConnectionPool)QString m_driver, m_host, m_dbName, m_user, m_password;int m_port = 3306;int m_maxConn = 10;QMutex m_mutex;int m_connIndex = 0;QThreadStorage<QString> m_threadConnName;
};#endif // DBCONNECTIONPOOL_H

DBConnectionPool.cpp

#include "DBConnectionPool.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>DBConnectionPool &DBConnectionPool::instance()
{static DBConnectionPool pool;return pool;
}void DBConnectionPool::init(const QString &driver,const QString &host,int port,const QString &dbName,const QString &user,const QString &password,int maxConn)
{m_driver = driver;m_host = host;m_port = port;m_dbName = dbName;m_user = user;m_password = password;m_maxConn = maxConn;
}QSqlDatabase DBConnectionPool::getConnection()
{if (!m_threadConnName.hasLocalData()) {QMutexLocker locker(&m_mutex);QString connName = QString("conn_%1").arg(++m_connIndex);m_threadConnName.setLocalData(connName);QSqlDatabase db = QSqlDatabase::addDatabase(m_driver, connName);db.setHostName(m_host);db.setPort(m_port);db.setDatabaseName(m_dbName);db.setUserName(m_user);db.setPassword(m_password);db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=5;""MYSQL_OPT_RECONNECT=1");if (!db.open()) {qCritical() << "Failed to open DB:" << db.lastError().text();}}return QSqlDatabase::database(m_threadConnName.localData());
}void DBConnectionPool::closeAll()
{QMutexLocker locker(&m_mutex);for (int i = 1; i <= m_connIndex; ++i) {QString name = QString("conn_%1").arg(i);if (QSqlDatabase::contains(name)) {QSqlDatabase::removeDatabase(name);}}m_connIndex = 0;
}

5.2 ORM 基类 OrmBase(使用连接池)

class OrmBase
{
public:OrmBase(const QString &tableName);virtual ~OrmBase() = default;bool insert(const QVariantMap &values);QVariantList select(const QString &whereClause = "", const QStringList &columns = {});bool update(const QVariantMap &values, const QString &whereClause);bool remove(const QString &whereClause);QSqlDatabase db() const;protected:QString m_tableName;
};

关键点:OrmBase 中不再保存 QSqlDatabase 实例,而是每次从线程池中获取:

QSqlDatabase OrmBase::db() const
{return DBConnectionPool::instance().getConnection();
}

5.3 事务封装类 OrmManager
用于封装如下逻辑:

  • beginTransaction()
  • commit()
  • rollback()
class OrmManager
{
public:static bool beginTransaction();static bool commit();static bool rollback();
};

实现:

bool OrmManager::beginTransaction()
{QSqlDatabase db = DBConnectionPool::instance().getConnection();return db.transaction();
}bool OrmManager::commit()
{QSqlDatabase db = DBConnectionPool::instance().getConnection();return db.commit();
}bool OrmManager::rollback()
{QSqlDatabase db = DBConnectionPool::instance().getConnection();return db.rollback();
}

5.4 使用示例:线程内事务 + 多表操作

DBConnectionPool::instance().init("QMYSQL", "127.0.0.1", 3306, "testdb", "root", "123456");UserModel user;
OrderModel order;OrmManager::beginTransaction();
bool ok1 = user.insert({{"name", "Tom"}});
bool ok2 = order.insert({{"user_id", 1}, {"amount", 200.0}});
if (ok1 && ok2)OrmManager::commit();
elseOrmManager::rollback();

5.5 线程使用示例(多线程安全)

QtConcurrent::run([](){UserModel u;u.insert({{"name", "ThreadUser"}});
});

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

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

相关文章

Python脚本保护工具库之pyarmor使用详解

概要 PyArmor是一个专门为Python代码提供加密保护的第三方库,旨在解决Python源代码易被反编译和泄露的安全问题。作为一种动态代码保护工具,PyArmor能够对Python脚本进行混淆和加密处理,有效防止源代码被恶意获取、分析或篡改。该库特别适用于商业软件开发、知识产权保护和…

仓颉编程语言:从入门到精通

为啥要瞅瞅仓颉这玩意儿&#xff1f; 有一说一&#xff0c;现在的编程语言多得跟米一样&#xff0c;对吧&#xff1f;那一门新语言想火&#xff0c;没点绝活儿肯定不行。仓颉&#xff08;Cangjie&#xff09;这哥们儿&#xff0c;是华为搞出来的新玩意儿&#xff0c;静态编译的…

线性探针是什么:是一种用于探测神经网络中特定特征的工具

线性探针是什么 线性探针是一种在机器学习和相关领域广泛应用的技术,用于评估预训练模型特征、检测数据中的特定序列等。在不同的应用场景下,线性探针有着不同的实现方式和作用: 评估预训练模型特征:在机器学习中,线性探针是一种评估预训练模型“特征迁移能力”的标准化方…

【论文阅读】Few-Shot PPG Signal Generation via Guided Diffusion Models

从少量样本数据选择到后处理的整体框架。首先,扩散模型在N样本数据集和指导下的训练。接着,模型生成一个增强的数据集,并进一步优化以提高保真度。最后,这些合成数据与少量样本训练数据集结合,用于基准模型的训练和评估。数据分布从最初的红色变为保真度增强的蓝色,这表明…

CentOS-7的“ifupdown“与Debian的“ifupdown“对比 笔记250706

CentOS-7的"ifupdown"与Debian的"ifupdown"对比 笔记250706 CentOS 7 和 Debian 的 ifupdown 工具名称相同&#xff0c;但在实现机制、配置文件语法和系统集成上存在显著差异。以下是核心对比分析&#xff1a; ⚙️ 一、核心差异概览 对比维度CentOS 7De…

架构如传承:技术长河中的可持续乐章

代码结构&#xff1a;协作基石 在软件开发的世界里&#xff0c;代码结构就如同建筑的框架&#xff0c;支撑着整个项目的运行。想象一下&#xff0c;你加入了一个新的开发团队&#xff0c;接手一个已经有一定规模的项目。当你打开代码库&#xff0c;看到的是一团乱麻般的代码&a…

Ubuntu22.04更新Openssh至9.9p2无法正常连接,报错解决

Ubuntu22.04更新Openssh至9.9p2无法正常连接&#xff0c;报错解决 1.报错信息如下所示ExecStart/usr/sbin/sshd -D $SSHD_OPTS (codeexited, status255/EXCEPTION)2.这通常说明 SSH 配置文件存在语法错误、缺失关键文件&#xff0c;或者端口被占用等问题。 3.检查配置文件是否有…

基于小程序的智能停车管理系统设计与开发

项目介绍 本课程演示的是一款基于小程序的智能停车管理系统设计与开发&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3…

多模态大语言模型arxiv论文略读(155)

Panther: Illuminate the Sight of Multimodal LLMs with Instruction-Guided Visual Prompts ➡️ 论文标题&#xff1a;Panther: Illuminate the Sight of Multimodal LLMs with Instruction-Guided Visual Prompts ➡️ 论文作者&#xff1a;Honglin Li, Yuting Gao, Chengl…

SAP ERP与Oracle EBS对比,两个ERP系统有什么区别?

据统计&#xff0c;2024年中国ERP软件市场规模预计突破210亿元&#xff0c;其中SAP和Oracle占据第一梯队&#xff0c;共占国内ERP市场45%以上的份额&#xff0c;在高端市场尤其显著。SAP和Oracle作为ERP行业的两大巨头&#xff0c;具体有什么区别呢&#xff1f;SAP是什么&#…

网络安全之RCE分析与利用详情

Gogs背景介绍Gogs&#xff08;Go Git Service&#xff09;是一款用Go语言编写的轻量级、开源的Git仓库托管系统。它的设计目标是让搭建和维护Git服务变得简单、快速&#xff0c;同时提供类似GitHub的功能&#xff0c;但对资源消耗更少&#xff0c;适合个人或者小型团队使用&…

OpenCV图片操作100例:从入门到精通指南(2)

接上篇&#xff0c;本文将继续分享OpenCV实用技巧&#xff0c;涵盖图像处理、目标检测、3D视觉等进阶领域&#xff01;六、图像变换进阶17. 图像金字塔# 高斯金字塔下采样 smaller cv2.pyrDown(img)# 高斯金字塔上采样 larger cv2.pyrUp(img)用于多尺度图像处理&#xff0c;构…

2、Connecting to Kafka

KafkaAdmin-请参阅配置主题ProducerFactory-请参阅发送消息ConsumerFactory-请参阅接收消息从2.5版本开始&#xff0c;每个版本都扩展了KafkaResourceFactory。这允许在运行时通过向引导服务器的配置中添加Supplier<String>来更改引导服务器&#xff1a;setBootstrapServ…

二进制部署CentOS8.5+Kubernetes1.33.2+Docker28.3.1高可用集群

Kubernetes 集群部署202507 本实验主要软件环境及资源如下&#xff1a; 二进制部署CentOS8.5Kubernetes1.33.2Docker28.3.1高可用集群 一、系统要求 ​Kubermetes 系统由一组可执行程序组成&#xff0c;用户可以通过Kubernetes在GitHub 的项目网站下载编译好的二进制文件或…

127. Java 泛型 - 泛型类与子类型

文章目录127. Java 泛型 - 泛型类与子类型1. 泛型类和接口的子类型化示例&#xff1a;ArrayList 和 List2. 自定义泛型接口的子类型化示例&#xff1a;泛型接口的子类型解释3. 泛型类和接口的类型参数4. 总结127. Java 泛型 - 泛型类与子类型 1. 泛型类和接口的子类型化 在 J…

内网服务器怎么设置公网远程访问? windows桌面连接和Linux自带SSH外网异地跨网用完整步骤教程

没有公网IP的本地主机跨网访问是经常需要用到的网络场景。要设置内网服务器在公网进行异地远程访问&#xff0c;需依次完成确保网络连接正常、配置防火墙、启用远程访问服务、和利用类似nat123内网映射外网打通等一系列步骤&#xff0c;以保障不同内网的远程访问的顺利进行。一…

数据提取之bs4(BeautifuSoup4)模块与Css选择器

BeautifuSoup4from bs4 import BeautifulSoup创建对象 <class bs4.BeautifulSoup>soup BeautifulSoup(源码, 解析器)bs4标签种类&#xff08;1&#xff09;tag: 标签print(soup.title, type(soup.title))&#xff08;2&#xff09;获取标签里面的文本内容, 可导航的字符…

CPP中的List

一.list的介绍&#xff1a;1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。2.list的底层是双向链表结构&#xff0c;带有哨兵位的头结点 。3. list与forward_list非常相似&#xff1a;最主要的不同在于forward_list是单…

Ntfs!LfsUpdateLfcbFromRestart函数分析之Ntfs!LfsFindOldestClientLsn

第0部分&#xff1a;//// Find the oldest client Lsn. Use the last flushed Lsn as a starting point.//Lfcb->OldestLsn Lfcb->LastFlushedLsn;LfsFindOldestClientLsn( RestartArea,Add2Ptr( RestartArea, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD ),&…

「日拱一码」021 机器学习——特征工程

目录 特征选择 过滤法&#xff08;Filter Methods&#xff09; 方差选择法 相关系数法 卡方检验 包裹法&#xff08;Wrapper Methods&#xff09; 递归特征消除&#xff08;RFE&#xff09; 嵌入法&#xff08;Embedded Methods&#xff09; L1正则化&#xff08;Lasso…