Android 数据库Room的开发使用详解

一.简介:
Room 在SQLite上提供了一个抽象层,以便在充分利用SQLite的强大功能的同时,能够流畅地访问数据库。
Room包含3个主要组件:
数据库:包含数据库持有者,并作为应用已保留持久性关系型数据的底层连接的主要接入点。
@Database注释
1.是扩展RoomDatabase的抽象类。
2.在注释中添加与数据库关联的实体表。
3.包含具有0个参数且返回使用@Dao 注释的类的抽象方法。
在运行时,您可以通过调用Room.databaseBuilder()或Room.inMemoryDatabaseBuilder()获取Database的实例。
@Entity:表示数据库中的表
@Dao:包含用于访问数据库的方法
image.png
二:依赖Room数据库
1.在App模块下bulid.Gradle 添加项目的依赖

//添加Room依赖
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'

image.png
三:创建一个实体类Entity

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)//主键是否自动增长,默认为false
 private int id;
 private String name;
 private int age;
 public int getId() {
        return id;
 }
    public void setId(int id) {
        this.id = id;
 }
    public String getName() {
        return name;
 }
    public void setName(String name) {
        this.name = name;
 }
    public int getAge() {
        return age;
 }
    public void setAge(int age) {
        this.age = age;
 }
}

image.png
1.主键:每一个实体必须定义至少一个字段作为主键。

 1.可以在实体中@PrimaryKey(autoGenerate = true)注解,同时你也可以使用autoGenerate属性,可以通过Room来自动分配ID
 2.也可以通过@Entity@Entity(primaryKeys = {"id","name"})如果有组合主键

2.通常Room会使用类名作为数据库的表名,如果你希望自定义表名在@Entity(tableName = "my_user"),注意:SQLite中,表名是不区分大小写的
3.Room用变量名称作为数据库表的字段名称,如果你希望字段名称和变量名称不一样,在变量出添加

public class User {
 @ColumnInfo(name = "first_name")
    private String name;
    }

4.索引和唯一性
根据你操作数据的方式你可能需要通过索引来提高查询数据库的速度,通过@Entity添加indices属性,有些字段设置唯一性,可以通过@Index注解下设置unique为true

@Entity(indices = {@Index(value = "name",unique = true)})
public class User {
private String name;
}

5.定义对象之间的关系
由于SQLite是关系型数据库,你可以指定对象之前的关系,Room是明确禁止直接使用关系,但Room仍然允许你在实体之间定义外键。
例如:如果有另一个实体叫做Book,你可以在User实体下使用@ForeignKey注解定义他们之间的关系。

@Entity(
        foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id", 
        childColumns = "user_id")//定义外键
)
public class Book {
    @PrimaryKey//定义主键
 public int bookId;
 public String title;
 @ColumnInfo(name = "user_id")//定义数据库表中的字段名
    public int userId;
 public int getUserId() {
        return userId;
 }
    public void setUserId(int userId) {
        this.userId = userId;
 }
    public int getBookId() {
        return bookId;
 }
    public void setBookId(int bookId) {
        this.bookId = bookId;
 }
    public String getTitle() {
        return title;
 }
    public void setTitle(String title) {
        this.title = title;
 }
}

6.创建嵌套对象
你可以使用@Embedded批注来表示要分解到表中子字段的对象
例如:我们的User类可以包含Address类型的字段,它表示名为street,city,state和postCode的字段的组合。要将组合列分别存储在表中,请在User类中包含使用@Embedded注释的Address字段

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code")
    public int postCode;
}

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;

    @Embedded
    public Address address;
}

故这个表示User对象的表包含具有以下名称的列:id,firstName,street,state,city和post_code。
@Embedded(prefix = "address_")如果实体具有多个相同类型的嵌入字段,则可以通过设置prefix属性使得每一个列保持唯一,把address_嵌入到列名的开头
7.忽略成员变量
如果你不想保留某些成员变量,可以使用@Ignore注解

@Ignore//指示Room需要忽略的字段
private int age;

四:创建一个Dao接口
Dao包含用于访问数据库的方法,创建一个操作实体类用@Dao进行注解
@Insert插入语句注释
@Delete删除语句注释
@Update()更新语句注释
@Query("SELECT * FROM user WHERE first_name=:name")查询语句

@Dao
public interface UserDao {
    /*插入数据User*/
 @Insert
 void insert(User user);
 @Query("SELECT * FROM  user")//从user表中查询所有,user是User实体类默认在Room中创建的表,也可以通过@Entity(tableName = "my_user"),指定表名,故这个表名就变成my_user
    List<User> getAllUsers();
 @Query("SELECT * FROM user WHERE first_name=:name")//设置筛选条件name,来查询这个first_name是表名first_name字段,通过@ColumnInfo(name = "first_name")来设置表字段名
    List<User> getUsersByName(String name);
}

五:创建一个数据库持有者类

@Database(entities = {User.class},version = 6,exportSchema = false)
public abstract class UserDatabase extends RoomDatabase {
    private static final String DB_NAME="UserDatabase.db";
 private static volatile UserDatabase instance;//创建单例
 public static synchronized UserDatabase getInstance(Context context){
        if (instance==null){
            instance=create(context);
 }
        return instance;
 }
 /**
 * 创建数据库*/
    private static UserDatabase create(Context context) {
        return Room.databaseBuilder(context,UserDatabase.class,DB_NAME)
        .allowMainThreadQueries()//允许在主线程操作数据库,一般不推荐;设置这个后主线程调用增删改查不会报错,否则会报错
        .fallbackToDestructiveMigration()//该方法能在升级异常重新创建数据库,但所有的数据都会丢失
        .addMigrations(new Migration(1,4) {
    @Override
 public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("alter table user add price TEXT");//添加一个字段 price升级数据库版本到4
 }
})
        .build();
 }
 
 public  abstract UserDao getUserDao();//这个是必要的,创建DAO的抽象类
}

注意:
1、编译时会检查SQL语句是否正确
2、不要在主线程中进行数据库操作
3、RoomDatabase最好使用单例模式
如果不设置数据库在主线程操作的话就会报错,错误提示为
image.png
故需要使用数据库最好在new Thread().start()子线程中使用,或者Handler 或者AsyncTask或者RXJava异步实现。
Room数据库升级

//第一步修改版本号为2,要升级的版本
@Database(entities = {User.class},version = 2,exportSchema = false)
//第二步,添加addMigrations()添加数据库升级
 Room.databaseBuilder(context,UserDatabase.class,DB_NAME)
        .addMigrations(new Migration(1,2) {
            @Override
 public void migrate(@NonNull SupportSQLiteDatabase database) {
                database.execSQL("alter table user add go TEXT");//在user 表中添加一个字段go 类型为TEXT
 Log.d("aa",database.getVersion()+"");
 }
        })
        .build();
//第三步在Entity实体类User中添加属性
private String go;
public String getGo() {
    return go;
}
public void setGo(String go) {
    this.go = go;
}
//这样数据库版本就升级到了2,就可以使用了
        

六:Room数据库使用
通过开辟子线程插入一条数据,也可以结合RXJava和Handler和AsyncTask等异步实现

User user=new User();
user.setAge(2223);
user.setName("eees");
user.setGo("wogo");
new Thread(new Runnable() {
    @Override
 public void run() {
        UserDatabase.getInstance(NineActivity.this).getUserDao().insert(user);
 Log.d("TAG","插入一条数据");
 }
}).start();

END:不负青春,不负韶华;不负梦想,不负未来。


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里