【Android】Room—数据库的基本操作

news2024/11/15 19:32:06

引言

在Android开发中,数据持久化是一个不可或缺的部分。随着应用的复杂度增加,选择合适的数据存储方式变得尤为重要。Room数据库作为Android Jetpack架构组件之一,提供了一种抽象层,使得开发者能够以更简洁、更安全的方式操作SQLite数据库。本文将带你深入了解Room数据库的基本概念、使用方式以及最佳实践。

核心组件

Room是一个持久化库,它提供了一个抽象层,用于在SQLite数据库中存储和查询数据。它通过注解处理器和编译时检查,确保数据库操作的类型安全,减少了运行时错误的可能性。

Entity(实体):

  • Entity 是一个注解,用于标记一个类作为数据库中的一个表。
  • 每个 Entity 都映射到数据库中的一个表。
  • 你可以使用注解来定义表的名称(@PrimaryKey 用于定义主键,@Entity(tableName = "name")),以及定义列(@Column)。
  • 一个 Entity 类通常包含数据字段和适当的 getter 和 setter 方法。

Dao(数据访问对象):

  • Dao 是一个接口,它定义了对数据库的操作,如插入、查询、更新和删除。
  • 每个 Dao 都与一个 Entity 相关联,并且定义了与该 Entity 相关的数据库操作。
  • 你可以在 Dao 接口中使用注解来定义 SQL 语句,如 @Query@Insert@Update@Delete 等。
  • Room 会根据 Dao 接口自动生成实现代码。

Database(数据库):

  • Database 是一个抽象类,它定义了整个数据库的结构,包括所有的 Dao。
  • 它使用 @Database 注解来标记,并且可以定义版本号和包含的实体。
  • 你可以使用 Room.databaseBuilder() 方法来创建数据库实例。
  • 通常,一个应用中只有一个数据库类,它包含了所有的 Dao。

在这里插入图片描述

示例

只说概念可能会觉得抽象还是没有理解,接下来就通过一个示例来看看吧!

在使用room之前一定不要忘记它是要添加依赖

implementation("androidx.room:room-runtime:2.4.3")
annotationProcessor("androidx.room:room-compiler:2.4.3")

我们就设置一个学生示例吧,进行学生信息的增删改查操作,我们将学生的信息展示在页面上,设置增删改查操作的按钮,这部分的代码就不做说明了,你一定非常熟悉了。

  1. 定义Entity:我们要存放的是学生信息,先创建一个学生类:
@Entity(tableName = "student")
public class student {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    int id;
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    String name;
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    int age;
    public student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Ignore //告诉Room不要用这个构造方法,这是我们所用的方法
    public student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Ignore
    public student(int id) {
        this.id = id;
    }
    public int getAge() {
        return age;
    }
}

里面的方法大家已经写了很多遍了,接下来就给大家解释里面的注解:

  • @Entity(tableName = "student")
    • 这个注解标记了这个类是一个数据库表的映射。tableName属性指定了数据库中表的名称,这里是"student"
  • @PrimaryKey(autoGenerate = true)
    • 这个注解标记了一个字段作为表的主键。autoGenerate = true表示主键值是自动生成的,通常是自增的。
  • @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    • 这个注解提供了字段的额外信息。name属性指定了数据库中列的名称,这里是"id"typeAffinity属性指定了列的数据类型,这里是ColumnInfo.INTEGER,表示这个字段在数据库中是整型。
  1. ColumnInfo.INTEGER:表示列的数据类型为整型。
  2. ColumnInfo.TEXT:表示列的数据类型为文本。
  3. ColumnInfo.REAL:表示列的数据类型为浮点数。
  4. ColumnInfo.BLOB:表示列的数据类型为二进制数据(例如图片或文件)。
  5. ColumnInfo.FLOAT:表示列的数据类型为浮点数,与REAL类似,但更明确表示为浮点数。
  6. ColumnInfo.LONG:表示列的数据类型为长整型。
  7. ColumnInfo.SHORT:表示列的数据类型为短整型。
  8. ColumnInfo.BOOLEAN:表示列的数据类型为布尔值。
  • @Ignore
    • 这个注解用于告诉Room忽略接下来的构造函数,不将其作为数据库操作的一部分。这通常用于那些仅用于应用逻辑,而不是数据库操作的构造函数。
  1. 定义DAO:创建一个接口来定义数据访问对象(DAO),使用@Dao注解标记。在这个接口中,定义方法来执行数据库操作,如插入、查询、更新和删除。
