Room的基本使用

参考:jetpack之Room数据库

目录

  • 引言
  • 一、基本使用
    • 1. 导入相关引用
    • 2. 建表Entity
    • 3. 数据库操作类Dao
    • 4. 数据库RoomDatabase
    • 5. 简单使用
  • 二、ViewModel + LiveData + Room 的结合开发
    • 1. 建表Entity
    • 2. 数据库操作类Dao
    • 3. 数据库RoomDatabase
    • 4. 仓库Repository
    • 5. ViewModel
  • 6. 数据适配器
    • 7. UI布局
    • 8. Activity的简单运用
  • 三、数据库升级
    • 1. 新增字段
    • 2. 删除字段
  • 四. Room的源码原理了解

引言

在这里插入图片描述
  Android Jetpack的出现统一了Android开发生态,各种三方库逐渐被官方组件所取代。
  Room也同样如此,逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份,更是因为其良好的开发体验,大大降低了SQLite的使用门槛。

  
相对于SQLiteOpenHelper等传统方法,使用Room操作SQLite有以下优势:

  • 编译器的SQL语法检查
  • 开发高效,避免大量模板代码
  • API设计友好,容易理解
  • 可以LiveData关联,具备LiveData Lifecycle的所有魅力

  
Room的使用,主要涉及以下3个组件:

  • Database:访问底层数据库的入口
  • Entity:代表数据库中的表(table),一般用注解
  • Data Access Object(DAO):数据库访问者

  通过Database获取DAO,然后通过DAO查询并获取entities,最终通过entities对数据库table中数据进行读写。

Room是SQLite数据库的抽象

一、基本使用

项目整体架构层级:
在这里插入图片描述

1. 导入相关引用

app的build.gradle中添加如下配置:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.myroom2"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

   // 导入Room相关依赖
    def room_version = "2.0.0-beta01"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version" // use kapt for Kotlin

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

    //ViewModel
    implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
}

2. 建表Entity

Student.java

package com.example.myroom.entity;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
// @Entity注解在类之上,代表是一张数据库表,tableName属性可以指定表的名称,默认为类名
// @Entity(tableName = "Student")
@Entity
public class Student  {

    // 主键  autoGenerate=true 自增长 从1开始
    @PrimaryKey(autoGenerate = true)
    private int uid;

    // 字段名称,name属性可以指定字段名称,默认为变量名
    // 默认不写@ColumnInfo注解,也会在数据库根据变量名新建一个字段。
    @ColumnInfo(name = "name")
    private String name;

    @ColumnInfo(name = "pwd")
    private String password;

    @ColumnInfo(name = "address")
    private int address;

    public Student(String name, String password, int address) {
        this.name = name;
        this.password = password;
        this.address = address;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAddress() {
        return address;
    }

    public void setAddress(int address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "uid=" + uid +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", address=" + address +
                '}';
    }
}

StudentTuple.java

package com.example.myroom.entity;

import androidx.room.ColumnInfo;

// @Entity 不能加,不然加了就是一张表了
// 并且 StudentTuple类中的name属性和pwd属性必须要和Student的name属性和pwd属性一致
public class StudentTuple {

    @ColumnInfo(name = "name")
    public String name;

    @ColumnInfo(name = "pwd")
    public String password;

    public StudentTuple(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "StudentTuple{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

  • @Entity:注解在类之上,代表是一张数据库表,tableName属性可以指定表的名称,默认为类名。
  • @PrimaryKey:表的主键,autoGenerate =true代表自增长。
  • @ColumnInfo:字段名称,name属性可以指定字段名称,默认为变量名。
  • @Ignore:该注解表示当前数据库忽略当前属性,不会创建该字段。
    默认不写@ColumnInfo注解,也会在数据库根据变量名新建一个字段。

3. 数据库操作类Dao

StudentDao.java

package com.example.myroom.dao;

import com.example.myroom.entity.Student;
import com.example.myroom.entity.StudentTuple;

import java.util.List;

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

// 当前类必须是interface
// @Dao注解在类之上,提供给外部操作数据库的方法
@Dao
public interface StudentDao {

    // Student...等价于Student[]
    @Insert
    void insert(Student... students);

    @Delete
    void delete(Student student);

    @Update
    void update(Student student);

    @Query("select * from Student")
    List<Student> getAll();

    // 查询一条记录
    @Query("select * from Student where name like:name")
    Student findByName(String name);

    // 数组查询  多个记录
    @Query("select * from Student where uid in(:userIds)")
    List<Student> getAllId(int[] userIds);

    // 只查询name 和 pwd 结果数据  给StudentTuple类接收
    // StudentTuple类中的name属性和pwd属性必须要和Student的name属性和pwd属性一致
    @Query("select name, pwd from Student")
    StudentTuple getRecord();
}

  • 当前类必须是interface
  • @Dao 注解在类之上,提供给外部操作数据库的方法
  • @Insert 添加,可以添加单个对象,亦可以是List、Array等集合类型。
  • @Query ,可以根据sql语句进行一系列查询、更新、删除操作。

4. 数据库RoomDatabase

AppDataBase.java

package com.example.myroom.db;

import com.example.myroom.dao.StudentDao;
import com.example.myroom.entity.Student;

import androidx.room.Database;
import androidx.room.RoomDatabase;

/**
 * @Dataase : 注解在类之上,抽象类,对外提供每张表的操作类
 * entities : 所有的数据库表代表的类
 * version  : 数据库版本,升级数据库需要修改版本号,一般和Migration配合使用
 * exportSchems : 存储展示数据库的结果信息,如果不设置的话,需要在database类上配置 exportSchema = false,要不然编译的时候会出现警告
 */
@Database(entities = {Student.class}, version = 1, exportSchema = false)
public abstract class AppDataBase extends RoomDatabase {

    // 暴露dao
    public abstract StudentDao userDao();

}
  • @Database: 注解在类之上,抽象类,对外提供每张表的操作类
  • entities:所有的数据库表代表的类
  • version:数据库版本,升级数据库需要修改版本好,一般和Migration配合使用
  • exportSchema :存储展示数据库的结构信息,如果不设置的话,需要再database类上配置exportSchema = false,要不然编译的时候会出现警告。

5. 简单使用

MainActivity.java

package com.example.myroom.ui;

import androidx.appcompat.app.AppCompatActivity;
import androidx.room.Room;

import android.os.Bundle;
import android.util.Log;

import com.example.myroom.R;
import com.example.myroom.dao.StudentDao;
import com.example.myroom.db.AppDataBase;
import com.example.myroom.entity.Student;
import com.example.myroom.entity.StudentTuple;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 数据库的操作都放到子进程中执行
        DBTest t = new DBTest();
        t.start();
    }

