最近Android推出全新的数据库框架Room,它与GreenDAO相似,基于ORM对象关系映射,属于轻量级且快速的数据库。对SQLite语句封装成了对象,也就意味着我们可以直接操作对象,是我们熟悉的对象。
Room基于SQLite,提供一个抽象层,可以快速访问SQLite的所有功能。应用程序处理特殊的结构化数据,可以极大程度受益于本地数据的持久化。大多数使用场景是缓存关联的数据块。那样,尽管设备无法联网,用户仍然可以在离线时查阅数据。任何用户初始化内容,一旦发生改变,会在设备联网后同步到服务器。Room框架如下图:
Room有3大组件:
Database:包含数据库持有者,并且作为应用程序持久关系数据的底层连接主要访问点。
使用 @Database
注解,需要满足以下条件:
继承RoomDatabase的抽象类;
在注解时,包含与数据库关联的entity集合;
包含抽象方法,无参数,返回值是使用@Dao注解的类;
在运行时,你可以调用Room.databaseBuilder()
或者 Room.inMemoryDatabaseBuilder()来获取Database的实例。
Entity:代表数据库的表,使用@Entity注解;
DAO:包含数据库访问的增删查改方法,注解方法包括@Insert、@Update、@Query、@Delete;
用法比较简单,在gradle添加依赖:
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
implementation "android.arch.persistence.room:rxjava2:1.1.1"
implementation "android.arch.persistence.room:testing:1.1.1"
定义一个抽象类,继承于RoomDatabase,在获取Database实例时,构造数据库:
private static AppDatabase buildDatabase(final Context appContext,
final AppExecutors executors) {
return Room.databaseBuilder(appContext, AppDatabase.class, DATABASE_NAME)
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
}
}).build();
}
定义一个接口model,比如一个product产品:
public interface Product {
int getId();
String getName();
String getDescription();
int getPrice();
}
定义一张表,使用@Entity进行注解,声明id作为主键:
@Entity(tableName = "products")
public class ProductEntity implements Product {
@PrimaryKey
private int id;
private String name;
private String description;
private int price;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public ProductEntity(int id, String name, String description, int price) {
this.id = id;
this.name = name;
this.description = description;
this.price = price;
}
}
定义一个DAO,实现增删查改方法:
@Dao
public interface ProductDao {
//插入单个对象,发生冲突时就替换原来的
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertProduct(ProductEntity product);
//插入对象列表
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List products);
//根据id查询对象
@Query("select * from products where id = :productId")
ProductEntity loadProduct(int productId);
//查询整张表
@Query("SELECT * FROM products")
List loadAllProducts();
//更新指定对象
@Update
void updateProduct(ProductEntity product);
//删除指定对象
@Delete
void deleteProduct(ProductEntity product);
}
插入对象,数据库增删查改操作都需要在非UI线程执行,也就是开启子线程执行:
private void insert(){
RoomApplication.getInstance().getExecutors().diskIO().execute(() -> {
List productList = new ArrayList<>();
ProductEntity product1 = new ProductEntity(1, "apple", "very tasty", 10);
productList.add(product1);
ProductEntity product2 = new ProductEntity(2, "grape", "very very tasty", 10);
productList.add(product2);
appDatabase.productDao().insertAll(productList);
});
}
需要查询整张表时,可以这样操作:
private void query(){
RoomApplication.getInstance().getExecutors().diskIO().execute(() -> {
List productList = appDatabase.productDao().loadAllProducts();
if (productList != null && productList.size() > 0){
for (ProductEntity productEntity:productList){
Log.e(TAG, "name = " + productEntity.getName());
Log.e(TAG, "description=" + productEntity.getDescription());
}
}
});
}
更新指定对象:
private void update(){
RoomApplication.getInstance().getExecutors().diskIO().execute(() -> {
ProductEntity product = new ProductEntity(1, "apple", "very tasty and sweet", 20);
appDatabase.productDao().updateProduct(product);
});
}
删除指定对象:
private void delete(){
RoomApplication.getInstance().getExecutors().diskIO().execute(() -> {
ProductEntity product = new ProductEntity(2, "grape", "very very tasty", 10);
appDatabase.productDao().deleteProduct(product);
});
}
关于Room数据库升级,可以写Migration类来实现升级。在运行期,Room会运行每个Migration类的
migrate()方法,把旧版本的数据库迁移到新版本。例如,数据库1.2版本升级到1.3版本,在表中增加字段:
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Product` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Product"
+ " ADD COLUMN pub_year INTEGER");
}
};
关于Room数据库测试,推荐编写JUnit测试单元,运行在Android设备上,进行数据库的测试。单元测试示例:
@RunWith(AndroidJUnit4.class)
public class SimpleEntityReadWriteTest {
private UserDao mUserDao;
private TestDatabase mDb;
@Before
public void createDb() {
Context context = InstrumentationRegistry.getTargetContext();
mDb = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
mUserDao = mDb.getUserDao();
}
@After
public void closeDb() throws IOException {
mDb.close();
}
@Test
public void writeUserAndReadInList() throws Exception {
User user = TestUtil.createUser(1);
user.setName("frank");
mUserDao.insert(user);
List byName = mUserDao.findUsersByName("frank");
assertThat(byName.get(0), equalTo(user));
}
}
关于Room中使用复杂数据类型,可以使用TypeConverter进行数据类型转换。例如,我们希望持有Date的实例,我们可以写一个TypeConverter来存储一个相等的Unix时间戳:
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();
}
}
看到这里,大家应该对Room数据库框架有深刻认识与理解,可以开启全新的数据库之旅了,结合LifeCycle使用那就更加灵活。
Demo地址:https://download.csdn.net/download/u011686167/10684169