花瓣小跑 发表于 2024-10-20 09:54:15

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

引言

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

Room是一个长期化库,它提供了一个抽象层,用于在SQLite数据库中存储和查询数据。它通过注解处置惩罚器和编译时查抄,确保数据库操纵的类型安全,淘汰了运行时错误的可能性。
Entity(实体):



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



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



[*]Database 是一个抽象类,它定义了整个数据库的结构,包括所有的 Dao。
[*]它使用 @Database 注解来标记,并且可以定义版本号和包含的实体。
[*]你可以使用 Room.databaseBuilder() 方法来创建数据库实例。
[*]通常,一个应用中只有一个数据库类,它包含了所有的 Dao。
https://i-blog.csdnimg.cn/direct/1aba58b96e04406cb6d620bd2bb0c0c0.png#pic_center
示例

只说概念可能会觉得抽象照旧没有明白,接下来就通过一个示例来看看吧!
在使用room之前一定不要忘记它是要添加依赖的
implementation("androidx.room:room-runtime:2.4.3")
annotationProcessor("androidx.room:room-compiler:2.4.3")
我们就设置一个学生示例吧,举行学生信息的增删改查操纵,我们将学生的信息展示在页面上,设置增删改查操纵的按钮,这部分的代码就不做说明了,你一定非常熟悉了。

[*]定义Entity:我们要存放的是学生信息,先创建一个学生类:
@Entity(tableName = "student")
public class student {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    int id;
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    String name;
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    int age;
    public student(int id, String name, int age) {
      this.id = id;
      this.name = name;
      this.age = age;
    }
    @Ignore //告诉Room不要用这个构造方法,这是我们所用的方法
    public student(String name, int age) {
      this.name = name;
      this.age = age;
    }
    @Ignore
    public student(int id) {
      this.id = id;
    }
    public int getAge() {
      return age;
    }
}
里面的方法大家已经写了许多遍了,接下来就给大家表明里面的注解:


[*]@Entity(tableName = "student"):

[*]这个注解标记了这个类是一个数据库表的映射。tableName属性指定了数据库中表的名称,这里是"student"。

[*]@PrimaryKey(autoGenerate = true):

[*]这个注解标记了一个字段作为表的主键。autoGenerate = true表示主键值是自动天生的,通常是自增的。

[*]@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER):

[*]这个注解提供了字段的额外信息。name属性指定了数据库中列的名称,这里是"id"。typeAffinity属性指定了列的数据类型,这里是ColumnInfo.INTEGER,表示这个字段在数据库中是整型。

   
[*]ColumnInfo.INTEGER:表示列的数据类型为整型。
[*]ColumnInfo.TEXT:表示列的数据类型为文本。
[*]ColumnInfo.REAL:表示列的数据类型为浮点数。
[*]ColumnInfo.BLOB:表示列的数据类型为二进制数据(比方图片或文件)。
[*]ColumnInfo.FLOAT:表示列的数据类型为浮点数,与REAL类似,但更明白表示为浮点数。
[*]ColumnInfo.LONG:表示列的数据类型为长整型。
[*]ColumnInfo.SHORT:表示列的数据类型为短整型。
[*]ColumnInfo.BOOLEAN:表示列的数据类型为布尔值。


[*]@Ignore:

[*]这个注解用于告诉Room忽略接下来的构造函数,不将其作为数据库操纵的一部分。这通常用于那些仅用于应用逻辑,而不是数据库操纵的构造函数。


[*]定义DAO:创建一个接口来定义数据访问对象(DAO),使用@Dao注解标记。在这个接口中,定义方法来执行数据库操纵,如插入、查询、更新和删除。
@Dao
public interface StudentDao {
    @Insert
    void insertStudent(student... students);
    @Delete
    void deleteStudent(student... students);
    @Update
    void updateStudent(student... students);
    @Query("SELECT * FROM student")
    List<student> getAllStudent();
    @Query("SELECT * FROM student WHERE id = :id")
    List<student> getStudentById(int id);
}
都是接口当中定义的方法名,这里的注解大家也都见过,在学习SQLite的时间对这四个方法用了许多次了,这里是直接使用注解,来标记这个方法。看末了一个注解就是为了告诉它根据表的id来寻找你所要查找的学生信息。

[*]定义Database:创建一个抽象类来继续RoomDatabase,并使用@Database注解标记。在这个类中,定义数据库的版本和包含的实体和DAO。
@Database(entities = {student.class}, version = 1, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {
    private static MyDataBase mInstance;
    private static final String DATABASE_NAME = "my_db.db";
    public static synchronized MyDataBase getInstance(Context context) {
      if (mInstance == null) {
            mInstance = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, DATABASE_NAME).build();
      }
      return mInstance;
    }
    public abstract StudentDao getStudentDao(); //Room会帮我们自动实现
}


[*]@Database(entities = {student.class}, version = 1, exportSchema = false):