    public class DBTest extends Thread {
        @Override
        public void run() {
            // 数据库的操作都在这里
            AppDataBase db = Room.databaseBuilder(getApplicationContext(),
                    AppDataBase.class,
                    "MyRoomDB")

                    // 可以设置强制主线程,默认是让你用子线程
                    //.allowMainThreadQueries()

                    .build();

            StudentDao dao = db.userDao();

            // U
            dao.insert(new Student("test1", "111", 1));
            dao.insert(new Student("test2", "222", 2));
            dao.insert(new Student("test3", "333", 3));
            dao.insert(new Student("test4", "444", 4));
            dao.insert(new Student("test5", "555", 5));

            // 查询全部数据
            List<Student> all = dao.getAll();
            Log.d("abc", "run getAll(): all.toString()=" + all.toString());

            // 查询名字为test2的一条数据
            Student test2 = dao.findByName("test2");
            Log.d("abc", "run findByName(): test2.toString()=" + test2.toString());

            // 查询uid为2, 3, 4的三条数据
            List<Student> allId = dao.getAllId(new int[]{2, 3, 4});
            Log.d("abc", "run getAllId(): allId.toString()=" + allId.toString());

            // 查询Student包里面的数据 到 StudentTuple里面去
            StudentTuple record = dao.getRecord();
            Log.d("abc", "run getRecord(): record.toString()=" + record.toString());
        }
    }
}

log输出结果:

2023-08-31 11:52:37.856 3988-4016/com.example.myroom D/abc: run getAll(): all.toString()=[Student{uid=1, name='test1', password='111', address=1}, Student{uid=2, name='test2', password='222', address=2}, Student{uid=3, name='test3', password='333', address=3}, Student{uid=4, name='test4', password='444', address=4}, Student{uid=5, name='test5', password='555', address=5}]
2023-08-31 11:52:37.862 3988-4016/com.example.myroom D/abc: run findByName(): test2.toString()=Student{uid=2, name='test2', password='222', address=2}
2023-08-31 11:52:37.866 3988-4016/com.example.myroom D/abc: run getAllId(): allId.toString()=[Student{uid=2, name='test2', password='222', address=2}, Student{uid=3, name='test3', password='333', address=3}, Student{uid=4, name='test4', password='444', address=4}]
2023-08-31 11:52:37.882 3988-4016/com.example.myroom D/abc: run getRecord(): record.toString()=StudentTuple{name='test1', password='111'} record.toString()=StudentTuple{name='test1', password='111'}

二、ViewModel + LiveData + Room 的结合开发

在这里插入图片描述

项目整体框架结构:
在这里插入图片描述

1. 建表Entity

Student.java

package com.example.myroom2.entity;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class Student {

    @PrimaryKey(autoGenerate = true)
    private int uid;

    @ColumnInfo(name = "name")
    private String name;

    @ColumnInfo(name = "pwd")
    private String password;

    @ColumnInfo(name = "addressId")
    private int addressId;
    
    public Student(int uid, String name, String password, int addressId) {
        this.uid = uid;
        this.name = name;
        this.password = password;
        this.addressId = addressId;
    }

    public Student(String name, String password, int addressId) {
        this.name = name;
        this.password = password;
        this.addressId = addressId;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAddressId() {
        return addressId;
    }

    public void setAddressId(int addressId) {
        this.addressId = addressId;
    }

    @Override
    public String toString() {
        return "Student{" +
                "uid=" + uid +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", addressId=" + addressId +
                '}';
    }
}

2. 数据库操作类Dao

StudentDao.java

package com.example.myroom2.dao;

import com.example.myroom2.entity.Student;

import java.util.List;

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

@Dao
public interface StudentDao {

    @Insert
    void insert(Student... students);

    @Delete
    void delete(Student student);

    @Update
    void update(Student student);

    @Query("select * from Student")
    List<Student> getAll();

    // 使用LiveData 关联Room
    @Query("select * from Student order by uid")
    LiveData<List<Student>> getAllLiveDataStudent();
}

3. 数据库RoomDatabase

package com.example.myroom2.db;

import android.content.Context;

import com.example.myroom2.dao.StudentDao;
import com.example.myroom2.entity.Student;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

// exportSchema = false 尽量写,内部需要检测,如果没有写,会抛出异常,因为内部要记录升级的所有副本
@Database(entities = {Student.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {

    private static AppDatabase instance;

    public static synchronized AppDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context.getApplicationContext(),
                    AppDatabase.class,
                    "MyRoomDB")
                    // 可以强制在主线程运行数据库操作
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }

    // 暴露dao
    public abstract StudentDao studentDao();
}

4. 仓库Repository

StudentRepository.java

package com.example.myroom2.repository;

import android.content.Context;

import com.example.myroom2.dao.StudentDao;
import com.example.myroom2.db.AppDatabase;
import com.example.myroom2.entity.Student;

import java.util.List;

import androidx.lifecycle.LiveData;

// 仓库
public class StudentRepository {

    private LiveData<List<Student>> liveDataAllStudent;  // 触发 改变的 LiveData的数据
    private StudentDao studentDao;  // 用户操作,只面向Dao

    public StudentRepository(Context context) {
        AppDatabase database = AppDatabase.getInstance(context);
        studentDao = database.studentDao();

        if (liveDataAllStudent == null) {
            liveDataAllStudent = studentDao.getAllLiveDataStudent();  // 使用LiveData关联Room
        }
    }

    // 下面的代码是:提供一些API给ViewModel使用
    // 增
    public void insert(Student... students){
        studentDao.insert(students);
    }

    // 删
    public void delete(Student student) {
        studentDao.delete(student);
    }

    // 改
    public void update(Student student) {
        studentDao.update(student);
    }

    // 查 普通的查询
    public List<Student> getAll() {
        return studentDao.getAll();
    }

    // 查  关联LiveData 暴露环节
    public LiveData<List<Student>> getLiveDataAllStudent() {
        return studentDao.getAllLiveDataStudent();
    }

}

5. ViewModel

StudentViewModel.java

package com.example.myroom2.viewmodel;

import android.app.Application;

import com.example.myroom2.entity.Student;
import com.example.myroom2.repository.StudentRepository;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

// AndroidViewModel 保证数据的稳定性
// AndroidViewModel与ViewMOdel的区别是AndroidViewModel多了一个application环境
public class StudentViewModel extends AndroidViewModel {

    private StudentRepository studentRepository;

    public StudentViewModel(@NonNull Application application) {
        super(application);
        studentRepository = new StudentRepository(application);
    }

    // 增
    public void insert(Student... students) {
        studentRepository.insert(students);
    }

    // 删
    public void delete(Student student) {
        studentRepository.delete(student);
    }

    // 改
    public void update(Student student) {
        studentRepository.update(student);
    }

    // 查 普通的查
    public List<Student> getAll() {
        return studentRepository.getAll();
    }

    // 查  关联LiveData
    public LiveData<List<Student>> getAllLiveDataStudent() {
        return studentRepository.getLiveDataAllStudent();
    }
}

6. 数据适配器

这里简单采用ListView来显示列表数据

GoodsAdapter.java

package com.example.myroom2.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.myroom2.R;
import com.example.myroom2.entity.Student;

import java.util.List;

// ListView 的 Adapter
public class GoodsAdapter extends BaseAdapter {
    private LayoutInflater inflater;
    private List<Student> students;

    public GoodsAdapter(Context context, List<Student> students) {
        this.inflater = LayoutInflater.from(context);
        this.students = students;
    }

    @Override
    public int getCount() {
        return students.size();
    }

    @Override
    public Object getItem(int position) {
        return students.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = inflater.inflate(R.layout.item, null);
        Student student = students.get(position);

        TextView tv_id = view.findViewById(R.id.tv_id);
        tv_id.setText("number:" + student.getUid());

        TextView tv_name = view.findViewById(R.id.tv_name);
        tv_name.setText("name:"+student.getName());
        return view;
    }
}

7. UI布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        tools:ignore="MissingConstraints" />

</LinearLayout>

ListView列表中对应的item布局
item.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- 配合 ListView用的 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#666"
        android:textSize="28sp" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dip"
        android:textColor="#666"
        android:textSize="25sp"
        android:layout_marginLeft="30dp"/>

</LinearLayout>


8. Activity的简单运用

package com.example.myroom2.ui;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.widget.ListView;

import com.example.myroom2.R;
import com.example.myroom2.adapter.GoodsAdapter;
import com.example.myroom2.entity.Student;
import com.example.myroom2.repository.StudentRepository;
import com.example.myroom2.viewmodel.StudentViewModel;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    ListView listView;
    StudentViewModel studentViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = findViewById(R.id.listView);

        // 如果MainViewModel extends ViewModel
        // mainViewModel = new ViewModelProvider(this,
        //      new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);

        // 如果MainViewModel extends AndroidViewModel
        studentViewModel = new ViewModelProvider(getViewModelStore(), new ViewModelProvider.AndroidViewModelFactory(getApplication()))
                .get(StudentViewModel.class);

        // 观察者
        studentViewModel.getAllLiveDataStudent().observe(this, new Observer<List<Student>>() {
            @Override
            public void onChanged(List<Student> students) {
                // 更新UI
                listView.setAdapter(new GoodsAdapter(MainActivity.this, students));
            }
        });

        // 模拟 仓库
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 默认给数据库Room增加数据
                for (int i = 0; i < 50; i++) {
                    studentViewModel.insert(new Student("test", "123", 1));
                }
            }
        }.start();

        // 模拟仓库 数据库数据被修改了,一旦数据库被修改了,那么数据会驱动UI的发生改变
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    studentViewModel.update(new Student(4, "test" + i, "123", 1));
                }
            }
        }.start();
    }
}

