Room的基本使用

news2024/12/25 0:35:28

参考: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.coloradmin.cn/o/954209.html

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

相关文章

什么时候用增量式PID,什么时候用位置式PID

PID控制器原理&#xff1a; 增量式PID&#xff1a; 位置式PID&#xff1a; 什么时候用位置式PID&#xff0c;什么时候用增量式PID&#xff1a; 在设计PID控制器时&#xff0c;应该考虑下什么时候用增量式&#xff0c;什么时候用位置式。需要看控制器输出u与控制目标之间的关系…

大数据(六):Pandas的基础应用详解(三)

专栏介绍 结合自身经验和内部资料总结的Python教程&#xff0c;每天3-5章&#xff0c;最短1个月就能全方位的完成Python的学习并进行实战开发&#xff0c;学完了定能成为大佬&#xff01;加油吧&#xff01;卷起来&#xff01; 全部文章请访问专栏&#xff1a;《Python全栈教…

Qt应用开发(基础篇)——进度对话框 QProgressDialog

一、前言 QProgressDialog类继承于QDialog&#xff0c;是Qt设计用来反馈进度的对话框。 对话框QDialog QProgressDialog提供了一个进度条&#xff0c;表示当前程序的某操作的执行进度&#xff0c;让用户知道操作依旧在激活状态&#xff0c;配合按钮&#xff0c;用户就可以随时终…

探索FedLCM——解锁FATE部署管理的实用功能

FedLCM &#xff08;Federation Lifecycle Manager&#xff0c;联邦生命周期管理器&#xff09;是 VMware 在 2022 年贡献到 FATE 社区的开源项目&#xff0c;通过 FedLCM 的部署管理服务和任务管理服务&#xff0c;我们可以用图形化的方式完成包括 FATE 集群的云原生部署、联邦…

Acwing791. 高精度加法

Acwing791. 高精度加法 题目描述代码展示 题目描述 代码展示 #include <iostream> #include <vector>using namespace std;vector<int> add(vector<int> &A, vector<int> &B) {if (A.size() < B.size()) return add(B, A);vector<…

扎根嵌入式行业需要什么学历文凭?

在嵌入式行业&#xff0c;学历并不是唯一关键。我本人拥有电子工程学士学位&#xff0c;但嵌入式行业更看重实际技能和经验。视频后方有免费的嵌入式学习资料&#xff0c;入门和进阶内容都涵盖其中。嵌入式行业一般接纳各种学历&#xff0c;从专科到本科到研究生&#xff0c;甚…

C++——vector:resize与reserve的区别,验证写入4GB大数据时相比原生操作的效率提升

resize和reserve的区别 reserve&#xff1a;预留空间&#xff0c;但不实例化元素对象。所以在没有添加新的对象之前&#xff0c;不能引用容器内的元素。而要通过调用push_back或者insert。 resize&#xff1a;改变容器元素的数量&#xff0c;且会实例化对象&#xff08;指定或…

用Python轻松实现Word文档到PDF的批量转换

Word文件&#xff08;Doc、Docx&#xff09;和PDF文件都是使用广泛的文档格式。其中&#xff0c;Word文档格式在编辑内容时优势明显&#xff0c;能够满足各种编辑需求。但想要保证文档的完整和格式的一致&#xff0c;以及使文档有良好的跨平台兼容性&#xff0c;PDF格式则是更好…

点对点传输技术可实现更大的文件传输

互联网的早期主要是基于点对点的系统。这是一个由学者和研究人员构成的网络&#xff0c;连接到这个网络的计算机都是同等的&#xff0c;因为每台计算机都提供了尽可能多的信息。在点对点连接的初期&#xff0c;不需要考虑快速传输大型文件。 随着互联网的发展&#xff0c;客户端…

strlen是碰到第一个字符串结束符‘\0‘就结束,所以长度不包括‘\0‘。定义数组时会默认在字符串后面添加一个‘\0‘

1 字符串数组 定义字符串数组的时候&#xff0c;会默认在字符串后面添加一个’\0’ 2 strlen 在碰到第一个字符串结束符’\0’的时候就结束计算&#xff0c;所以strlen的长度不包括’\0’。 3 实例 #include <stdio.h> #include <string.h>int main(void) {in…