[*]@Database注解用于定义数据库的设置。它告诉Room这个数据库包含哪些实体(entities),数据库的版本(version),以及其他一些设置项。
[*]entities = {student.class}:指定了这个数据库包含的实体类。在这个例子中,它包含student实体。
[*]version = 1:指定了数据库的版本号。当数据库结构发生变革时(比方添加、删除或修改实体),必要增加这个版本号。
[*]exportSchema = false:指定是否答应Room导出数据库的schema文件。如果设置为true,Room会在编译时天生一个包含数据库schema的文件,这有助于调试和测试。在这个例子中,它被设置为false,意味着不导出schema文件。

[*]public abstract class MyDataBase extends RoomDatabase:

[*]定义了一个名为MyDataBase的抽象类,它继续自RoomDatabase。这个类将作为数据库的顶层接口,用于创建和管理数据库实例。

[*]private static MyDataBase mInstance;:

[*]定义了一个静态的MyDataBase实例,用于实现数据库的单例模式。这样可以确保整个应用步伐中只有一个数据库实例。

[*]private static final String DATABASE_NAME = "my_db.db";:

[*]定义了一个常量,指定了数据库文件的名称。在这个例子中,数据库文件将被命名为my_db.db。

[*]public static synchronized MyDataBase getInstance(Context context):

[*]定义了一个静态方法,用于获取数据库的单例实例。这个方法使用了synchronized关键字来确保线程安全,避免在多线程环境下创建多个数据库实例。
[*]context.getApplicationContext():获取应用步伐级别的上下文,用于创建数据库实例。

[*]if (mInstance == null) { mInstance = Room.databaseBuilder(...).build(); }:

[*]如果mInstance为null,则使用Room.databaseBuilder()方法创建一个新的数据库实例。这个方法链式调用了多个设置方法,包括指定数据库类、数据库名称、以及数据库构建的其他设置。

[*]public abstract StudentDao getStudentDao();:

[*]定义了一个抽象方法,用于获取StudentDao的实例。Room在编译时会为这个方法天生实现代码,这样你就可以在应用步伐中通过调用getStudentDao()来获取StudentDao的实例,进而执行数据库操纵。


[*]接下来就可以为按钮注册点击事件,运行步伐看数据的更改了
public class MainActivity extends AppCompatActivity {
    StudentDao studentDao;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      //......获取按钮,页面的滚动控件
      buttonAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student("Jack", 20);
                student s2 = new student("Rose", 30);
                new InsertStudentTask(studentDao).execute(s1, s2);
            }
      });
      buttonResearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new GetAllStudentTask(studentDao).execute();
            }
      });
      buttonUpdata.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student(3,"Tason", 21);
                new UpdataStudentTask(studentDao).execute(s1);
            }
      });
      buttonDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                student s1 = new student(2);
                new DeleteStudentTask(studentDao).execute(s1);
            }
      });
    }

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

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

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

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

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

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

      @Override
      protected void onPostExecute(List<student> students) {
            super.onPostExecute(students);
            studentRecyclerViewAdapter.setstudent(students);
            studentRecyclerViewAdapter.notifyDataSetChanged();
      }
    }
}
注意我们不在主线程举行数据库的相干操纵,
接下来就运行一下:
https://i-blog.csdnimg.cn/direct/be2df67ba80d437390b9b737d2289f50.png#pic_center
当我们举行增加操纵,页面没有发生变革,我们点击查询操纵,我们在上面的代码当中知道没当按下查询操纵就会获取所有的学生信息,并将其展示在页面的滚动控件当中:
https://i-blog.csdnimg.cn/direct/8686b8c450d24416b0f8d7b55dd41286.png#pic_center
再看看删除和修改操纵吧,上面的代码我们将2号学生删除,并修改3号学生的信息,操纵后按下查询按钮:
https://i-blog.csdnimg.cn/direct/38f0bb47b377439abd517e7ea9ff0b16.png#pic_center
优化

我们看到数据库的信息已经修改了。每次修改都要举行查询才能看到更新的信息就很麻烦,而且你也不知道到底有没有跟新就要举行查询操纵,在之前我们学习了LiveData每当数据变革,就会自动告诉View,使其自动更新,那不就大大优化了步伐吗
https://i-blog.csdnimg.cn/direct/609901bcfa534418a2a8a08b8ebe8626.png#pic_center
官方给出的架构指南,Model当中使用Room访问SQLite的内容。接下来就看看如何使用吧!
1、2、3步是一样的,这里就不多说了,只是对于数据库的操纵有轻微的修改,第一次学习写的就不修改了,大家看看吧
@Dao
public interface StudentDao {
    @Insert
    void insertStudent(student... students);
    @Delete
    void deleteStudent(student... students);
    @Update
    void updateStudent(student... students);
    @Query("DELETE FROM student")
    void deleteAllAtudent();
    @Query("SELECT * FROM student")
    LiveData<List<student>> getAllStudentsLive();
}