三、数据库升级

  往往在实际的项目开发阶段会有这样的需求:后台服务器返回的实体字段信息一天一个样。如果我们想之前的数据信息不被覆盖,但是要把新的字段信息添加到数据库表中,就需要借助Migration了。

1. 新增字段

(1)第一步,在表中添加字段属性,比如在Student表中新增flag属性字段

// 注意:升级增加的字段
@ColumnInfo(name="flag")
private int flag;

Student.java

package com.example.myroom2.entity;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class Student {

    @PrimaryKey(autoGenerate = true)
    private int uid;

    @ColumnInfo(name = "name")
    private String name;

    @ColumnInfo(name = "pwd")
    private String password;

    @ColumnInfo(name = "addressId")
    private int addressId;


    // 注意:升级增加的字段
    @ColumnInfo(name="flag")
    private int flag;

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

    public Student(int uid, String name, String password, int addressId) {
        this.uid = uid;
        this.name = name;
        this.password = password;
        this.addressId = addressId;
    }

    public Student(String name, String password, int addressId) {
        this.name = name;
        this.password = password;
        this.addressId = addressId;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAddressId() {
        return addressId;
    }

    public void setAddressId(int addressId) {
        this.addressId = addressId;
    }

    @Override
    public String toString() {
        return "Student{" +
                "uid=" + uid +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", addressId=" + addressId +
                '}';
    }
}

(2)第二步,修改数据库版本,在RoomDatabase的实现类AppDatabase中修改version版本将之前的1改成2

// exportSchema = false 尽量写,内部需要检测,如果没有写,会抛出异常,因为内部要记录升级的所有副本
@Database(entities = {Student.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
	... ...
}

(3) 第三步,新建升级类MIGRATION_1_2,重写migrate()方法,在migrate()方法中进行sql语句操作:

    // 下面是稳定升级的方式
    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            // 这里用SQL脚本完成数据的变化
            database.execSQL("alter table Student add column flag integer not null default 1");
        }
    };

