认识Room数据库
1.ROOM数据库是什么
ROOM是Google为Android平台推出的一款数据库持久化库,它基于SQLite之上构建,旨在为开发者提供一种更高效、安全和简洁的方式来管理本地数据库。
ROOM是Android Jetpack的一个架构组件,它通过注解(Annotations)方式简化了数据库表的创建、数据查询和操作。
其作为SQLite之上的抽象层,简化了数据库设置和配置以及交互方面的操作。
2.ROOM数据库原理
ROOM数据库的设计理念和 JavaWeb 的数据库操作在分层结构和实体映射上有相似性。
ROOM数据库的核心由Entity(实体),DAO(数据库访问对象),Database(数据库类)组成
1.Entity
ROOM使用编译时注解处理器,解析实体类并生成对应的表定义和列映射代码,将实体类的每个字段映射到对应表的列,当类被注解标记为实体时,其中的所有字段都将被持久化,除非通过别的注解忽略该字段,例如Ignore。
@Entity(tableName = "user_table")
public class User {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name")
private String name;
@ColumnInfo(name = "user_age")
private int age;
public User(String name, int age) {
this.name = name;
this.age = 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;
}
}
这是一个实体类的示例
其中@Entity(tableName = "user_table")
这个注解代表将以下User类标记为一个实体并且对应映射表名为user_table。
User类中声明了id,name,age三个变量
@PrimaryKey(autoGenerate = true)
表示将id标记为主键,同时autoGenerate=true
的操作表示让数据库自动为id这列赋值,默认为自增主键。
@ColumnInfo(name = "user_age")
表示将age变量对应的列名名称自定义为user_age,同时ColumInfo还支持为字段创建索引的功能。
需要注意的是ROOM设置主键时,必须有一个对应的setter方法
public void setId(int id) {
this.id = id;
}
2.DAO
用于提供和数据库交互的接口,定义数据的增删改查操作。ROOM根据DAO的注解,在编译时解析方法并生成底层的SQL操作,同时验证SQL查询的合法性和字段匹配问题。Room 使用动态代理为 DAO 接口生成实现类,在运行时处理方法调用。
@Dao
public interface UserDao {
@Insert
void insertUser(User user);
@Update
void updateUser(User user);
@Delete
void deleteUser(User user);
@Query("SELECT * FROM user_table ORDER BY user_name ASC")
LiveData<List<User>> getAllUsers();
@Query("SELECT * FROM user_table WHERE id = :userId LIMIT 1")
User getUserById(int userId);
}
这是一个DAO类的示例,@DAO
将以下接口标记为DAO,标记的可以使接口也可以是抽象类。
接口中的注解分别将下面的方法标记为不同的数据库操作。
@Query
注解提供了使用自定义SQL查询语句的方法,其中:userId
作为占位符,当传入userId
后被替换
3.Database
用于定义数据库的整体结构,包含所有的实体和DAO,同时提供一个用于创建和管理数据库的入口。拥有一个RoomDatabase的抽象基类,ROOM的数据库类就继承自它,在通过注解标记后,在编译的时候生成该类的实现。通过version参数来标记数据库的版本。
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
这是一个Database类的示例,public abstract UserDao userDao();
定义了与数据库交互的 DAO 接口。
注解中entities={User,class}
表示该数据库包含一个User实体,也就是文章上面实体示例中的User类,version=1
指定当前数据库版本为1,exportSchema = false
表示禁用将数据库的架构导出为一个JSON文件,默认情况下,exportSchema
为true
,表示将生成一个 schema
文件,存储在项目的 build
目录下,供数据库迁移使用,当需要更新数据库等情况下会使用到它。
Room.databaseBuilder(context, AppDatabase.class, "database-name")
.addMigrations(MIGRATION_1_2)
.build();
这是一个更新数据库架构的方法,同时需要一个更新的重载方法,以下以为表添加一个新列为例。
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database)
{
database.execSQL("ALTER TABLE user_table ADD COLUMN phoneNumber TEXT");
}
};
3.ROOM数据库的使用
-
添加依赖
在build.gradle中添加
// app/build.gradle dependencies { // Room components implementation "androidx.room:room-runtime:2.5.2" annotationProcessor "androidx.room:room-compiler:2.5.2" // For LiveData (optional) implementation "androidx.lifecycle:lifecycle-livedata:2.6.1" // For Java 8 features (optional, if needed) compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }//将2.5.2替换为所需的ROOM版本
-
定义实体类、DAO接口、Database
-
创建数据库客户端(单例模式)
public class DatabaseClient { private Context context; private static DatabaseClient instance; private AppDatabase appDatabase; private DatabaseClient(Context context) { this.context = context; // 创建数据库实例 appDatabase = Room.databaseBuilder(context, AppDatabase.class, "MyRoomDatabase") .fallbackToDestructiveMigration() // 数据库版本升级时清空数据 .build(); } public static synchronized DatabaseClient getInstance(Context context) { if (instance == null) { instance = new DatabaseClient(context); } return instance; } public AppDatabase getAppDatabase() { return appDatabase; } }
在这个例子中,
DatabaseClient
用于创建和和管理数据库实例AppDatabase
. 1.成员变量
context:是android的上下文对象,用于创建数据库实例
instance:用于保存DatabaseClient的唯一一个实例的静态变量,防止多次创建实例
appDatabase:表示数据库实例,可以通过它访问与数据库交互的DAO方法
2.构造方法
private DatabaseClient(Context context)
这个方法被声明为私有防止外部直接创建
DatabaseClient
的实例,并且其只能通过getInstance
方法来获取实例。
Room.databaseBuilder(context, AppDatabase.class, "MyRoomDatabase")
:
ROOM.databaseBuilder
是创建数据库实例的方式,
context
是应用的上下文,
AppDatabase.class
是数据库的类,
MyRoomDatabase
是数据库的名字,它将决定数据库储存路径。
fallbackToDestructiveMigration()
:这是一个较暴力的迁移策略。在数据库版本不匹配时,ROOM会直接清空数据库并直接创建。可以使用Migration来防止丢失数据。
build()
:创建并返回AppDatabase的实例。 3.
public AppDatabase getAppDatabase()
:通过该公共方法来获取AppDatabase实例 -
在Activity中使用ROOM数据库进行操作
public class MainActivity extends AppCompatActivity { DatabaseClient databaseClient = DatabaseClient.getInstance(context); AppDatabase appDatabase = databaseClient.getAppDatabase(); UserDao userDao = appDatabase.userDao(); userDao.getAllusers(); }
首先
DatabaseClient databaseClient = DatabaseClient.getInstance(context);
声明构造客户端,然后通过AppDatabase appDatabase = databaseClient.getAppDatabase();
获取数据库实例,最后通过UserDao userDao = appDatabase.userDao();
获取DAO方法接口,通过userDao,可以直接使用已经定义好的操作语句例如userDao.getAllusers();
@Query("SELECT * FROM user_table ORDER BY user_name ASC") LiveData<List<User>> getAllUsers();
这可以查询到表中所有行。但是示例中并没有保存方法返回的数据,仅仅作为一个用法的示例。
4.ROOM数据库的常用注解
-
@Entity
:将一个类映射为数据库的表常用属性:
tableName
:指定表名indices
:创建索引primaryKeys
:定义主键foreignKeys
:定义外键 -
@PrimaryKey
:标记主键字段常用属性:
autoGenerate
:设置是否自动分配主键值 -
@ColumnInfo
:为类中的字段指定数据库的表中的列的信息常用属性:
name
:设置列名typeAffinity
:设置列的数据类型defaultValue
:设置列的默认值 -
@Dao
:将一个接口或抽象类标记为数据访问对象 -
@Insert
:将方法标记为插入操作 -
@Update
:将方法标记为更新操作 -
@Delete
:将方法标记为删除操作 -
@Query
:将方法标记为自定义的SQL查询语句的操作 -
@Database
:将一个类标记为ROOM数据库类常用属性:
@entities
:定义数据库包含的实体类@version
:设置数据库的版本号@exportSchema
:设置是否导出数据库架构 -
@Embedded
:嵌套对象,将嵌套的其他类的字段映射为该类的表的列 -
@Ignore
:忽略字段,防止其被映射到表中 -
@Transaction
:将方法标记为事务操作,保证操作原子性,例如在一个事务中需要连续执行多个数据库操作时数据确保同步 -
@TypeConverters
:将自定义数据类型转换为数据库支持的数据类型示例:
public class Converters { @TypeConverter public static Date fromTimestamp(Long value) { return value == null ? null : new Date(value); } @TypeConverter public static Long dateToTimestamp(Date date) { return date == null ? null : date.getTime(); } } @Database(entities = {User.class}, version = 1) @TypeConverters({Converters.class}) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); }
可用于储存日期等非基本类型
5.ROOM数据库的优势
- 相比于SQLite在运行时验证,ROOM数据库在编译阶段就会验证语法的正确性,减少运行时产生的错误提高安全性。
- 对于不太复杂的语句,可以通过大大简化代码。
- 通过DAO方法直接返回类型安全的对象或集合,无需手动解析游标
- ROOM自动支持事务,确保数据一致性。