@Dao
public interface StudentDao {
    @Insert
    void insertStudent(student... students);
    @Delete
    void deleteStudent(student... students);
    @Update
    void updateStudent(student... students);
    @Query("SELECT * FROM student")
    List<student> getAllStudent();
    @Query("SELECT * FROM student WHERE id = :id")
    List<student> getStudentById(int id);
}

都是接口当中定义的方法名,这里的注解大家也都见过,在学习SQLite的时候对这四个方法用了很多次了,这里是直接使用注解,来标记这个方法。看最后一个注解就是为了告诉它根据表的id来寻找你所要查找的学生信息。

  1. 定义Database:创建一个抽象类来继承RoomDatabase,并使用@Database注解标记。在这个类中,定义数据库的版本和包含的实体和DAO。
@Database(entities = {student.class}, version = 1, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {
    private static MyDataBase mInstance;
    private static final String DATABASE_NAME = "my_db.db";
    public static synchronized MyDataBase getInstance(Context context) {
        if (mInstance == null) {
            mInstance = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, DATABASE_NAME).build();
        }
        return mInstance;
    }
    public abstract StudentDao getStudentDao(); //Room会帮我们自动实现
}
  • @Database(entities = {student.class}, version = 1, exportSchema = false)
    • @Database注解用于定义数据库的配置。它告诉Room这个数据库包含哪些实体(entities),数据库的版本(version),以及其他一些配置项。
    • entities = {student.class}:指定了这个数据库包含的实体类。在这个例子中,它包含student实体。
    • version = 1:指定了数据库的版本号。当数据库结构发生变化时(例如添加、删除或修改实体),需要增加这个版本号。
    • exportSchema = false:指定是否允许Room导出数据库的schema文件。如果设置为true,Room会在编译时生成一个包含数据库schema的文件,这有助于调试和测试。在这个例子中,它被设置为false,意味着不导出schema文件。
  • public abstract class MyDataBase extends RoomDatabase
    • 定义了一个名为MyDataBase的抽象类,它继承自RoomDatabase。这个类将作为数据库的顶层接口,用于创建和管理数据库实例。
  • private static MyDataBase mInstance;
    • 定义了一个静态的MyDataBase实例,用于实现数据库的单例模式。这样可以确保整个应用程序中只有一个数据库实例。
  • private static final String DATABASE_NAME = "my_db.db";
    • 定义了一个常量,指定了数据库文件的名称。在这个例子中,数据库文件将被命名为my_db.db
  • public static synchronized MyDataBase getInstance(Context context)
    • 定义了一个静态方法,用于获取数据库的单例实例。这个方法使用了synchronized关键字来确保线程安全,避免在多线程环境下创建多个数据库实例。
    • context.getApplicationContext():获取应用程序级别的上下文,用于创建数据库实例。
  • if (mInstance == null) { mInstance = Room.databaseBuilder(...).build(); }
    • 如果mInstancenull,则使用Room.databaseBuilder()方法创建一个新的数据库实例。这个方法链式调用了多个配置方法,包括指定数据库类、数据库名称、以及数据库构建的其他配置。
  • public abstract StudentDao getStudentDao();
    • 定义了一个抽象方法,用于获取StudentDao的实例。Room在编译时会为这个方法生成实现代码,这样你就可以在应用程序中通过调用getStudentDao()来获取StudentDao的实例,进而执行数据库操作。
  1. 接下来就可以为按钮注册点击事件,运行程序看数据的更改了