Windows下将nginx等可执行文件添加为服务

Windows下将nginx等可执行文件添加为服务 为什么将可执行文件添加为服务&#xff1f;将可执行文件添加为服务的步骤步骤 1&#xff1a;下载和安装 Nginx步骤 2&#xff1a;添加为服务方法一&#xff1a;使用 Windows 自带的 sc 命令方法二&#xff1a;使用 NSSM&#xff08;Non…

[Go版]算法通关村第十六关青铜——原来滑动窗口如此简单

目录 应用了滑动窗口思想的示例滑动窗口的基本思想两个入门题题目&#xff1a;子数组最大平均数思路分析&#xff1a;计算滑动窗口值总和为 right -left复杂度&#xff1a;时间复杂度 O ( n ) O(n) O(n)、空间复杂度 O ( 1 ) O(1) O(1)Go代码 题目&#xff1a;最长连续递增序列…

unity 物体至视图中心以及新对象创建位置

如果游戏对象不在视野中心或在视野之外&#xff0c; 一种方法是双击Hierarchy中的对象名称 另一种是选中后按F 新建物体时对象的位置不是在坐标原点&#xff0c;而是在当前屏幕的中心

【Android】SDK安装及配置

一、下载SDK Tools https://www.androiddevtools.cn 以windows10系统为例&#xff0c;下载压缩版直接解压即可。 二、安装SDK Tools 解压后双击运行SDK Manager.exe 一般根据默认推荐安装即可。 如果无法打开SDK Manager&#xff0c;可以参考&#xff1a;https://blog.cs…

【Ubuntu】解决ubuntu虚拟机和物理机之间复制粘贴问题(无需桌面工具)

解决Ubuntu虚拟机和物理机之间复制粘贴问题 第一步 先删除原来的vmware tools&#xff08;如果有的话&#xff09; sudo apt-get autoremove open-vm-tools第二步 安装软件包&#xff0c;一般都是用的desktop版本&#xff08;如果是server换一下&#xff09; sudo apt-get …

视频云存储/安防监控视频/智能分析网关V3裸土未覆盖/苫盖算法功能详解

随着经济的发展和建筑工地的增多&#xff0c;对于土堆的裸露情况实时监测和管理变得尤为重要。为了解决这一问题&#xff0c;TSINGSEEE青犀AI智能分析网关V3的裸土未苫盖算法就能很好地解决。 AI算法模型可以实时识别路面/建筑工地中的土堆是否裸露&#xff0c;将工地、道路等…

Erasure-Code(纠删码) 最佳实践

Erasure-Code(纠删码) 最佳实践 1. 纠删码原理 这个星球产生的数据越来越庞大&#xff0c;差不多2010年开始各大互联网公司大都上线了系统以应对数据膨胀带来的成本增长。Erasure-Code&#xff08;纠删码&#xff09;技术应用其中。典型如Google 新一代分布式存储系统colossu…

一种借助MYSQL递归CTE生成所有的组合情况的实现方法

需求说明 有如下表和数据&#xff1a; Nname1户口2查询机构数过多3危险驾驶4多头用信 需要输出name里的所有组合情况&#xff0c;即单个值&#xff0c;两两组合&#xff0c;三个组合、四个组合。结果为2的n次方-1中情况&#xff0c;这里是15。 预期结果为&#xff1a; Com…

DP358 11MHz 增益带宽轨到轨输出运算放大器

DP358、DP321、DP323、DP324是一款低噪声、低压、低 功耗轨到轨输出运放大器&#xff0c;该系列放大器的增益带宽为 11MHz,压摆率为 8.5V/uS,其中DP323 在掉电工作模式下待机电流小于1uA。该系列放大器可以广泛应用于各种电子产品领域。 主要特性: 轨到轨最大输入输出失调电压…

国标GB28181视频监控EasyGBS国标视频平台分享链接不生效的问题解决方案

EasyGBS平台可提供流媒体接入、处理、转发等服务&#xff0c;支持内网、公网的监控设备通过国标GB/T28181协议进行视频监控直播。平台可拓展性强&#xff0c;部署灵活&#xff0c;可实现的视频能力有&#xff1a;实时直播、视频录像、语音对讲、云存储、检索及回放、告警、级联…