Room在SQLite上提供了一个抽象层, 能让开发者更简单便利的访问SQLite。
@Database(entities = {User.class}, version = 2)
public abstract class UserDatabase extends RoomDatabase{
private static UserDatabase sDatabase;
private static Object sObject = new Object();
public static UserDatabase getInstance(Context context) {
if(sDatabase == null) {
synchronized (sObject) {
if(sDatabase == null){
//允许主线程访问数据库,默认是不允许
// sDatabase = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "users.db")
// .allowMainThreadQueries()
// .build();
sDatabase = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "users.db")
.build();
}
}
}
return sDatabase;
}
public abstract UserDao getUserDao();//必须包含Dao
}
Room提供了 Migration 类用于迁移数据库,每一个 Migration 需要在构造函数里指定开始版本和结束版本。在运行时,Room会按照提供版本的顺序顺序执行每个Migration的migrate()方法,将数据库升级到最新的版本。
@Database(entities = {User.class}, version = 2)
public abstract class UserDatabase extends RoomDatabase{
private static UserDatabase sDatabase;
private static Object sObject = new Object();
private static Migration migrations1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE users_tb " + " ADD COLUMN address1_city TEXT default null");
database.execSQL("ALTER TABLE users_tb " + " ADD COLUMN address2_city TEXT default null");
Log.i("room---", "upgrade 1 to 2");
}
};
public static UserDatabase getInstance(Context context) {
if(sDatabase == null) {
synchronized (sObject) {
if(sDatabase == null){
//允许主线程访问数据库,默认是不允许
// sDatabase = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "users.db")
// .allowMainThreadQueries()
// .build();
sDatabase = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "users.db")
.addMigrations(migrations1_2)
.build();
}
}
}
return sDatabase;
}
public abstract UserDao getUserDao();
}
此组件的一个实例表示数据库的一行数据,对于每个Entity类来说,都会有对应的table被创建。想要这些Entity被创建,就需要写在上面Database的注解参数entities列表中。默认Entity中的所有字段都会拿来创建表,除非在该字段上加上@Ignore注解。
每个Entity至少定义一个主键,即使你的Entity只有一个字段也是如此。定义主键使用@PrimaryKey。如果你想让Room给你的Entity自动生成ID的话,可以使用@Primary的autoGenerate属性。如果Entity具有复合主键的话,可以使用@Entity的primaryKeys属性,参照下方代码:
@Entity(tableName = "users_tb")
public class User extends Object {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name")
public String name;
@ColumnInfo(name = "user_age")
public int age;
@Override
public String toString() {
return "id=" + id + " name=" + name + " age=" + age + " address1=" + mAddress1 + " address2=" + mAddress2;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
}
@Entity(tableName = "users_tb", indices = {@Index("user_age"), @Index("user_name")})
public class User extends Object {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name")
public String name;
@ColumnInfo(name = "user_age")
public int age;
@Override
public String toString() {
return "id=" + id + " name=" + name + " age=" + age + " address1=" + mAddress1 + " address2=" + mAddress2;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
}
public class Address {
public String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "city:" + city;
}
}
@Entity(tableName = "users_tb", indices = {@Index("user_age"), @Index("user_name")})
public class User extends Object {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name")
public String name;
@ColumnInfo(name = "user_age")
public int age;
@Embedded(prefix = "address1_")
public Address mAddress1;
@Embedded(prefix = "address2_")
public Address mAddress2;
@Override
public String toString() {
return "id=" + id + " name=" + name + " age=" + age + " address1=" + mAddress1 + " address2=" + mAddress2;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress1(Address address1) {
mAddress1 = address1;
}
public void setAddress2(Address address2) {
mAddress2 = address2;
}
public Address getAddress1() {
return mAddress1;
}
public Address getAddress2() {
return mAddress2;
}
}
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id"))
class Book {
@PrimaryKey
public int bookId;
public String title;
@ColumnInfo(name = "user_id")
public int userId;
}
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(User user);
@Delete
int delete(User user);
@Query("select * from users_tb")
List queryAll();
@Update
int update(User user);
@Query(" select * from users_tb where user_age= :age")
int queryByAge(int age);
}
public class SimpleInfo {
@ColumnInfo(name = "user_name")
public String name;
@ColumnInfo(name = "user_age")
public int age;
}
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(User user);
@Delete
int delete(User user);
@Update
int update(User user);
@Query("select * from users_tb where user_age= :age")
int queryByAge(int age);
@Query("select * from users_tb")
List queryAll();
@Query("select user_name, user_age from users_tb")
List querySimpleInfo();
}
room dao支持对数据集合/数组进行操作如下,如果insert方法只接受一个参数的话,表示仅仅插入一条数据,这是这个方法可以返回一个long型值,为新行的id。如果参数为数组或集合,则需要返回对应的long[]或者List,update 方法可以返回一个int型数据,表示此次修改影响到的行数, delete 方法可以返回一个int型数据,表示此次删除的行数。
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(User user);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(List<User> userList);
@Delete
int delete(User user);
@Delete
void deleteList(List<User> userList);
@Update
int update(User user);
@Update
void updateList(List<User> userList);
@Query("select * from users_tb where user_age= :age")
int queryByAge(int age);
@Query("select * from users_tb")
List queryAll();
@Query(" select user_name, user_age from users_tb")
List querySimpleInfo();
}
Room中的类型转换支持你将某个类的值存储到某一列中,为此Room提供了TypeConverter这个类用于将自定义类转换成Room所支持的类型。
例如我们想要将Date对象进行存储,我们可以这么写:
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遇到Date,就能将其转换成Room所支持的Long了。
下面看看AppDatabase要怎么写:
@Database(entities = {User.class}, version = 1)
@TypeConverters({Converter.class})
public abstract class UserDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
在UserDatabase上添加TypeConverters注解,并将Converter作为其参数。
接着User实体:
ORMLite是一款通过反射完成对象关系映射的数据库框架。其Android部分在github上有1.2k的star和0.3k的Fork。其使用简单,但由于使用反射,造成了一定的性能开销,其自身提供了ormlite_config机制通过读取文件内容绕过反射来创建数据表。其主要特性如下:
1.使用反射来完成对象关系的映射,速度较慢。
2.使用类sql描述sql语句,比如where.eq(“name”,name).and().eq(“deleteTime”,0);
3.在insert操作后自动设置数据的主键。
4.支持将父类的变量解析为数据库表字段。
5.支持sqlcipher。
6.提供connectionProxy,用于在CRUD等操作时进行统一的逻辑操作,如发送事件等。
GreenDao是Android平台的一款流行的对象关系映射数据库框架,在github上有8.4k的star和2.4k的fork,jar包大小140KB。
其主要特性如下:
1.使用自定义的gradle插件来完成sql相关代码的生成。该插件在GreenDao3.0版本后才开始支持,在3.0之前需要我们引入一个greendao generated项目用来生成代码。
2.它使用类sql来表示sql语句,类似ORMLite
3.他支持懒加载,在查找时,首先返回一个cursor,在我们需要使用到具体数据时,才将之前得到cursor转变为实体对象。
4.支持sqlcipher。
5.不支持将父类的变量解析为数据库表字段。
Room同样为Android平台的一款对象关系映射框架,其为2017年谷歌IO推出的Android Architecture Component的一部分,其主要特性如下:
1.其使用谷歌官方的注解处理器annotationProcessor完成对注解的解析。
2.使用原生sql来表达对数据库的操作。会在编译时会验证字段名称是否匹配,如果有问题,则发生编译错误,而不是运行时故障。
3.它还支持同为Android Architecture Component的LiveData,实现数据的动态刷新和绑定组件生命周期功能。
4.他并不支持sqlcipher,需要我们使用第三方库来支持。
5.支持父类变量解析为数据库表字段。
6.默认会让主线程的数据库查询操作崩溃,可以通过allowMainThreadQueries绕过这个限制。