[*]创建Repository:Repository作为数据层的抽象,封装了数据来源,它可以是一个类,包含了一系列方法来执行数据库操纵。这些方法通常会调用DAO中定义的操纵,并将结果包装成LiveData或Flow对象,以便ViewModel可以观察数据变革。
public class StudentRepository {
    private StudentDao studentDao;

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

      @Override
      protected Void doInBackground(student... students) {
            studentDao.updateStudent(students);
            return null;
      }
    }
    //对数据进行删除
    public void deleteStudent(student... students) {
      new DeleteStudentTask(studentDao).execute(students);
    }
    class DeleteStudentTask extends AsyncTask<student, Void, Void> {
      private StudentDao studentDao;
      public DeleteStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
      }
      @Override
      protected Void doInBackground(student... students) {
            studentDao.deleteStudent(students);
            return null;
      }
    }
    //对数据进行查找
    public LiveData<List<student>> getAllStudentsLive() {
      return studentDao.getAllStudentsLive();
    }
    //将所有数据都删除
    public void deleteAllStudent() {
      new DeleteAllStudentTask(studentDao).execute();
    }
    class DeleteAllStudentTask extends AsyncTask<Void, Void, Void> {
      private StudentDao studentDao;
      public DeleteAllStudentTask(StudentDao studentDao) {
            this.studentDao = studentDao;
      }
      @Override
      protected Void doInBackground(Void... voids) {
            studentDao.deleteAllAtudent();
            return null;
      }
    }
}


[*]构造函数:

[*]StudentRepository的构造函数吸收一个Context对象,用它来获取数据库的实例。MyDataBase.getInstance(context)是一个单例模式的数据库实例获取方法,确保整个应用中只有一个数据库实例。

[*]异步使命(AsyncTask):

[*]InsertStudentTask、UpdataStudentTask、DeleteStudentTask和DeleteAllStudentTask是内部类,它们继续自AsyncTask。这些类用于在后台线程上执行数据库操纵,以避免阻塞主线程。
[*]AsyncTask已经被标记为过期,推荐使用java.util.concurrent包中的类、Kotlin Coroutines或者其他现代的并发办理方案。

[*]doInBackground方法:

[*]在AsyncTask的doInBackground方法中执行实际的数据库操纵。这个方法在后台线程上运行,吸收的参数是传递给execute方法的参数。

[*]LiveData:

[*]getAllStudentsLive方法返回一个LiveData对象,它包含了数据库中所有学生数据的列表。LiveData是一个可观察的数据存储器,它具有生命周期感知能力,只会在观察者处于活泼状态时发送数据更新。


[*]使用ViewModel:ViewModel负责管理UI相干的数据,它可以调用Repository中的方法来获取数据,并根据数据变革更新UI。ViewModel使用LiveData或Flow作为数据的载体,这样当数据发生变革时,UI可以自动更新。
public class StudentViewModel extends AndroidViewModel {
    //当要使用上下文的时候使用AndroidViewModel
    private StudentRepository repository;
    public StudentViewModel(@NonNull Application application) {
      super(application);
      this.repository = new StudentRepository(application);
    }
    public void insertStudent(student... students) {
      repository.insertStudent(students);
    }
    public void deleteStudent(student... students) {
      repository.deleteStudent(students);
    }
    public void deleteAllStudent() {
      repository.deleteAllStudent();
    }
    public void updateStudent(student... students) {
      repository.updateStudent(students);
    }
    public LiveData<List<student>> research() {
      return repository.getAllStudentsLive();
    }
}

[*]观察数据变革:在UI层(如Activity或Fragment),观察ViewModel中的LiveData或Flow对象。当数据发生变革时,UI层会收到通知并更新界面。
studentViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(StudentViewModel.class);
studentViewModel.research().observe(this, new Observer<List<student>>() {
    @Override
    public void onChanged(List<student> students) {
      studentRecyclerViewAdapter.setstudent(students);
      studentRecyclerViewAdapter.notifyDataSetChanged();
    }
}); //为其注册监听,到获取到的数据库内容进行变化,就更新UI
buttonAdd.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      student s1 = new student("Jack", 20);
      student s2 = new student("Rose", 30);
      studentViewModel.insertStudent(s1,s2);
    }
}); //只展示一个按钮的点击事件,其他的你也肯定会写了
接下来就运行步伐看看吧!
我们举行了增加删除修改操纵,页面直接自己更新了,带来了很大的便利
https://i-blog.csdnimg.cn/direct/012f65e5d8d347bcb03601ded9029fdf.png#pic_center
按下清空页面就没有了,数据库就被清空了!
本篇内容介绍了数据库的基本操纵,感谢你的阅读!
文章到这里就竣事了!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Android】Room—数据库的基本操纵