Linux网络 序列化与反序列化

embedded/2025/1/23 10:13:11/

概念

序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。以下是关于序列化与反序列化的介绍:

  • 序列化:将对象的状态信息转换为可以存储或传输的格式,通常是字节序列或文本格式。
  • 反序列化:将序列化后的数据还原为原始对象或数据结构的过程。

出现原因

序列化的出现主要是为了满足在不同系统、不同语言之间进行数据传输和存储的需求,以下是具体原因:

  • 跨平台和跨语言通信:不同的操作系统和编程语言对数据的表示和存储方式各不相同。例如,Java中的对象在内存中的布局和C++中的对象就有很大差异。通过序列化,可以将数据转换为一种通用的格式,如JSON或XML,这样不同平台和语言编写的程序就能够相互理解和处理这些数据,实现跨平台和跨语言的通信。
  • 网络传输:在网络通信中,数据是以字节流的形式传输的。为了能够在网络上传输复杂的数据结构和对象,需要将它们序列化为字节流,然后在接收端进行反序列化,还原为原始的数据结构和对象。
  • 数据持久化:将对象存储到磁盘或数据库中时,需要先将其序列化为字节流或特定的存储格式,以便能够在需要时进行反序列化恢复。

常见的序列化格式

  • JSON:一种轻量级的数据交换格式,易于阅读和编写,广泛应用于Web开发和API设计中。
  • XML:一种标记语言,具有良好的扩展性和可读性,常用于数据存储和配置文件。
  • Protocol Buffers:一种高效的二进制序列化格式,具有较小的存储空间和较快的解析速度,常用于分布式系统和大数据应用中。

JSON