public class MainActivity extends AppCompatActivity {
    StudentDao studentDao;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //......获取按钮,页面的滚动控件
        buttonAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student("Jack", 20);
                student s2 = new student("Rose", 30);
                new InsertStudentTask(studentDao).execute(s1, s2);
            }
        });
        buttonResearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new GetAllStudentTask(studentDao).execute();
            }
        });
        buttonUpdata.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student(3,"Tason", 21);
                new UpdataStudentTask(studentDao).execute(s1);
            }
        });
        buttonDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student(2);
                new DeleteStudentTask(studentDao).execute(s1);
            }
        });
    }

    class DeleteStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;
        public DeleteStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(student... students) {
            studentDao.deleteStudent(students);
            return null;
        }
    }
    class UpdataStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;
        public UpdataStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(student... students) {
            studentDao.updateStudent(students);
            return null;
        }
    }
    class InsertStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;

        public InsertStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(student... students) {
            studentDao.insertStudent(students);
            return null;
        }
    }
    class GetAllStudentTask extends AsyncTask<Void, Void, List<student>> {
        private StudentDao studentDao;

        public GetAllStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        @Override
        protected List<student> doInBackground(Void... voids) {
            return studentDao.getAllStudent();
        }

        @Override
        protected void onPostExecute(List<student> students) {
            super.onPostExecute(students);
            studentRecyclerViewAdapter.setstudent(students);
            studentRecyclerViewAdapter.notifyDataSetChanged();
        }
    }
}

注意我们不在主线程进行数据库的相关操作,

接下来就运行一下:

在这里插入图片描述

当我们进行增加操作,页面没有发生变化,我们点击查询操作,我们在上面的代码当中知道没当按下查询操作就会获取所有的学生信息,并将其展示在页面的滚动控件当中:

在这里插入图片描述

再看看删除和修改操作吧,上面的代码我们将2号学生删除,并修改3号学生的信息,操作后按下查询按钮:
在这里插入图片描述

优化

我们看到数据库的信息已经修改了。每次修改都要进行查询才能看到更新的信息就很麻烦,而且你也不知道到底有没有跟新就要进行查询操作,在之前我们学习了LiveData每当数据变化,就会自动告诉View,使其自动更新,那不就大大优化了程序吗

在这里插入图片描述

官方给出的架构指南,Model当中使用Room访问SQLite的内容。接下来就看看如何使用吧!

1、2、3步是一样的,这里就不多说了,只是对于数据库的操作有稍微的修改,第一次学习写的就不修改了,大家看看吧

@Dao
public interface StudentDao {
    @Insert
    void insertStudent(student... students);
    @Delete
    void deleteStudent(student... students);
    @Update
    void updateStudent(student... students);
    @Query("DELETE FROM student")
    void deleteAllAtudent();
    @Query("SELECT * FROM student")
    LiveData<List<student>> getAllStudentsLive();
}
  1. 创建Repository:Repository作为数据层的抽象,封装了数据来源,它可以是一个类,包含了一系列方法来执行数据库操作。这些方法通常会调用DAO中定义的操作,并将结果包装成LiveData或Flow对象,以便ViewModel可以观察数据变化。
public class StudentRepository {
    private StudentDao studentDao;

