Android之Room学习

Android之Room学习

前言

学习Room数据库,顺便复习了自己的数据库知识。

Room在SQLite上提供一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。

这是开发指南上给出Room的概念,即简化了SQLite的操作,减少代码量。

1. Room组件内容

Room主要由三部分组成:

  • 数据库(Database): 存储数据表的容器

    @Database(entity={User.class}, version = 1)
    public abstract class AppDatabase extends RoomDatabase {
        ......
    }
    

    上面标识用于指定自定义数据库类(抽象类)包括数据库中的表和数据库版本版本

    自定义数据库类继承RoomDatabase,用来连接Entity和DAO,配置一些数据库功能。

    由于实例化一个 RoomDatabase 对象的开销是比较大的,所以 DataBase 的使用需要遵循单例模式,只在全局创建一个实例即可。

  • 实体(Entity):数据库中的表,用于存储结构化数据

    • 在标识@Entity时指定表名、索引、外键、组合主键等
      - @PrimaryKey指定主键等
      - @ColumnInfo指定列信息等
  • 访问方法(DAO):访问数据库的方法

    DAO即为对数据进行操作,增删改查

    @Dao
    public interface UserDao {
        @Query("SELECT * FROM users")
        List<User> getAll();
        @Update
        void update(User... users);
    
        @Insert
        void insertAll(User... users);
    
        @Delete
        void deleteByID(User user);
    }
    

    增删改查四大操作,只有查询操作是相对丰富的。其余的基本根据传入的结构进行相应的操作,个人认为主要是依靠主键进行操作,同时@Query中的语句同样可以完成其他三种操作。

    Room组件之间的关系

2. Entity属性

2.1 主键

public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    ......
}
@Entity(tableName = "users")
public class User {
public class User {
    public int id;
    ......
}

2.2 索引和唯一约束

索引的写法

@Entity(tableName = "users", indices = { @Index(name = "name",value = {"first_name","last_name"})})
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    .......
}

UNIQUE写法

@Entity(tableName = "users", indices = { @Index(name = "name",value = {"first_name","last_name"}, unique = true)})
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    .......
}

2.3 主键与唯一索引的区别

是否保证唯一性是否允许为空一个表中的数量是否允许组合
主键至多1个是,但不推荐
唯一索引可以有多个是,但不推荐

2.4 外键

外键,从表外键字段到主表的某一字段,一般都是外键字段是主表的主键字段。

@Entity(tableName = "Userinfo", foreignKeys = @ForeignKey(entity=User.class, parentColumns = "id", childColumns = "user_id"))
public class Userinfo {
    @PrimaryKey
    public int user_id;

    @PrimaryKey(autoGenerate = true)
    public int id;

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

    @ColumnInfo(name = "age")
    public int age;
}

3. 数据库升级

升级数据库需要在数据库实例化时添加一个.addMigrations(Migration)

   static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("Create table userInfo(user_id integer primary key," +
                                                    "sex String," +
                                                    "age integer," +
                                                    "id integer," +
                    "constraint FK_ID foreign key(user_id) References user(id))");
            Log.d("appDatabase", "升级成功");

        }
    };
    public static AppDatabase create(Context context){
        //allowMainThreadQueries()方法,所以加上这行代码是为了防止 【Cannot access database on the main thread since it may potentially lock the UI for a long period of time.】 的报错。正式使用时,请务必去掉这行代码,因为它会让所有耗时操作运行在主线程!
        return Room.databaseBuilder(context, AppDatabase.class, "app-db").allowMainThreadQueries().addMigrations(MIGRATION_1_2).build();
    }

4. ROOM对象关系

ROOM不支持对象之间的引用,因此当我们所希望的某些成员是一个封装的紧密内部实体,而非基础型数据时,就引入了嵌套的概念.穿件嵌套主要通过依赖@Embedded注释来实现.

4.1 创建嵌套对象

定义

如果某一Entity的某一字段是一个紧密整体,即非基础类型数据时,可以使用嵌套的方法对该紧密整体进行拆解到Entity的字段中.

实现

使用@Embedded将该紧密整体(类)中数据分解成实体的列字段.

比如:用户User的数据字段有住址Address型,使用嵌套将Address的各个数据单独存储到User的列字段.如下:

单独声明一个住址类Address(注意不注释为Entity

public class Address {
    public String street;
    public String state;
    public String city;
    @PrimaryKey
    @ColumnInfo(name = "post_code")
    public String postCode;
}

User中嵌套住址类Address

@Entity(tableName = "user")
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;

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

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

    @Embedded
    public Address address;

查询User时,得到的字段有:

idfirst_namelast_namestreetstatecitypostCode

4.2 一对一关系

定义

一对一关系指的是父类Entity与子类Entity一一对应,可以类比于一次函数,一个 x 对应一个 y ,同样一个 y 对应一个 x .

实现

在父Entity中进行@Embedded声明子Entity类,并对两者进行一对一关系建立.

如音乐软件中用户User的歌曲库Libary只能有一个,且每一个歌曲库对应着自己的

  1. 分别注释实体
@Entity(tableName = "users")
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;

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

    @ColumnInfo(name = "last_name")
    public String lastName;
}
@Entity(tableName = "Libary")
public class Libary {
    @PrimaryKey
    public long libaryID;

    @ColumnInfo(name = "user_id")
    public long userID;
}
  1. 定义二者之间的一对一关系类UserAndLibary.

    使用@Embedded来指定父Entity.

    使用@Relation来指定对应关系,parentColumn为父Entity的主键,entityColumn为"引用"父Entity主键的子Entity列字段.

public class UserAndLibary {
    @Embedded public User user;
    @Relation(
            parentColumn = "id",
            entityColumn = "user_id"
    )
    public Libary libary;
}
  1. DAO中添加方法返回关系类实例

    由于该方法需要 Room 运行两次查询,因此应向该方法添加 @Transaction注释,以确保整个操作以原子方式执行。

4.3 一对多关系

定义

一对多关系指的是父类Entity的一条实例与子类Entity中n条实例(n可以为任意非负整数)对应,可以类比于高次函数,一个 x 对应一个 y ,但是一个 y 可以对应 n 个 x .

实现

在父Entity中进行@Embedded声明子Entity类,并对两者进行一对多关系建立.

如音乐软件中用户User的播放列表MusicList可以有多个,且每一个播放列表对应着自己的用户

  1. 分别注释实体

    @Entity(tableName = "users")
    public class User {
        @PrimaryKey(autoGenerate = true)
        public int id;
    
        @ColumnInfo(name = "first_name")
        public String firstName;
    
        @ColumnInfo(name = "last_name")
        public String lastName;
    }
    
    @Entity(tableName = "musicList")
    public class MusicList {
        @PrimaryKey
        public long id;
        @ColumnInfo(name = "list_name")
        public String listName;
        @ColumnInfo(name = "user_id")
        public long userID;
    }
    
  2. 定义二者之间的一对多关系类UserWithMusicLists.

    使用@Embedded来指定父Entity.

    使用@Relation来指定对应关系,parentColumn为父Entity的主键,entityColumn为"引用"父Entity主键的子Entity列字段,同时与一对一不同的是,此处子Entity改为列表型数据类型.

    public class UserWithMusicLists {
        @Embedded
        public User user;
        @Relation(
                parentColumn = "id",
                entityColumn = "user_id"
        )
        public List<MusicList> musicLists;
    }
    
  3. DAO中添加方法返回关系类实例

    与一对一相似,不再赘述.

    @Transaction
    @Query("SELECT * FROM users")
    List<UserWithMusicLists> getAllUsersWithMusicLists();
    

4.4 多对多关系

定义

多对多关系指的是父类Entity的一条实例与子类Entity中n条实例(n可以为任意非负整数)对应; 同样,子类Entity的一条实例与父类Entity中n条实例(n可以为任意非负整数)对应.

实现

在父Entity中进行@Embedded声明子Entity类,同样,也在子Entity类中进行@Embedded声明父Entity,并对两者进行多对多关系建立.