其中,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,比较常用,以下是关于它的介绍:

    特点

    • 轻量级:JSON比XML更小、更快,更易解析,占用带宽小,适合在网络上传输。
    • 易于阅读和编写:JSON采用类似于C语言家族的习惯,易于人阅读和编写,同时也易于机器解析和生成。
    • 独立于语言:JSON使用JavaScript语法来描述数据对象,但它独立于语言和平台,支持多种编程语言,如C、C++、Java、Python、PHP等。

    数据结构

    • 对象:对象是一个无序的“名称/值”对集合,以 { 左括号开始, } 右括号结束,每个“名称”后跟一个 : 冒号,“名称/值”对之间使用, 逗号分隔。
    • 数组:数组是值的有序集合,以 [ 左中括号开始, ] 右中括号结束,值之间使用, 逗号分隔。
    • :值可以是双引号括起来的字符串、数值、true、false、null、对象或者数组,这些结构可以嵌套。

    例如,有这样的数据

    hello2025.1.18Mike
    

    经过JSON序列化后,变成以下格式

    {"message": "hello","time": "2025.1.18","name": "Mike"
    }
    

    Jsoncpp

    Jsoncpp 是一个用于处理 JSON 数据的 C++ 库。它提供了将 JSON 数据序列化为字符串以及从字符串反序列化为 C++ 数据结构的功能。Jsoncpp 是开源的,广泛用于各种需要处理 JSON 数据的 C++ 项目中。

    安装

    在Linux中,在不同的环境下可以使用对应的指令安装

    C++
    ubuntu:sudo apt-get install libjsoncpp-dev
    Centos: sudo yum install jsoncpp-devel

    安装成功后就会包含在 /usr/include/jsoncpp 中,所以我们在使用该库时需要包含头文件<jsoncpp/json/json.h>.

    序列化

    序列化指的是将数据结构或对象转换为一种格式,以便在网络上传输或存储到文件中。Jsoncpp 提供了多种方式进行序列化:

    使用 Json::Value 的 toStyledString 方法

    优点:将 Json::Value 对象直接转换为格式化的 JSON 字符串。

    示例:
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    string s = root.toStyledString();
    cout << s << endl;
    return 0;
    }
    
    执行成功后的结果如下:
    {
    "name" : "joe",
    "sex" : "男"
    }

    使用 Json::StreamWriter

    优点:提供了更多的定制选项,如缩进、换行符等。
    示例:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <memory>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    Json::StreamWriterBuilder wbuilder; // StreamWriter 的工厂
    unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());
    stringstream ss;
    writer->write(root, &ss);
    cout << ss.str() << endl;
    return 0;
    }
    

    执行成功后的结果如下:

    {
    "name" : "joe",
    "sex" : "男"
    }

    使用 Json::FastWriter

    优点:比 StyledWriter 更快,因为它不添加额外的空格和换行符。
    示例:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <memory>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main()
    {
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    Json::FastWriter writer;
    string s = writer.write(root);
    cout << s << endl;
    return 0;
    }

    执行成功后的结果如下:

    {"name":"joe","sex":"男"}

    反序列化

    反序列化指的是将序列化后的数据重新转换为原来的数据结构或对象。Jsoncpp 提供了以下方法进行反序列化:

    使用 Json::Reader

    优点:提供详细的错误信息和位置,方便调试。
    示例:
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;
    int main() {
    // JSON 字符串
    string json_string = "{\"name\":\"张三\",\"age\":30, \"city\":\"北京\"}";
    // 解析 JSON 字符串
    Json::Reader reader;
    Json::Value root;
    // 从字符串中读取 JSON 数据
    bool parsingSuccessful = reader.parse(json_string,root);
    if (!parsingSuccessful) {
    // 解析失败,输出错误信息
    cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << endl;
    return 1;
    }
    // 访问 JSON 数据
    string name = root["name"].asString();
    int age = root["age"].asInt();
    string city = root["city"].asString();
    // 输出结果
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    cout << "City: " << city << endl;
    return 0;
    }

    执行成功后的结果如下:

    Name: 张三
    Age: 30
    City: 北京

    案例 网络版计算器

    例如, 我们需要实现一个服务器版的加法器,我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。

    makefile

    all: server client
    server:TcpServermain.ccg++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp
    client:TcpClient.ccg++ -o $@ $^ -std=c++17 -ljsoncpp
    .PHONY:clean
    clean:rm -f server client

    Mutex.hpp

    #pragma once
    #include <iostream>
    #include <pthread.h>
    using namespace std;class Mutex
    {
    public:Mutex(const Mutex&)=delete;const Mutex& operator=(const Mutex&)=delete;Mutex(){pthread_mutex_init(&_lock,nullptr);}~Mutex(){pthread_mutex_destroy(&_lock);}void Lock(){pthread_mutex_lock(&_lock);}pthread_mutex_t * LockPtr(){return &_lock;}void Unlock(){pthread_mutex_unlock(&_lock);}
    private:pthread_mutex_t _lock;
    };
    class LockGuard
    {public:LockGuard(Mutex& m):_mutex(m){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex& _mutex;
    };

    Cond.hpp

    #pragma once
    #include"Mutex.hpp"
    class Cond
    {public:Cond(){pthread_cond_init(&_cond,nullptr);}~Cond(){pthread_cond_destroy(&_cond);}void Wait(Mutex& mutex){pthread_cond_wait(&_cond,mutex.LockPtr());}void Notify(){pthread_cond_signal(&_cond);}void NotifyAll(){pthread_cond_broadcast(&_cond);}private:pthread_cond_t _cond;
    };

    Thread.hpp

    #pragma once
    #include <pthread.h>
    #include <iostream>
    #include <functional>
    #include <string>
    #include <unistd.h>
    using namespace std;
    using func_t = function<void(string)>;
    static int number = 1;
    enum STATUS
    {NEW,RUNNING,STOP
    };
    class Thread
    {
    private:static void *Routine(void *arg){Thread *t = static_cast<Thread *>(arg);t->_func(t->_name);return nullptr;}public:Thread(func_t func): _func(func), _status(NEW), _joinable(true){_name = "Thread-" + to_string(number++);_pid = getpid();}bool Start(){if (_status != RUNNING){_status = RUNNING;int n = pthread_create(&_tid, nullptr, Routine, this);if (n != 0){return false;}return true;}return false;}bool Stop(){if (_status == RUNNING){_status = STOP;int n = pthread_cancel(_tid);if (n != 0){return false;}return true;}return false;}bool Join(){if (_joinable){_status = STOP;int n = pthread_join(_tid, nullptr);if (n != 0){return false;}return true;}return false;}void Detach(){_joinable = false;pthread_detach(_tid);}string Name(){return _name;}
    private:string _name;pthread_t _tid;pid_t _pid;STATUS _status;bool _joinable;func_t _func;
    };

    ThreadPool.hpp

    #pragma once
    #include <iostream>
    #include <string>
    #include <queue>
    #include <vector>
    #include <memory>
    #include "Mutex.hpp"
    #include "Cond.hpp"
    #include "Thread.hpp"
    using thread_t = shared_ptr<Thread>;
    const static int defaultnum = 5;template <class T>
    class ThreadPool
    {
    private:bool IsEmpty() { return _taskq.empty(); }void HandlerTask(string name){cout << "线程: " << name << ", 进入HandlerTask的逻辑" << endl;while (true){// 1. 拿任务T t;{LockGuard lockguard(_lock);while (IsEmpty() && _isrunning){_wait_num++;_cond.Wait(_lock);_wait_num--;}// 2. 任务队列为空 && 线程池退出了if (IsEmpty() && !_isrunning)break;t = _taskq.front();_taskq.pop();}// 2. 处理任务t(); // 规定,未来所有的任务处理,全部都是必须提供t()方法!}cout << "线程: " << name << " 退出";}ThreadPool(const ThreadPool<T> &) = delete;ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false){for (int i = 0; i < _num; i++){_threads.push_back(make_shared<Thread>(bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));cout << "构建线程" << _threads.back()->Name() << "对象 ... 成功" << endl;}}public:static ThreadPool<T> *getInstance(){if (instance == NULL){LockGuard lockguard(mutex);if (instance == NULL){cout << "单例首次被执行,需要加载对象..." << endl;instance = new ThreadPool<T>();instance->Start();}}return instance;}void Equeue(T in){LockGuard lockguard(_lock);if (!_isrunning)return;_taskq.push(in);if (_wait_num > 0)_cond.Notify();}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread_ptr : _threads){cout << "启动线程" << thread_ptr->Name() << " ... 成功";thread_ptr->Start();}}void Wait(){for (auto &thread_ptr : _threads){thread_ptr->Join();cout << "回收线程" << thread_ptr->Name() << " ... 成功";}}void Stop(){LockGuard lockguard(_lock);if (_isrunning){_isrunning = false; // 不工作// 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了if (_wait_num > 0)_cond.NotifyAll();}}private:vector<thread_t> _threads;int _num;int _wait_num;std::queue<T> _taskq; // 临界资源Mutex _lock;Cond _cond;bool _isrunning;static ThreadPool<T> *instance;static Mutex mutex; // 只用来保护单例
    };template <class T>
    ThreadPool<T> *ThreadPool<T>::instance = NULL;
    template <class T>
    Mutex ThreadPool<T>::mutex; // 只用来保护单例

    InetAddr.hpp

    #pragma once
    #include <string>
    #include <iostream>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <cstring>
    using namespace std;
    class InetAddr
    {
    public:InetAddr();InetAddr(int port, string ip = ""): _port(port), _ip(ip){bzero(&_sockaddr, sizeof(_sockaddr));_sockaddr.sin_family = AF_INET;_sockaddr.sin_port = htons(_port);if (_ip.empty())_sockaddr.sin_addr.s_addr = INADDR_ANY;else_sockaddr.sin_addr.s_addr = inet_addr(_ip.c_str());}InetAddr(const struct sockaddr_in &sockaddr){_port = ntohs(sockaddr.sin_port);char buf[64];_ip = inet_ntop(AF_INET, &sockaddr.sin_addr, buf, sizeof(buf));}bool operator==(const InetAddr &other){return _ip == other._ip;}InetAddr operator=(const InetAddr &other){_ip = other._ip;_port = other._port;_sockaddr = other._sockaddr;return *this;}struct sockaddr *getSockaddr(){return (struct sockaddr *)&_sockaddr;}int getSockaddrLen(){return sizeof(_sockaddr);}const string &getIp(){return _ip;}int getPort(){return _port;}private:string _ip;int _port;struct sockaddr_in _sockaddr;
    };

    Common.hpp

    enum
    {SOCKET_ERROR=1,BIND_ERROR,LISTEN_ERROR,ACCEPT_ERROR,CONNECT_ERROR
    };

    Protocol.hpp

    #pragma once
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
    using namespace std;const string Sep = "\r\n";
    // 给信息添加报头
    //{json} -> length\r\n{json}\r\n
    bool Encode(string &message)
    {if (message.size() == 0)return false;string package = to_string(message.size()) + Sep + message + Sep;message = package;return true;
    }
    // 解析协议,提取信息
    bool Decode(string &package, string *message)
    {auto pos = package.find(Sep);if (pos == string::npos)                                                                                                              return false;string message_length_str = package.substr(0, pos);int message_length = stoi(message_length_str);int full_length = message_length_str.size() + 2 * Sep.size() + message_length;if (package.size() < full_length)return false;*message = package.substr(pos + Sep.size(), message_length);package.erase(0,full_length);return true;
    }
    class Request
    {
    public:Request(){}Request(int x, int y, char op): _x(x), _y(y), _op(op){}// 使用jsoncpp序列化void Serialize(string &out_str){Json::Value root;root["x"] = _x;root["y"] = _y;root["op"] = _op;out_str = root.toStyledString();}// 反序列化bool Deserialize(string &in_str){Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(in_str, root);if (!parsingSuccessful){cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages();return false;}_x = root["x"].asInt();_y = root["y"].asInt();_op = root["op"].asInt();return true;}void Print(){cout<<"x: "<<_x << endl;cout<<"y: "<<_y << endl;cout<<"op: "<<_op << endl;}int X() const {return _x;}int Y() const {return _y;}char Op() const {return _op;}
    private:int _x, _y;char _op;
    };class Response
    {public:Response(){}Response(int result ,int code):_result(result),_code(code){}void Serialize(string& out_str){Json::Value root;root["result"]=_result;root["code"]=_code;out_str=root.toStyledString();}bool Deserialize(string& in_str){Json::Value root;Json::Reader reader;bool parsingsuccessful=reader.parse(in_str,root);if(!parsingsuccessful){cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << endl;return false;}_result = root["result"].asInt();_code = root["code"].asInt();return true;}void SetResult(int res){_result=res;}void SetCode(int c){_code=c;}int Result(){return _result;}int Code(){return _code;}private:int _result = 0;int _code = 0;
    };

    TcpServer.hpp

    #pragma once
    #include <iostream>
    #include <pthread.h>
    #include <functional>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <cstring>
    #include <memory>
    #include "Common.hpp"
    #include "InetAddr.hpp"
    #include "ThreadPool.hpp"
    using namespace std;#define BACKLOG 8
    using handler_t = function<string(string &)>;
    static const uint16_t gport = 8080;class TcpServer
    {using task_t = function<void()>;struct ThreadData{int sockfd;TcpServer *self;};public:TcpServer(handler_t handler, uint16_t port = gport): _handler(handler), _port(port), _isrunning(false){}void InitServer(){// 创建socket_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){cout << "socket error" << endl;exit(SOCKET_ERROR);}cout << "socket create success,sockfd is: " << _listensockfd << endl;// 填写IP端口struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// bindint ret = bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));if (ret < 0){cout << "bind error" << endl;exit(BIND_ERROR);}cout << "bind success" << endl;// 将socket设置为监听状态ret = listen(_listensockfd, BACKLOG);if (ret < 0){cout << "listen error" << endl;exit(LISTEN_ERROR);}cout << "listen success" << endl;}void HandleRequest(int sockfd) // TCP为全双工通信{char buffer[4096];string package;while (true){// int n = read(sockfd, buffer, sizeof(buffer) - 1);int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;cout << buffer << endl;package += buffer;string cmd_result = _handler(package);// write(sockfd, cmd_result.c_str(), cmd_result.size());if (cmd_result.empty())continue;cout << cmd_result<<endl;send(sockfd, cmd_result.c_str(), cmd_result.size(), 0);}else if (n == 0){// 如果读取的值为0,说明client退出cout << "client quit" << endl;break;}else{// 读取失败break;}}close(sockfd);}void Start(){_isrunning = true;while (_isrunning){// 获取新连接struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &peerlen);if (sockfd < 0){cout << "accept error" << endl;exit(ACCEPT_ERROR);}cout << "accept success,sockfd is: " << sockfd << endl;InetAddr addr(peer);cout << "client info: " << addr.getIp() << ":" << addr.getPort() << endl;// 将任务交给线程池ThreadPool<task_t>::getInstance()->Equeue([&](){this->HandleRequest(sockfd);});}}void Stop(){_isrunning = false;}private:int _listensockfd; // 监听socketuint16_t _port;bool _isrunning;// 处理上层任务的入口handler_t _handler;
    };

    TcpServermain.cc

    #include "TcpServer.hpp"
    #include "CommandExec.hpp"
    #include "Daemon.hpp"
    #include "Calculator.hpp"
    #include <functional>
    #include <unistd.h>
    #include <memory>
    // 解析package
    using cal_func = function<Response(const Request &)>;
    class Parse
    {
    public:Parse(cal_func func): _func(func){}// 提取报文中一次计算的完整信息string Entry(string &package){// 判断报文完整性string message;string resstr;while (Decode(package, &message)){cout << message;if (message.empty())break;// 反序列化Request req;if (!req.Deserialize(message))break;cout << "Request: ";req.Print();// 计算Response res = _func(req);// 序列化string tmp;res.Serialize(tmp);cout << "序列化: " << tmp << endl;// 添加报头Encode(tmp);cout << "Encode: " << tmp;// 拼接应答resstr += tmp;}return resstr;}private:cal_func _func;
    };
    int main()
    {// 变成守护进程Daemon(false, false);Calculator mycal;Parse mypar([&](const Request &req){ return mycal.Execute(req); });unique_ptr<TcpServer> server = make_unique<TcpServer>([&](string& package){ return mypar.Entry(package); });server->InitServer();server->Start();return 0;
    }

    TcpClient.cc

    #include <iostream>
    #include <string>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <cstring>
    using namespace std;
    #include "Common.hpp"
    #include "Protocol.hpp"
    //./client server_ip server_port
    int main(int argc, char *argv[])
    {if (argc != 3){cout << "Usage:./client server_ip server_port" << endl;return 0;}string server_ip = argv[1];int server_port = stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cout << "socket create error" << endl;exit(SOCKET_ERROR);}// 填写网络信息struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());// client 无需显示bind,connect连接时自动bindint n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0){cout << "connect error" << endl;exit(CONNECT_ERROR);}string message;while (true){int x, y;char op;cout << "input x: ";cin >> x;cout << "input y: ";cin >> y;cout << "input op: ";cin >> op;Request req(x, y, op);req.Serialize(message); // 序列化Encode(message);        // 添加协议n = send(sockfd, message.c_str(), message.size(), 0);if (n > 0){char buffer[1024];int m = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (m > 0){buffer[m] = 0;string package = buffer;string content;Decode(package, &content); // 去报头提取内容Response res;              // 反序列化res.Deserialize(content);cout << res.Result() << "[" << res.Code() << "]" << endl;}elsebreak;}elsebreak;}close(sockfd);return 0;
    }

     

       


      http://www.ppmy.cn/embedded/156275.html

      相关文章

      C++之初识模版

      目录 1.关于模版的介绍 2.函数模版 2.1函数模板概念 2.2函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5模板参数的匹配原则 3.类模版 3.1类模板的定义格式 3.2 类模板的实例化 1.关于模版的介绍 C中的模板是一种通用编程工具&#xff0c;它允许程序员编…

      数据结构——堆(介绍,堆的基本操作、堆排序)

      我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

      C++:将字符数组rkpryyrag,每个字母转换为其前面第13个字母后输出,如果超过a则从z再继续接着数。例如:b前面第1个字母是a。a前面第3个字母是x。

      代码如下&#xff1a; #include <iostream> #include <string> using namespace std;int main(){string str "rkpryyrag";for (int i 0; i < str.length(); i){if (str[i] > a && str[i] < z){if (str[i] - a < 13){cout <<…

      ASP.NET Blazor部署方式有哪些?

      今天我们来说说Blazor的三种部署方式&#xff0c;如果大家还不了解Blazor&#xff0c;那么我先简单介绍下Blazor Blazor 是一种 .NET 前端 Web 框架&#xff0c;在单个编程模型中同时支持服务器端呈现和客户端交互性&#xff1a; ● 使用 C# 创建丰富的交互式 UI。 ● 共享使用…

      机器学习(5):支持向量机

      1 介绍 支持向量机&#xff08;Support Vector Machine&#xff0c;简称 SVM&#xff09;是一种监督学习算法&#xff0c;主要用于分类和回归问题。SVM 的核心思想是找到一个最优的超平面&#xff0c;将不同类别的数据分开。这个超平面不仅要能够正确分类数据&#xff0c;还要使…

      解决Oracle SQL语句性能问题(10.5)——常用Hint及语法(6)(并行相关Hint)

      10.5.3. 常用hint 10.5.3.6. 并行相关Hint 1)parallel:显式的指示优化器为SQL语句或SQL语句中的特定表指定或计算并行度。该Hint具体语法如下所示。 SQL> select|insert|update|delete /*+ parallel[(integer|default|auto|manual)] */ ...; --注: 1)这里,parallel…

      图论 八字码

      我们可能惊异于某些技巧。我们认为这个技巧真是巧妙啊。或者有人认为我依靠自己的直觉想出了这个表示方法。非常自豪。我认为假设是很小的时候&#xff0c;比如说小学初中&#xff0c;还是不错的。到高中大学&#xff0c;就有一些不成熟了。因为这实际上是一个竞技。很多东西前…

      【Elasticsearch】RestClient操作文档

      RestClient操作文档 新增文档实体类API语法 查询文档删除文档修改文档批量导入文档小结 新增文档 将数据库中的信息导入elasticsearch中 以商品数据为例 实体类 定义一个索引库结构对应的实体。 Data ApiModel(description "索引库实体") public class ItemDoc{…