Android 数据库 Room 的开发应用详解
一. 简介:
Room 在 SQLite 上提供了一个形象层,以便在充分利用 SQLite 的弱小性能的同时,可能流畅地拜访数据库。
Room 蕴含 3 个次要组件:
数据库:蕴含数据库持有者,并作为利用已保留持久性关系型数据的底层连贯的次要接入点。
@Database 正文
1. 是扩大 RoomDatabase 的抽象类。
2. 在正文中增加与数据库关联的实体表。
3. 蕴含具备 0 个参数且返回应用 @Dao 正文的类的形象办法。
在运行时,您能够通过调用 Room.databaseBuilder()或 Room.inMemoryDatabaseBuilder()获取 Database 的实例。
@Entity: 示意数据库中的表
@Dao: 蕴含用于拜访数据库的办法
二:依赖 Room 数据库
1. 在 App 模块下 bulid.Gradle 增加我的项目的依赖
// 增加 Room 依赖
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'
三:创立一个实体类 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;}
}
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 最好应用单例模式
如果不设置数据库在主线程操作的话就会报错,谬误提醒为
故须要应用数据库最好在 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: 不负青春, 不负韶华; 不负幻想, 不负将来。