(4)第四步,添加数据库升级配置,在Room.databaseBuilder()中进行操作

instance = Room.databaseBuilder(context.getApplicationContext(),
                    AppDatabase.class,
                    "MyRoomDB")
                    // 可以强制在主线程运行数据库操作
                    .allowMainThreadQueries()

                    // 暴力升级 强制执行(数据会丢失 慎用)
                    // .fallbackToDestructiveMigration()

                    // 稳定的方式升级
                    .addMigrations(MIGRATION_1_2)
                    .build();

  其中(2)、(3)、(4)这三个步骤都是在RoomDatabase的实现类AppDatabase中进行操作,代码修改如下:

AppDatabase.java

package com.example.myroom2.db;

import android.content.Context;

import com.example.myroom2.dao.StudentDao;
import com.example.myroom2.entity.Student;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

// exportSchema = false 尽量写,内部需要检测,如果没有写,会抛出异常,因为内部要记录升级的所有副本
@Database(entities = {Student.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {

    private static AppDatabase instance;

    public static synchronized AppDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context.getApplicationContext(),
                    AppDatabase.class,
                    "MyRoomDB")
                    // 可以强制在主线程运行数据库操作
                    .allowMainThreadQueries()

                    // 暴力升级 强制执行(数据会丢失 慎用)
                    // .fallbackToDestructiveMigration()

                    // 稳定的方式升级
                    .addMigrations(MIGRATION_1_2)
                    .build();
        }
        return instance;
    }

    // 暴露dao
    public abstract StudentDao studentDao();

    // 下面是稳定升级的方式
    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            // 这里用SQL脚本完成数据的变化
            database.execSQL("alter table Student add column flag integer not null default 1");
        }
    };
}