    public StudentRepository(Context context) {
        MyDataBase dataBase = MyDataBase.getInstance(context);
        this.studentDao = dataBase.getStudentDao();
    }
    //对数据进行添加
    public void insertStudent(student... students) {
        new InsertStudentTask(studentDao).execute(students);
    }
    class InsertStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;
        public InsertStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        @Override
        protected Void doInBackground(student... students) {
            studentDao.insertStudent(students);
            return null;
        }
    }
    //对数据进行修改
    public void updateStudent(student... students) {
        new UpdataStudentTask(studentDao).execute(students);
    }
    class UpdataStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;
        public UpdataStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }

        @Override
        protected Void doInBackground(student... students) {
            studentDao.updateStudent(students);
            return null;
        }
    }
    //对数据进行删除
    public void deleteStudent(student... students) {
        new DeleteStudentTask(studentDao).execute(students);
    }
    class DeleteStudentTask extends AsyncTask<student, Void, Void> {
        private StudentDao studentDao;
        public DeleteStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        @Override
        protected Void doInBackground(student... students) {
            studentDao.deleteStudent(students);
            return null;
        }
    }
    //对数据进行查找
    public LiveData<List<student>> getAllStudentsLive() {
        return studentDao.getAllStudentsLive();
    }
    //将所有数据都删除
    public void deleteAllStudent() {
        new DeleteAllStudentTask(studentDao).execute();
    }
    class DeleteAllStudentTask extends AsyncTask<Void, Void, Void> {
        private StudentDao studentDao;
        public DeleteAllStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        @Override
        protected Void doInBackground(Void... voids) {
            studentDao.deleteAllAtudent();
            return null;
        }
    }
}
  • 构造函数
    • StudentRepository的构造函数接收一个Context对象,用它来获取数据库的实例。MyDataBase.getInstance(context)是一个单例模式的数据库实例获取方法,确保整个应用中只有一个数据库实例。
  • 异步任务(AsyncTask)
    • InsertStudentTaskUpdataStudentTaskDeleteStudentTaskDeleteAllStudentTask是内部类,它们继承自AsyncTask。这些类用于在后台线程上执行数据库操作,以避免阻塞主线程。
    • AsyncTask已经被标记为过时,推荐使用java.util.concurrent包中的类、Kotlin Coroutines或者其他现代的并发解决方案。
  • doInBackground方法
    • AsyncTaskdoInBackground方法中执行实际的数据库操作。这个方法在后台线程上运行,接收的参数是传递给execute方法的参数。
  • LiveData
    • getAllStudentsLive方法返回一个LiveData对象,它包含了数据库中所有学生数据的列表。LiveData是一个可观察的数据存储器,它具有生命周期感知能力,只会在观察者处于活跃状态时发送数据更新。
  1. 使用ViewModel:ViewModel负责管理UI相关的数据,它可以调用Repository中的方法来获取数据,并根据数据变化更新UI。ViewModel使用LiveData或Flow作为数据的载体,这样当数据发生变化时,UI可以自动更新。
public class StudentViewModel extends AndroidViewModel {
    //当要使用上下文的时候使用AndroidViewModel
    private StudentRepository repository;
    public StudentViewModel(@NonNull Application application) {
        super(application);
        this.repository = new StudentRepository(application);
    }
    public void insertStudent(student... students) {
        repository.insertStudent(students);
    }
    public void deleteStudent(student... students) {
        repository.deleteStudent(students);
    }
    public void deleteAllStudent() {
        repository.deleteAllStudent();
    }
    public void updateStudent(student... students) {
        repository.updateStudent(students);
    }
    public LiveData<List<student>> research() {
        return repository.getAllStudentsLive();
    }
}
  1. 观察数据变化:在UI层(如Activity或Fragment),观察ViewModel中的LiveData或Flow对象。当数据发生变化时,UI层会收到通知并更新界面。
studentViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(StudentViewModel.class);
studentViewModel.research().observe(this, new Observer<List<student>>() {
    @Override
    public void onChanged(List<student> students) {
        studentRecyclerViewAdapter.setstudent(students);
        studentRecyclerViewAdapter.notifyDataSetChanged();
    }
}); //为其注册监听,到获取到的数据库内容进行变化,就更新UI
buttonAdd.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        student s1 = new student("Jack", 20);
        student s2 = new student("Rose", 30);
        studentViewModel.insertStudent(s1,s2);
    }
}); //只展示一个按钮的点击事件,其他的你也肯定会写了

接下来就运行程序看看吧!