如音乐软件中播放列表MusicList中有许多音乐Music,Music可以存放许多播放列表MusicList.

  1. 分别注释实体并关联两个实体

    @Entity(tableName = "users")
    public class User {
        @PrimaryKey(autoGenerate = true)
        public int id;
    
        @ColumnInfo(name = "first_name")
        public String firstName;
    
        @ColumnInfo(name = "last_name")
        public String lastName;
    }
    
    @Entity(tableName = "musicList")
    public class MusicList {
        @PrimaryKey
        public long id;
        
        @ColumnInfo(name = "list_name")
        public String listName;
        
        @ColumnInfo(name = "user_id")
        public long userID;
    }
    
    @Entity(tableName = "musicLists_musics", primaryKeys = {"music_list_id","music_id"})
    public class MusicListAndMusic {
        @ColumnInfo(name = "music_list_id")
        public long musicListId;
    
        @ColumnInfo(name = "music_id")
        public long musicId;
    }
    
  2. 定义二者之间的多对多关系类MusicWithMusicListsMusicListWithMusics.

    使用@Embedded来指定父Entity.

    使用@Relation来指定对应关系,parentColumn为父Entity的主键,entityColumn为"引用"父Entity主键的子Entity列字段,引入associateBy=@Junction(XXX.class)表明多对多关系.

    public class MusicWithMusicLists {
        @Embedded
        public  Music music;
        @Relation(
                parentColumn = "id",
                entityColumn = "id",
                associateBy = @Junction(MusicListAndMusic.class)
        )
        public List<MusicList> musicLists;
    }
    
    
    public class MusicListWithMusics {
        @Embedded public MusicList musicList;
        @Relation(
                parentColumn = "id",
                entityColumn = "id",
                associateBy = @Junction(MusicListAndMusic.class)
        )
        public List<Music> musics;
    }
    
  3. DAO中添加方法返回关系类实例

    与上文类似,不再赘述.

        @Transaction
        @Query("SELECT * FROM musicLists")
        List<MusicListWithMusics> getAllMusicsWithMusicLists();
    
        @Transaction
        @Query("SELECT * FROM musics")
        List<MusicWithMusicLists> getAllMusicListsWithMusics();
    

4.5 嵌套关系

定义

当出现查询多表集合时,想实现表格之间的相互关联就需要使用嵌套关系。

实现

拆分处理,画出关联图,理清嵌套关系,从最底层开始一层一层往上嵌套.

如下图用户和播放列表之间一对多,播放列表和音乐多对多。

在这里插入图片描述

  1. 先处理音乐与播放列表的对应关系

    多对多关系定义重复,因此省略。

  2. 处理用户与带有音乐的列表的关系

    新建关系类UserWithMusicListsWithMusics

    引入entity指定entityColumn来源.

    public class UserWithMusicListsAndMusics {
        @Embedded public User user;
        @Relation(
                parentColumn = "id",
                entity = MusicList.class,
                entityColumn = "user_id"
        )
        public List<MusicListWithMusics> musicLists;
    }
    

4.6 注意

嵌套关系非常影响处理数据的速度,因为回去嵌套在内的表都会被查询,过于复杂.

4.7 思考

学习到最后,才发觉这一部分内容和数据库中联合查询的内容有着很大的相似程度——多表查询

直接使用Query进行联合查询也可以得到相同的结果.

5. ROOM数据库视图

定义

从一个或多个表导出的虚拟的表,表内容由查询定义。具有普通表的结构,但是不实现数据存储。
对视图的修改:单表视图一般用于查询和修改,会改变基本表的数据
多表视图一般用于查询,不会改变基本表的数据。

定位:虚拟表,并没有实际储存,多用于查询。

实现

与一般数据库操作类似。

以上文用户为例,实现如下:

@DatabaseView("SELECT users.id, users.first_name, users.last_name , libary.libaryId  FROM users INNER JOIN libary ON libary.user_id = users.id")
public class UserDetailView {
    public long id;

    public String firstName;

    public String lastName;

    public long libaryId;
}

在数据库方面:

@Database(entities = {User.class, Address.class, Libary.class, Music.class, MusicList.class, MusicListAndMusic.class}, views = {UserDetailView.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
	......
}

总结

至此,ROOM的基本学习结束.

在这一块主要掌握几点

  1. 数据表的生成,主要是Entity的定义
  2. 数据库的生成,主要继承RoomDatabase类,且数据库只需要一个实例,因此需要进行单例模式改造(使用懒汉式加锁双重排查)
  3. 数据表的关联,定义Entity,然后定义关联类(类中@Embedded父类实体+@Relation指定相关字段),具体看上述分析
  4. 数据表的视图,注释@DataView,在数据库上指定即可

其他学习分享系列

数据结构与算法系列

数据结构与算法之哈希表

数据结构与算法之跳跃表

数据结构与算法之字典树

数据结构与算法之2-3树

数据结构与算法之平衡二叉树

数据结构与算法之十大经典排序

数据结构与算法之二分查找三模板

如有兴趣可以关注我的微信公众号,每周带你学一点算法与数据结构。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT 涓涓清泉

感谢打赏,我会更加努力写更好的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值