2. 删除字段

  这里要注意Room是不能降级的,我非要删除一个字段,却要保证数据的稳定性,这个是特殊情况,需采用特殊手法进行降级。

  这个相对来说,比新增字段要复杂的多。比如我在上面的基础上再把flag字段删除掉
(1)第一步,将实体类中的flag字段删掉
(2)第二步,修改数据库版本,在RoomDatabase的实现类AppDatabase中修改version版本由原来的2修改为3。
(3)第三步,新建升级类MIGRATION_2_3,重写migrate()方法,在migrate方法中进行sql语句操作。

    static final Migration MIGRATION_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            // SQL 四步法
            // 1.先建立临时表
            database.execSQL("create table student_temp (uid integer primary key not null, name text, pwd text, addressId integer)");

            // 2.把之前表的数据插入到新表中
            database.execSQL("insert into student_temp (uid, name, pwd, addressId) " + "select uid, name, pwd, addressid from student");

            // 3.删除student 旧表
            database.execSQL("drop table student");

            // 4.修改 临时表为新表student
            database.execSQL("alter table student_temp rename to student");
        }
    };

其实这一步可以新建升级类MIGRATION_2_3继承Migration

public class MIGRATION_2_3 extends Migration {

    /**
     * 这个版本2要和对应的数据库版本对应
     */
    public MIGRATION_2_3() {
        super(2, 3);
    }
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {

        //TODO 1、创建一个备份表
        database.execSQL("create table student_temp (uid integer primary key not null, name text, pwd text, addressId integer)");

        //TODO 2、把数据拷贝到备份表中
        database.execSQL("insert into student_temp (uid, name, pwd, addressId) " + "select uid, name, pwd, addressid from student");

        //TODO 3、删除旧表
        database.execSQL("drop table student");

        //TODO 4、将备份表名称改为原来的表名称
        database.execSQL("alter table student_temp rename to student");

    }
}

注意,构造方法中版本是从2到3,需要在migrate方法中执行4个步骤才能将age字段移除。

  • 创建备份表,包含所有字段,除了age字段
  • 把原始数据拷贝到新的备份表中
  • 删除原始表
  • 修改备份表名称为原始表名称

(4)第四步,添加数据库升级配置,在Room.databaseBuilder中进行操作

instance = Room.databaseBuilder(context.getApplicationContext(),
                    AppDatabase.class,
                    "MyRoomDB")
                    // 可以强制在主线程运行数据库操作
                    .allowMainThreadQueries()

                    // 暴力升级 强制执行(数据会丢失 慎用)
                    // .fallbackToDestructiveMigration()

                    // 稳定的方式升级
                    .addMigrations(MIGRATION_2_3)
                    .build();

四. Room的源码原理了解

APT注解处理器生成代码
在这里插入图片描述
AppDataBase_Impl.java

package com.example.myroom.db;

import androidx.room.DatabaseConfiguration;
import androidx.room.InvalidationTracker;
import androidx.room.RoomOpenHelper;
import androidx.room.RoomOpenHelper.Delegate;
import androidx.room.util.TableInfo;
import androidx.room.util.TableInfo.Column;
import androidx.room.util.TableInfo.ForeignKey;
import androidx.room.util.TableInfo.Index;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import androidx.sqlite.db.SupportSQLiteOpenHelper.Callback;
import androidx.sqlite.db.SupportSQLiteOpenHelper.Configuration;
import com.example.myroom.dao.StudentDao;
import com.example.myroom.dao.StudentDao_Impl;
import java.lang.IllegalStateException;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings("unchecked")
public final class AppDataBase_Impl extends AppDataBase {
  private volatile StudentDao _studentDao;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `Student` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `pwd` TEXT, `address` INTEGER NOT NULL)");
        _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
        _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"228d349e23ba02cda67e8bc5ffa53bf0\")");
      }

      @Override
      public void dropAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("DROP TABLE IF EXISTS `Student`");
      }

      @Override
      protected void onCreate(SupportSQLiteDatabase _db) {
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onCreate(_db);
          }
        }
      }

      @Override
      public void onOpen(SupportSQLiteDatabase _db) {
        mDatabase = _db;
        internalInitInvalidationTracker(_db);
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onOpen(_db);
          }
        }
      }

      @Override
      protected void validateMigration(SupportSQLiteDatabase _db) {
        final HashMap<String, TableInfo.Column> _columnsStudent = new HashMap<String, TableInfo.Column>(4);
        _columnsStudent.put("uid", new TableInfo.Column("uid", "INTEGER", true, 1));
        _columnsStudent.put("name", new TableInfo.Column("name", "TEXT", false, 0));
        _columnsStudent.put("pwd", new TableInfo.Column("pwd", "TEXT", false, 0));
        _columnsStudent.put("address", new TableInfo.Column("address", "INTEGER", true, 0));
        final HashSet<TableInfo.ForeignKey> _foreignKeysStudent = new HashSet<TableInfo.ForeignKey>(0);
        final HashSet<TableInfo.Index> _indicesStudent = new HashSet<TableInfo.Index>(0);
        final TableInfo _infoStudent = new TableInfo("Student", _columnsStudent, _foreignKeysStudent, _indicesStudent);
        final TableInfo _existingStudent = TableInfo.read(_db, "Student");
        if (! _infoStudent.equals(_existingStudent)) {
          throw new IllegalStateException("Migration didn't properly handle Student(com.example.myroom.entity.Student).\n"
                  + " Expected:\n" + _infoStudent + "\n"
                  + " Found:\n" + _existingStudent);
        }
      }
    }, "228d349e23ba02cda67e8bc5ffa53bf0", "7b5888a424cc9ab1174dfe15e25efa2c");
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
  }

  @Override
  protected InvalidationTracker createInvalidationTracker() {
    return new InvalidationTracker(this, "Student");
  }

  @Override
  public void clearAllTables() {
    super.assertNotMainThread();
    final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
    try {
      super.beginTransaction();
      _db.execSQL("DELETE FROM `Student`");
      super.setTransactionSuccessful();
    } finally {
      super.endTransaction();
      _db.query("PRAGMA wal_checkpoint(FULL)").close();
      if (!_db.inTransaction()) {
        _db.execSQL("VACUUM");
      }
    }
  }

  @Override
  public StudentDao userDao() {
    if (_studentDao != null) {
      return _studentDao;
    } else {
      synchronized(this) {
        if(_studentDao == null) {
          _studentDao = new StudentDao_Impl(this);
        }
        return _studentDao;
      }
    }
  }
}