我们进行了增加删除修改操作,页面直接自己更新了,带来了很大的便利

在这里插入图片描述

按下清空页面就没有了,数据库就被清空了!

本篇内容介绍了数据库的基本操作,感谢你的阅读!

文章到这里就结束了!

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

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

相关文章

PCIe进阶之TL:First/Last DW Byte Enables Rules Traffic Class Field

1 First/Last DW Byte Enables Rules & Attributes Field 1.1 First/Last DW Byte Enables Rules Byte Enable 包含在 Memory、I/O 和 Configuration Request 中。本文定义了相应的规则。Byte Enable 位于 header 的 byte 7 。对于 TH 字段值为 1 的 Memory Read Request…

【算法篇】哈希类(笔记)

目录 一、常见的三种哈希结构 二、LeetCode 练习 1. 有效的字母异位词 2. 两个数组的交集 3. 快乐数 4. 两数之和 5. 四数相加II 6. 赎金信 7. 三数之和 8. 四数之和 一、常见的三种哈希结构 当想使用哈希法来解决问题的时候&#xff0c;一般会选择如下三种数据…

java中的注解原理是什么?

Java中的注解&#xff08;Annotations&#xff09;是一种用于提供元数据的机制。它可以通过在代码中添加注解的形式&#xff0c;将一些额外的信息嵌入到代码里。注解本质上不会改变程序的实际逻辑行为&#xff0c;但是可以帮助开发工具、编译器、框架等获取这些元数据&#xff…

短信验证码倒计时 (直接复制即可使用) vue3

需求&#xff1a; 要实现一个获取验证码的需求&#xff0c;点击获取验证码60秒内不可以重复点击&#xff0c;方式有两种可以直接复制使用&#xff1b; 效果图 实现方案 方案1 (单个文件内使用比较推荐) <el-button :disabled"codeDisabled" click.stop"h…

SQL进阶的技巧:如何实现某列的累计乘积?

0 场景描述 在做数据处理的时候,尤其是复利累积的时候,有时候会有这样一场景,通过某种条件找到一列数据[X1,X2,X3...Xn],然后想要求y=X1X2X3...Xn。下面给出一个具体案例来详细解释这一问题,如下图所示,每个组的name值只有2个(2个A/B/C),当name=A or C时,price为value…

鸡蛋检测系统源码分享

鸡蛋检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

python webapi上传文件

一、安装 pip install Flask 二、 编写上传文件接口webapi.py http://127.0.0.1:5000/upload from flask import Flask,request from werkzeug.utils import secure_filename import uuidapp Flask(__name__)app.route(/) def hello_world():return Hello, World!app.post(…

MySQl篇(基本介绍)(持续更新迭代)

目录 一、为什么要使用数据库 1. 以前存储数据的方式 2. 什么是数据库 3. 采用的数据库的好处 4. 如何理解数据库、数据库管理系统、SQL 5. 如何理解数据是有组织的存储 6. 现在的数据库 二、关系型数据系统 1. 什么是关系型数据库 2. 关系型数据库特点 3. 关系型数据…

p11 日志,元数据,进程的查看

直接运行docker run -d centos这个时候回启动容器&#xff0c;但是因为容器里面没有前台进程所以这个时候docker会把没用的进程给停止掉&#xff0c;可以看到docker ps命令没有查看到任何的正在运行的容器 但是如果说你使用 -it命令进入到了容器里面&#xff0c;这个他就不会…

并发编程 - 锁(属性修饰符 atomic)

引言 在多线程编程中&#xff0c;数据一致性是一个必须解决的问题。多个线程同时访问同一片共享数据时&#xff0c;极易发生竞争条件&#xff08;race conditions&#xff09;&#xff0c;导致数据的不一致性&#xff0c;甚至程序崩溃。为了解决这些问题&#xff0c;我们需要引…

Vulnhub:BlueSky

