相关文章推荐
文雅的数据线  ·  split xml file into ...·  1 年前    · 
安静的打火机  ·  Day20:介紹 Docker 的 ...·  1 年前    · 
逃课的茶壶  ·  Envi ROI Tool的Record ...·  1 年前    · 

Android数据持久层直接使用SQLite很麻烦,Google官方推出了Room, Google对Room的定义: The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room是SQLite之上的一个抽象层,通过Room层来摆脱,繁琐的 数据库 操作,包括创建、更新数据库、表以及进行增删查改等。

Room数据库结构

Room数据库包含三个对象:

1、 Entity: 对应数据库中的表,可以使用Entity注解将一个类变成数据库中的一张表结构
2、 DAO: 全称Database Access Object,定义了对数据库中数据的读写等操作,DAO中可以使用SQL语句来操作数据库
3、 RoomDatabase: 数据库持有类,用于创建数据库或者连接到数据库。内部包含DAO和Entity

上面总体说了Room数据库的三个对象,下面分别介绍三个对象的作用和使用方法。

Entity

Room Database中的Entity表示一张数据表结构,一个Entity实例就是表中的一行,如定义一个Person类的Entity。

@Entity
public class Person{
    @PrimaryKey
    public int id;
    public String firstName;
    public String lastName;

当有两个Person对象在表中时,表中的数据如下:
在这里插入图片描述
重点:

1、一个Entity对象代表数据表中的一行,一个Entity类代表一张数据表
2、Entity中的成员变量都是数据表中的列
3、一个 Java 类定义成Entity只要加上Entity注解就可以了

Entity的使用

1、定义一个Entity

@Entity(tableName = "person_table")
public class Person{
    @PrimaryKey
    public int id;
    public String firstName;
    public String lastName;

如果想给Entity命名可以在@Entity注解的后面加上 (tableName = “person_table”) ,如果不加会使用默认命名,上面的id,firstName, lastName都定义成了public类型,是为了数据库能够有操作这些数据的权限,也可以选择将这些都设置成private类型,然后通过setter和getter来操作。

2、定义主键

每一个Entity至少定义一个主键(primary key),哪怕Entity中只有一个变量也要将这个变量定义为主键,在Room数据库中使用注解 @PrimaryKey 来定义主键,@PrimaryKey 的使用方式有两种一种是在类变量前面加,如果主键比较复杂可以加在@Entity注解的后面。

@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
    public String firstName;
    public String lastName;

3、改变属性列名

和改变表名称tableName一样,可以改变表中的列名称,使用 @ColumnInfo来改变列的名称。如果不改的话默认使用变量名的小写形式。

@Entity(tableName = "person_table")
public class Person {
    @PrimaryKey
    public int id;
    @ColumnInfo(name = "first_name")
    public String firstName;
    @ColumnInfo(name = "last_name")
    public String lastName;

4、使某些变量不生成数据表中的字段

由上面可知当一个类前加了Entity注解后类中的所有成员变量都会生成表中的属性列,如果我们不希望某个变量生成表中的属性列,可以使用注解 @Ignore。

@Entity
public class Person {
    @PrimaryKey
    public int id;
    public String firstName;
    public String lastName;
    @Ignore
    Bitmap picture;

如果有类继承上面的Person类,并且不想picture生成数据表中的列,那么可以ignoredColumns

@Entity(ignoredColumns = "picture")
public class RemotePerson extends Person {
    @PrimaryKey
    public int id;
    public boolean hasVpn;

5、两个数据表关联

SQLite是关系数据库,可以明确两个对象间的关系,大多数的关系数据库都可以指定表之间的关联,但是Room数据库不能够之间指定两个表之间的关系,但是可以通过@ForeignKey注解来进行表的关联。

@Entity(foreignKeys = @ForeignKey(entity = Person.class,
                                  parentColumns = "id",
                                  childColumns = "user_id"))
public class Book {
    @PrimaryKey
    public int bookId;
    public String title;
    @ColumnInfo(name = "user_id")
    public int userId;

6、嵌套Entity

如果定义的Entity类里面有个对象,并且希望定义的Entity中的表列字段包含Entity类对象中的变量,可以在Entity类对象中加@Embedded标注。

public class Address {
    public String street;
    public String state;
    public String city;
    @ColumnInfo(name = "post_code")
    public int postCode;
@Entity
public class Person{
    @PrimaryKey
    public int id;
    public String firstName;
    @Embedded
    public Address address;

这样Person表中的列字段有 id, firstName, street, state, city,和 post_code.

在DAO(data access object)中,可以使用SQL语句进行对数据库的操作并且将这些语句与Java中方法关联调用,编译器会检查SQL语句并且通过注解生成对应的查询语句,例如@Insert。

1、DAO必现是抽象类或者接口
2、所有的查询语句必须在单独的线程里面执行

DAO的使用

可以使用sql中的语句来实现DAO的操作。

1、插入数据

当在方法前使用@Insert注解时,Room数据库会将方法中的所有参数都插入到数据表中。

public interface MyDao { @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(User... users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List<User> friends);

当@Insert 方法只插入一个参数时,插入会返回long数据代表插入的对象在数据表中的行号,当插入一个数组或者集合形式的数据时,会返回long数组或者集合。

2、更新数据

@Update标注的方法会查找表中与所给数据主键一样的数据进行更新。

public interface MyDao { @Update public void updateUsers(User... users);

3、查询数据

@Query 标注的查询方法是使用最多的方法,每个查询方法在编译期间就会被验证是否正确,如果错误就会报错,不会在运行时再提示失败。在

public interface MyDao { @Query("SELECT * FROM user") public User[] loadAllUsers();

如果存在查询语法错误,或者数据库中不存在user表,Room数据库会在编译时提示错误信息。 也可以使用SQL语句实现复杂的查询

public interface MyDao { @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") public User[] loadAllUsersBetweenAges(int minAge, int maxAge); @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") public List<User> findUserWithName(String search);

4、查询列表中的数据并返回子列表

有时候并不需要将查询数据的所有字段都返回,大部分时候只需要返回查询数据的一个子域。下面实现返回查询的一个子域。

public class NameTuple {
    @ColumnInfo(name = "first_name")
    public String firstName;
    @ColumnInfo(name = "last_name")
    public String lastName;

然后查询返回子域中的字段

public interface MyDao { @Query("SELECT first_name, last_name FROM user") public List<NameTuple> loadFullName();

RoomDatabase

上面已经介绍了DAO和Entity,Entity定义表,DAO定义对表中数据进行操作,RoomDatabase包含了DAO,并且提供创建和连接数据库的方法。

创建Room database

创建Room database包括三个步骤:

1、创建继承RoomDatabase的抽象类。
2、在继承的类前使用注解@Database。
3、申明数据库结构的Entity,并且设置数据库的版本号。

@Database(entities = {Word.class}, version = 1)//申明Entity和数据库的版本号
public abstract class WordRoomDatabase 
                      extends RoomDatabase { 
   public abstract WordDao wordDao();//创建DAO的抽象类
   private static WordRoomDatabase INSTANCE;//创建单例
   static WordRoomDatabase getDatabase(final Context context) {
 		 if (INSTANCE == null) {
    			synchronized (WordRoomDatabase.class) {
      			if (INSTANCE == null)    {
       				    INSTANCE = Room.databaseBuilder(
						context.getApplicationContext(),
						WordRoomDatabase.class, "word_database")
						.addCallback(sOnOpenCallback)
						.fallbackToDestructiveMigration()
						.build();                
  return INSTANCE;}
private static RoomDatabase.Callback sOnOpenCallback =
  new RoomDatabase.Callback(){
	@Override
	public void onOpen (@NonNull SupportSQLiteDatabase db){
		super.onOpen(db);
		initializeData();
	}};
 

1、编译时会检查SQL语句是否正确
2、不要在主线程中进行数据库操作
3、RoomDatabase最好使用单例模式

最后上一张 Android 在使用Room数据库时和Room数据库交互的图
在这里插入图片描述
APP从RoomDatabase中获取DAO实例,并且通过DAO中定义的方法来操作数据库中Entity。获取设置entity的数据。

Room数据库升级

SQLite API 做了什么

SQLite数据库处理schema的改变是在database version的帮助下完成的。更准确的说,每当你添加,删除,或者修改表导致schema变化的时候,你都必须增加数据库的版本号并更新SQLiteOpenHelper.onUpgrade方法。当你从旧版本到新版本的时候,是它告诉SQLite该做什么。

它也是app开始和数据库工作是所触发的第一个调用。SQLite将首先处理版本的升级,然后才打开数据库。

Room做了什么

Room以 Migration 类的形式提供可一个简化SQLite迁移的抽象层。Migration提供了从一个版本到另一个版本迁移的时候应该执行的操作。Room使用它自己实现的SQLiteOpenHelper,并在onUpgrade方法中触发你定义的迁移步骤。

这里是第一次获取数据库时将发生的事情:

  1. Room数据库被创建
  2. SQLiteOpenHelper.onUpgrade 方法被调用,然后Room触发迁移
  3. 数据库被打开
schema简单变化的迁移

让我们修改User类,向users表中添加一个新的字段:last_update。在UsersDatabase类中我们需要做如下工作:

1.把版本号增加到 3

@Database(entities = {User.class}, version = 3)
public abstract class UsersDatabase extends RoomDatabase

2. 添加一个version 2到 version 3的Migration

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE users "
                + " ADD COLUMN last_update INTEGER");

3. 把migration 添加到 Room database builder:

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_2_3)
        .build();

当运行app的时候,下面的步骤被执行:

第一步:尝试从version 2更新到version 3

  • 触发migration并修改表,保持用户的数据✅。
  • 更新room_master_table中的identity hash ✅

第二步:尝试打开数据库

  • 现在当前版本的Identity hash 和保存在数据库room_master_table表中的就是相同的了✅。
schema 复杂变化的迁移

SQLite的ALTER TABLE命令非常局限,只支持重命名表以及添加新的字段。比如,把user的id从int类型改成String需要经过如下几步才能完成:

  • 创建一个新的临时表,
  • 把users表中的数据拷贝到临时表中,
  • 丢弃users表
  • 把临时表重命名为users

使用Room,Migration的实现是这样的:

static final Migration MIGRATION_3_4 = new Migration(3, 4) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // Create the new table
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
// Copy the data
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");
// Remove the old table
        database.execSQL("DROP TABLE users");
// Change the table name to the correct one
        database.execSQL("ALTER TABLE users_new RENAME TO users");
多版本迁移

要是你的用户有一个运行版本号为1的app,想升级到版本4呢?目前位置我们定义了这些migrations:version 1 到 2, version 2 到 3, version 3 到 4, 所以Room 会一个接一个的触发所有 migration。

Room可以处理大于1的版本增量:我们可以一次性定义一个从1到4的migration,让迁移的速度更快。

static final Migration MIGRATION_1_4 = new Migration(1, 4) { @Override public void migrate(SupportSQLiteDatabase database) { // Create the new table database.execSQL( "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))"); // Copy the data database.execSQL( "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users"); // Remove the old table database.execSQL("DROP TABLE users"); // Change the table name to the correct one database.execSQL("ALTER TABLE users_new RENAME TO users");

然后,我们只需把它添加到migration列表中:

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
        .build();

以上就是Room的一些基础的运用。

private static AppDatabase create(final Context context) { return Room.databaseBuilder( context, AppDatabase.class,
文章目录Jetpack组件之 Room操作数据库简介主要类依赖定义实体类访问数据创建数据库插入数据删除数据更新数据Tips Jetpack组件之 Room操作数据库 看了google出品的jetpack库,感觉还是很不错的,这里开始我们的Jetpack学习之旅 本文所有源码收录在 --> https://github.com/ddssingsong/AnyTool Room持久...
项目中使用了GreenDao创建数据库,建立了一个库并在其中生成了2个,现在做整体优化,想使用Room创建数据库,必经Room是google官方产品,还是值得尝试一波的。 一:对于Room使用,关注三个点: 1.创建Entity:直接上代码(部分代码省略) 第一个Entity @Entity(tableName = "login_history_table") public class LoginHistory { @PrimaryKey(autoGenerate = true)
使用SQLite API执行数据库迁移总有一种是在拆弹的感觉-仿佛一不小心就会让app在用户手中爆炸。如果你使用Room来处理数据库的操作,那么迁移就非常简单了。 使用Room的时候,如果你改变了数据库的schema但是没有更新version,app将会crash。而如果你更新了version但是没有提供迁移,数据库就会drop掉,用户将丢失数据。 数据库迁移背后的原理 SQLite A...
前言:还是在上篇文章的基础上进行数据库版本的升级和迁移 前面几篇关于Room操作数据库的文章没看的可以先去看一下: Android 使用Room操作SQLite数据库让其变得无比高效和简洁(教程一) Android 使用Room操作SQLite数据库让其变得无比高效和简洁(前一篇文章的完善) Android使用Room操作SQLite数据库让其变得无比高效和简洁(进一步完善用RecyclerView显示数据库中的数据) 紧跟着上面的代码进行数据库版本的升级和迁移 首先在Word类里面添加或者删除某些字段
一、数据库升级 数据库升级在每一个版本上新的时候会经常遇到,一般是字段进行了修改。一般新增一些字段的情况比较多。以下四步是完成数据库升级的基本步骤。 二、代码实现 1、添加Entity字段 原Entity @PrimaryKey var id: Int, var name: String, var gender: Int 添加一个String类型的字段: @Entity data class User( @PrimaryKey(autoGenerate = true) var id: I
详情参照官方文档 def room_version = "2.2.3" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt ins...
1. 在您的项目中添加数据库依赖项,例如SQLite或Room。 2. 创建一个数据库类,该类将继承自RoomDatabase或SQLiteOpenHelper,并包含有关数据库的信息,例如名称和版本号。 3. 创建一个数据访问对象(DAO)接口,该接口将定义用于访问数据库的方法。 4. 在您的应用程序中使用DAO接口中定义的方法来访问数据库。 5. 在您的应用程序中处理数据库操作时,请确保使用异步任务或线程来避免阻塞UI线程。 6. 在您的应用程序中处理数据库操作时,请确保处理异常和错误情况。 希望这些步骤能够帮助您在Android Studio中成功登录数据库