StudentDao_Impl.java

package com.example.myroom.dao;

import android.database.Cursor;
import androidx.room.EntityDeletionOrUpdateAdapter;
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.room.util.StringUtil;
import androidx.sqlite.db.SupportSQLiteStatement;
import com.example.myroom.entity.Student;
import com.example.myroom.entity.StudentTuple;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.lang.SuppressWarnings;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
public final class StudentDao_Impl implements StudentDao {
  private final RoomDatabase __db;

  private final EntityInsertionAdapter __insertionAdapterOfStudent;

  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfStudent;

  private final EntityDeletionOrUpdateAdapter __updateAdapterOfStudent;

  public StudentDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfStudent = new EntityInsertionAdapter<Student>(__db) {
      @Override
      public String createQuery() {
        return "INSERT OR ABORT INTO `Student`(`uid`,`name`,`pwd`,`address`) VALUES (nullif(?, 0),?,?,?)";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, Student value) {
        stmt.bindLong(1, value.getUid());
        if (value.getName() == null) {
          stmt.bindNull(2);
        } else {
          stmt.bindString(2, value.getName());
        }
        if (value.getPassword() == null) {
          stmt.bindNull(3);
        } else {
          stmt.bindString(3, value.getPassword());
        }
        stmt.bindLong(4, value.getAddress());
      }
    };
    this.__deletionAdapterOfStudent = new EntityDeletionOrUpdateAdapter<Student>(__db) {
      @Override
      public String createQuery() {
        return "DELETE FROM `Student` WHERE `uid` = ?";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, Student value) {
        stmt.bindLong(1, value.getUid());
      }
    };
    this.__updateAdapterOfStudent = new EntityDeletionOrUpdateAdapter<Student>(__db) {
      @Override
      public String createQuery() {
        return "UPDATE OR ABORT `Student` SET `uid` = ?,`name` = ?,`pwd` = ?,`address` = ? WHERE `uid` = ?";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, Student value) {
        stmt.bindLong(1, value.getUid());
        if (value.getName() == null) {
          stmt.bindNull(2);
        } else {
          stmt.bindString(2, value.getName());
        }
        if (value.getPassword() == null) {
          stmt.bindNull(3);
        } else {
          stmt.bindString(3, value.getPassword());
        }
        stmt.bindLong(4, value.getAddress());
        stmt.bindLong(5, value.getUid());
      }
    };
  }

  @Override
  public void insert(Student... students) {
    __db.beginTransaction();
    try {
      __insertionAdapterOfStudent.insert(students);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

  @Override
  public void delete(Student student) {
    __db.beginTransaction();
    try {
      __deletionAdapterOfStudent.handle(student);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

  @Override
  public void update(Student student) {
    __db.beginTransaction();
    try {
      __updateAdapterOfStudent.handle(student);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }

  @Override
  public List<Student> getAll() {
    final String _sql = "select * from Student";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    final Cursor _cursor = __db.query(_statement);
    try {
      final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
      final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
      final int _cursorIndexOfPassword = _cursor.getColumnIndexOrThrow("pwd");
      final int _cursorIndexOfAddress = _cursor.getColumnIndexOrThrow("address");
      final List<Student> _result = new ArrayList<Student>(_cursor.getCount());
      while(_cursor.moveToNext()) {
        final Student _item;
        final String _tmpName;
        _tmpName = _cursor.getString(_cursorIndexOfName);
        final String _tmpPassword;
        _tmpPassword = _cursor.getString(_cursorIndexOfPassword);
        final int _tmpAddress;
        _tmpAddress = _cursor.getInt(_cursorIndexOfAddress);
        _item = new Student(_tmpName,_tmpPassword,_tmpAddress);
        final int _tmpUid;
        _tmpUid = _cursor.getInt(_cursorIndexOfUid);
        _item.setUid(_tmpUid);
        _result.add(_item);
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }

  @Override
  public Student findByName(String name) {
    final String _sql = "select * from Student where name like?";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
    int _argIndex = 1;
    if (name == null) {
      _statement.bindNull(_argIndex);
    } else {
      _statement.bindString(_argIndex, name);
    }
    final Cursor _cursor = __db.query(_statement);
    try {
      final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
      final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
      final int _cursorIndexOfPassword = _cursor.getColumnIndexOrThrow("pwd");
      final int _cursorIndexOfAddress = _cursor.getColumnIndexOrThrow("address");
      final Student _result;
      if(_cursor.moveToFirst()) {
        final String _tmpName;
        _tmpName = _cursor.getString(_cursorIndexOfName);
        final String _tmpPassword;
        _tmpPassword = _cursor.getString(_cursorIndexOfPassword);
        final int _tmpAddress;
        _tmpAddress = _cursor.getInt(_cursorIndexOfAddress);
        _result = new Student(_tmpName,_tmpPassword,_tmpAddress);
        final int _tmpUid;
        _tmpUid = _cursor.getInt(_cursorIndexOfUid);
        _result.setUid(_tmpUid);
      } else {
        _result = null;
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }

  @Override
  public List<Student> getAllId(int[] userIds) {
    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
    _stringBuilder.append("select * from Student where uid in(");
    final int _inputSize = userIds.length;
    StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
    _stringBuilder.append(")");
    final String _sql = _stringBuilder.toString();
    final int _argCount = 0 + _inputSize;
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
    int _argIndex = 1;
    for (int _item : userIds) {
      _statement.bindLong(_argIndex, _item);
      _argIndex ++;
    }
    final Cursor _cursor = __db.query(_statement);
    try {
      final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
      final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
      final int _cursorIndexOfPassword = _cursor.getColumnIndexOrThrow("pwd");
      final int _cursorIndexOfAddress = _cursor.getColumnIndexOrThrow("address");
      final List<Student> _result = new ArrayList<Student>(_cursor.getCount());
      while(_cursor.moveToNext()) {
        final Student _item_1;
        final String _tmpName;
        _tmpName = _cursor.getString(_cursorIndexOfName);
        final String _tmpPassword;
        _tmpPassword = _cursor.getString(_cursorIndexOfPassword);
        final int _tmpAddress;
        _tmpAddress = _cursor.getInt(_cursorIndexOfAddress);
        _item_1 = new Student(_tmpName,_tmpPassword,_tmpAddress);
        final int _tmpUid;
        _tmpUid = _cursor.getInt(_cursorIndexOfUid);
        _item_1.setUid(_tmpUid);
        _result.add(_item_1);
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }

  @Override
  public StudentTuple getRecord() {
    final String _sql = "select name, pwd from Student";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    final Cursor _cursor = __db.query(_statement);
    try {
      final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
      final int _cursorIndexOfPassword = _cursor.getColumnIndexOrThrow("pwd");
      final StudentTuple _result;
      if(_cursor.moveToFirst()) {
        final String _tmpName;
        _tmpName = _cursor.getString(_cursorIndexOfName);
        final String _tmpPassword;
        _tmpPassword = _cursor.getString(_cursorIndexOfPassword);
        _result = new StudentTuple(_tmpName,_tmpPassword);
      } else {
        _result = null;
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp微信小程序用户隐私保护

使用wx.requirePrivacyAuthorize实现微信小程序用户隐私保护。 一、前言 微信小程序官方出了一个公告《关于小程序隐私保护指引设置的公告》。不整的话&#xff0c;后果很多授权无法使用&#xff0c;详见《小程序用户隐私保护指引内容介绍》 。 二、隐私相关设置 1、在 微信…

STM32的HAL库的定时器使用

用HAL库老是忘记了定时器中断怎么配置&#xff0c;该调用哪个回调函数。今天记录一下&#xff0c;下次再忘了就来翻一下。 系统的时钟配置&#xff0c;定时器的时钟是84MHz 这里定时器时钟是84M&#xff0c;分频是8400后&#xff0c;时基就是1/10000s&#xff0c;即0.1ms。Per…

CXL 内存交织(Memory Interleaving)

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c…

学习pytorch8 土堆说卷积操作

土堆说卷积操作 官网debug torch版本只有nn 没有nn.functional代码执行结果 B站小土堆视频学习笔记 官网 https://pytorch.org/docs/stable/nn.html#convolution-layers 常用torch.nn, nn是对nn.functional的封装&#xff0c;使函数更易用。 卷积核从输入图像左上角&#xf…

构建简单的Node.js HTTP服务器,发布公网远程访问的快速方法

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

线性代数的学习和整理16:什么是各种空间(类型),向量空间,距离(类型)?

目录 1 空间相关的群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的预备知识 1.1&#xff1a;群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的定义&#xff08;表示不懂&#xff0c;只是做个标记&#xff09; 2 空间 2.1 各种空间概念…

MySQL学习-day1

2023.8.26 今天是8月26号&#xff0c;正式开始学习MySQL&#xff0c;看的是尚硅谷的MySQL教程&#xff0c;每天记录一下自己的学习笔记&#xff0c;以供复盘。 数据库的常见概念 1、DB&#xff1a;数据库&#xff0c;存储数据的容器。 2、DBMS&#xff1a;数据…

JS数组原理探究!

JavaScript 数组的 API 经常会被 JS 开发者频繁使用&#xff0c;在整个 JavaScript 的学习过程中尤为重要。 数组作为一个最基础的一维数据结构&#xff0c;在各种编程语言中都充当着至关重要的角色&#xff0c;你很难想象没有数组的编程语言会是什么模样。特别是 JavaScript&…

04ShardingSphere-JDBC垂直分片

1、准备服务器 比如商城项目中&#xff0c;有用户、订单等系统&#xff0c;数据库在设计时用户信息与订单信息在同一表中。这里创建用户服务、订单服务实现数据库的用户信息和订单信息垂直分片 服务器规划&#xff1a;使用docker方式创建如下容器 服务器&#xff1a;容器名se…

Flink的checkpoint是怎么实现的?

分析&回答 Checkpoint介绍 Checkpoint容错机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。Flink的Checkpoint机制原理来自“Chandy-Lamport alg…

四川玖璨电子商务有限公司:如何给短视频做运营

短视频已经成为了人们日常生活中的娱乐方式之一。随着短视频平台的兴起&#xff0c;越来越多的人开始尝试在短视频上进行运营&#xff0c;而给短视频配上适合的音乐则是其中一项重要的工作。在本文中&#xff0c;我们将探讨如何给短视频做运营&#xff0c;特别是如何给短视频配…

非常详细的 Ceph 介绍、原理、架构

非常详细的 Ceph 介绍、原理、架构 1. Ceph架构简介及使用场景介绍 1.1 Ceph简介 Ceph是一个统一的分布式存储系统&#xff0c;设计初衷是提供较好的性能、可靠性和可扩展性。 Ceph项目最早起源于Sage就读博士期间的工作&#xff08;最早的成果于2004年发表&#xff09;&#…

第九章 动态规划part12(代码随想录)

309.最佳买卖股票时机含冷冻期 1. 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j]&#xff0c;第i天状态为j&#xff0c;所剩的最多现金为dp[i][j]。 2. 确定递推公式 拆分卖出股票状态是因为冷冻期前一天一定是具体卖出股票状态。 状态一 dp[i][0]&…

CUBLAS库入门教程(从环境配置讲起)

文章目录 前言一、搭建环境二、简单介绍三、 具体例子四、疑问 前言 CUBLAS库是NVIDIA CUDA用于线性代数计算的库。使用CUBLAS库的原因是我不想去直接写核函数。 &#xff08;当然&#xff0c;你还是得学习核函数该怎么写。但是人家写好的肯定比我自己写的更准确&#xff01;&…

微信小程序发布一个npm包

参考:https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html 同npm一样流程 npm install weixin_heath_apis

计算机视觉-LeNet

目录 LeNet LeNet在手写数字识别上的应用 LeNet在眼疾识别数据集iChallenge-PM上的应用 LeNet LeNet是最早的卷积神经网络之一。1998年&#xff0c;Yann LeCun第一次将LeNet卷积神经网络应用到图像分类上&#xff0c;在手写数字识别任务中取得了巨大成功。LeNet通过连续使用…

电脑组装教程分享!

案例&#xff1a;如何自己组装电脑&#xff1f; 【看到身边的小伙伴组装一台自己的电脑&#xff0c;我也想试试。但是我对电脑并不是很熟悉&#xff0c;不太了解具体的电脑组装步骤&#xff0c;求一份详细的教程&#xff01;】 电脑已经成为我们日常生活中不可或缺的一部分&a…

vue3组合式api bus总线式通信

vue2中可以创建一个 vue 实例&#xff0c; 做为 总结来完成组件间的通信 但是在vue3中&#xff0c; 这种方法是不能使用的。 因为vue3中main.js中&#xff0c; 使用的createApp() 没有机会再写 new Vue了 但是我们可以使用 mitt 的插件来解决这个问题 vue3 bus组件的用法 安装…

Python序列类型

序列&#xff08;Sequence&#xff09;是有顺序的数据列&#xff0c;Python 有三种基本序列类型&#xff1a;list, tuple 和 range 对象&#xff0c;序列&#xff08;Sequence&#xff09;是有顺序的数据列&#xff0c;二进制数据&#xff08;bytes&#xff09; 和 文本字符串&…

MATLAB中isequal函数转化为C语言

背景 有项目算法使用matlab中isequal函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中isequal简单介绍 语法 tf isequal(A,B) tf isequal(A1,A2,...,An) 说明 如果 A 和 B 等效&#xff0c;则 tf is…