靶机下载地址 信息收集 主机发现 nmap扫描攻击机同网段存活主机。 nmap 192.168.31.0/24 -Pn -T4 靶机ip&#xff1a;192.168.31.171。 端口扫描 nmap 192.168.31.171 -A -p- -T4 开放端口22,8080。 目录扫描 访问8080端口&#xff0c;如图&#xff0c;是tomcat管理页面…

Vue3.0组合式API:使用reactive()、ref()创建响应式代理对象

1、reactive() 方法 reactive() 方法用于将定义的 JavaScript 对象转换为响应式对象。 使用方法&#xff1a; <script setup> //第一步&#xff1a;导入函数 import { reactive } from vue;//第二步&#xff1a;创建响应式对象 const data reactive(对象类型的数据);…

prompt实用技巧-AI+Mermaid【酷炫钉钉文档】

AI 新技能&#xff0c;最近 chatGPTo1 发布后模型能力出现了新的跨越&#xff0c;之前模型的一本正经的胡说八道幻想模式&#xff0c;让AI 对待理科推理明显弱于文案的 AGI 的生成。 prompt engineer 工程师程序员的福音 prompt 内容如下&#xff0c; 按照以上格式生成创建公…

C++ Primer Plus(速记版)-类和数据抽象

第十二章 类 在 C 中&#xff0c;用类来定义自己的抽象数据类型。 12.1. 类的定义和声明 12.1.1. 类定义&#xff1a;扼要重述 最简单地说&#xff0c;类就是定义了一个新的类型和一个新作用域。 类成员 类成员可以是属性、方法或类型别名。 成员可以是公有&#xff08;publ…

CPU 缓存的局部性特性

CPU 缓存的局部性特性是现代处理器中用来提高程序执行效率的重要原理。局部性特性是指程序在运行时&#xff0c;其数据和指令的访问具有一定的规律性&#xff0c;主要体现在时间局部性和空间局部性两方面。利用这些规律&#xff0c;CPU 可以更高效地管理缓存&#xff0c;减少从…

github远程仓库环境搭建及使用

目录 1、创建一台虚拟机 centos 源的配置 备份源 修改源 重新加载缓存 安装软件 配置epel 2、关闭防火墙和selinux 关闭防火墙 临时关闭SELinux 永久关闭SELinux&#xff1a;编辑SELinux的配置文件 配置文件的修改内容 3、git是本地仓库&#xff0c;linux系统中一…

CleanClip:Mac上的剪贴板神器,轻松追溯历史记录

CleanClip 是一款功能强大的 Mac 剪贴板历史管理工具,它可以帮助用户轻松地管理和追溯复制粘贴的历史记录。CleanClip 能够保存用户复制过的所有内容,包括文本、图片、文件等,并且提供了多种视图模式,包括列表视图和网格视图,方便用户快速找到需要的内容。 评论区获取软件 Cl…

查询中的算术表达式

需要修改数据显示方式&#xff0c;如执行计算&#xff0c;或者作假定推测&#xff0c;这些都可能用到算术表达式。 一个算术表达式可以包含列名、固定的数字值和算术运算符。 使用算术运算符 示例&#xff1a;查询雇员的年薪&#xff0c;并显示他们的雇员ID&#xff0c;名字…

【计算机网络】应用层序列化

目录 一、序列化和反序列化二、重新理解 read、write、recv、send 和 tcp 为什么支持全双工三、Jsoncpp 一、序列化和反序列化 如果我们要实现一个网络版的加法器&#xff0c;需要把客户端的数据发给服务端&#xff0c;由服务端处理数据&#xff0c;再把处理结果发给客户端。 …

【线程】线程的概念

本文重点&#xff1a; 了解线程概念&#xff0c;理解线程与进程区别与联系 线程的概念 线程是进程内的一个执行分支&#xff0c;执行粒度要比进程细 乍一看&#xff0c;肯定不懂&#xff0c;太抽象了&#xff0c;怎么理解呢&#xff1f; 以前讲的进程&#xff0c;每个进程…