简单了解Room: 它是对象关系映射框架,采用注解的形式。它能缓存相关数据,使用户在断网的情况下浏览相应内容,重新连接网络时,用户发起的内容更改都会同步到服务器。能将本地数据保存到数据库中,它提供了一个SQLite的抽象层(封装),能够让我们更加稳健地访问数据库,能够提升数据库的性能。
Room包含三个主要的组件:
1.Database:包含数据库持有者,并充当与应用程序持久化的、关系型的数据的底层连接的主要访问点。
2.Entity:表示数据库内的表。
3.DAO: 包含用于访问数据库的方法。
小贴士:
- 要在应用中使用Room,要在应用的build.gradle文件中添加依赖。
- Room.databaseBuilder()或Room.inMemoryDatabaseBuilder()可以获得数据库实例哦!
使用Room数据库来获取与该数据库关联的数据访问对象(DAO),再使用每个DAO从数据库中获取实体,将对这些实体所有更改保存回数据库中。最后,使用实体来获取和设置与数据库中的表列相对应的值。
此处附上Room的架构图如下:
下面是Room的简单使用:具有一个实体和一个DAO的示例数据库配置
- User
@Entity
public class User{
@PrimaryKey
public int uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(prefix = "last_name")
public String lastName;
}
@Entity(tableName = “users”)默认情况下,Room 将类名称用作数据库表名称。如果希望表具有不同的名称,设置 @Entity 注释的 tableName 属性。
@PrimaryKey是将字段标注为主键,每个实体必须将至少 1 个字段定义为主键。即使只有 1 个字段,我们仍然需要为该字段添加 @PrimaryKey 注释。
@ColumnInfo用来定义表中的字段名。Room 将字段名称用作数据库中的列名称。如果您希望列具有不同的名称,请将 @ColumnInfo 注释添加到字段。
小贴士: SQLite 中的表名称不区分大小写。
- UserDao
将DAO类添加到应用前要向build.gradle文件中添加架构组件工件。
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
@Query是DAO类中使用的主要注释,它是对数据库的查询,允许对数据库执行读/写操作。Room会验证查询的返回值:当返回的对象的字段名称与查询响应中对应列的名称只有部分字段名称匹配发出警告;没有任何字段名称匹配,发出错误。(关于查询,如果学了数据库原理就能比较容易理解)。
@Insert是实现将所有参数插入到数据库,若只接收一个参数,则是插入新的rowId返回long,如果参数是数组或者集合,则返回long[]或List。
@Delete会从数据库中删除一组以参数形式给出的实体,它使用主键查找要删除的实体。
- Database
@Database(entities = {User.class}, version = 1)
public abstract class Database extends RoomDatabase {
public abstract UserDao userDao();
}
创建上述文件后,我们就可以用下面的代码来获取已创建的数据库实例。
Database db = Room.databaseBuilder(getApplicationContext(),
Database.class, "database-name").build();
理论知识学的差不多了,现在到了实践环节,在AS中做个简单的关于学生表的案例,从这三方面入手:
数据库:建立数据库、升级数据库,删除数据库(这个用的少);
数据表:建立、修改、删除数据库中的数据表;
记录:对数据表中的数据记录进行添加、删除、修改、查询等操作。
先写上Room的三大组件:
@Entity
@Entity
public class Student {//定义学生表
@PrimaryKey //设为主键并可设为自增,这里没有设置
private long id;//学生学号
@ColumnInfo(name = "name")//可用于定义表中字段名,这里并不需要,就举个栗子!
private String name;//学生姓名
private String sex;//学生性别
private int age;//学生年龄
private String major;//学生专业
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public String getMajor() {
return major;
}
public int getAge() {
return age;
}
public void setId(long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setMajor(String major) {
this.major = major;
}
public void setAge(int age) {
this.age = age;
}
}
小贴士: 这里你可以直接用@Entity,然后会报错,你直接让AS自动帮你添加依赖好啦,我开始自己添加依赖不知道为啥没成功。
@Dao
@Dao
public interface StudentDao {
@Query("SELECT * FROM Student")
public Student[] getAllStudents(); //加载所有学生数据
@Query("SELECT * FROM Student WHERE name = :name")
public Student loadStudentByName(String name); //根据名字加载学生信息
@Insert
public void insertOneStudent(Student student); //插入一条学生信息
@Insert
public void insertMultiStudent(Student... students); //插入多条学生信息
@Update(onConflict = OnConflictStrategy.REPLACE)
public int updateStudents(Student... students); //更新学生信息,当有冲突时则进行替代
@Delete
public void deleteStudent(Student student);//删除学生信息
@Delete
public void deleteStudents(Student... students);//删除所有学生信息
}
@Database
@Database(entities = {Student.class},version = 6, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract StudentDao studentDao();
private static AppDatabase mInstance;
public AppDatabase() { }
public static AppDatabase getsInstance(Context context) {
if (mInstance == null) {
synchronized (AppDatabase.class) {
if (mInstance == null) {
mInstance = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "studentDb").fallbackToDestructiveMigration().addMigrations().allowMainThreadQueries().build();
}
}
}
return mInstance;
}
}
- 在MainActivity中添加下列代码你就可以在data里面看到数据库了
如果你的应用在单个进程中运行,则在实例化 AppDatabase 对象时应遵循单例设计模式。
AppDatabase database = AppDatabase.getsInstance(this);
//拿到数据库操作对象
studentDao = database.studentDao();
喏!
- exportSchema 设为false表示不想保留版本的历史记录。
- fallbackToDestructiveMigration().addMigrations().allowMainThreadQueries()这一长串的东西是用于数据库的迁移。在调试过程中,你可能会碰到下面的错误,修改了数据库,但是版本没有升级。
java.lang.IllegalStateException: Room cannot verify the data integrity.
Looks like you've changed schema but forgot to update the version
number. You can simply fix this by increasing the version number.
在这时,你就将version+1, 如果你没有添加migration,还是会有错误,会让你添加一个addMigration或者调用fallbackToDestructiveMigration完成数据库的迁移。加上上面这串东西后就可以正常运行,但是你又会发现,你的数据库被清空了。
解决方案:需要提供一个实现了的migration
1.把版本号自增
@Database(entities = {Student.class},version = 6, exportSchema = false)
2.添加一个version:1->2的migration
static final Migration MIGRATION_1_2 = new Migration(1, 2) {。。。}
3.把migration 添加到 databaseBuilder
mInstance = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, “studentDb”) .fallbackToDestructiveMigration()
.addMigrations()
.allowMainThreadQueries()
.build()
这样数据库更新了,旧数据也保留了。
- entities = {Student.class}表示这个数据库目前只有一个Student表,如果有多张表要用逗号隔开。
现在继续我们的简单案例,有关表的增删改查
案例界面:(有时间的话可做美化)
MainActivity(里面是按钮监听等)
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private String name;
private Student student;
private Button add;//添加一条记录按钮
private Button add_some;//添加多条记录按钮
private Button delete;//删除一条数据按钮
private Button update;//修改一条数据按钮
private Button enquire;//查询一条数据按钮
private Button enquire_some;//查询所有数据按钮
private Button delete_some;//删除所有数据
private EditText show;//结果
private EditText update_edi1;//要修改的姓名
private EditText update_edi2;//修改后的专业
private EditText enquire_edi;//要查询的姓名
private EditText delete_edi;//要删除的姓名
private EditText add_id_edi;//要添加的学号
private EditText add_name_edi;//要添加的姓名
private EditText add_sex_edi;//要添加的性别
private EditText add_age_edi;//要添加的年龄
private EditText add_major_edi;//要添加的专业
private StudentDao studentDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
add = findViewById(R.id.Add);
add_some = findViewById(R.id.Add_Some);
delete = findViewById(R.id.Delete);
update = findViewById(R.id.Update);
enquire = findViewById(R.id.Enquire);
enquire_some = findViewById(R.id.Enquire_Some);
delete_some = findViewById(R.id.Delete_Some);
show = findViewById(R.id.show);
update_edi1 = findViewById(R.id.Update_edi1);
update_edi2 = findViewById(R.id.Update_edi2);
enquire_edi = findViewById(R.id.Enquire_edi);
delete_edi = findViewById(R.id.Delete_edi);
add_id_edi = findViewById(R.id.Add_id_edi);
add_name_edi = findViewById(R.id.Add_name_edi);
add_sex_edi = findViewById(R.id.Add_sex_edi);
add_age_edi = findViewById(R.id.Add_age_edi);
add_major_edi = findViewById(R.id.Add_major_edi);
add.setOnClickListener(this);
add_some.setOnClickListener(this);
delete.setOnClickListener(this);
update.setOnClickListener(this);
enquire.setOnClickListener(this);
enquire_some.setOnClickListener(this);
delete_some.setOnClickListener(this);
AppDatabase database = AppDatabase.getsInstance(this);
//拿到数据库操作对象
studentDao = database.studentDao();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.Add:
//增加一条学生记录
studentDao.insertOneStudent(createOneStudent());
show.setText("添加一条记录成功!");
break;
case R.id.Add_Some:
//增加多条学生记录
studentDao.insertMultiStudent(createMultiStudent());
show.setText("添加多条记录成功!");
break;
case R.id.Delete:
//删除一条学生记录
name = delete_edi.getText().toString();
student = studentDao.loadStudentByName(name);
String info =student.getId()+" "
+student.getName()+" "
+student.getSex()+" "
+student.getAge()+" "
+student.getMajor();
studentDao.deleteStudent(student);
show.setText(info);
break;
case R.id.Update:
//更新一条记录
updateRecord();
break;
case R.id.Enquire:
//查询一条记录
student = queryOneRecord();
String info2 = student.getId()+" "
+student.getName()+" "
+student.getSex()+" "
+student.getAge()+" "
+student.getMajor();
studentDao.deleteStudent(student);
show.setText(info2);
break;
case R.id.Enquire_Some:
//查询所有记录
Student[] studentList = studentDao.getAllStudents();
String info3 = "";
for(int i=0;i<studentList.length;i++){
info3 += studentList[i].getId()+" "
+studentList[i].getName()+" "
+studentList[i].getSex()+" "
+studentList[i].getAge()+" "
+studentList[i].getMajor()+"\n";
}
show.setText(info3);
break;
case R.id.Delete_Some:
Student[] studentList1 = studentDao.getAllStudents();
studentDao.deleteStudents(studentList1);
show.setText("删除成功!");
break;
}
}
private void updateRecord() {
name = update_edi1.getText().toString();
Student student = studentDao.loadStudentByName(name);
student.setMajor(update_edi2.getText().toString());
studentDao.updateStudents(student);
String info = student.getId()+" "
+student.getName()+" "
+student.getSex()+" "
+student.getAge()+" "
+student.getMajor();
show.setText(info);
}
private Student queryOneRecord() {
name = enquire_edi.getText().toString();
Student student = studentDao.loadStudentByName(name);
return student;
}
private Student createOneStudent(){
Student student = new Student();
student.setId(Integer.parseInt(add_id_edi.getText().toString()));
student.setName(add_name_edi.getText().toString());
student.setSex(add_sex_edi.getText().toString());
student.setAge(Integer.parseInt(add_age_edi.getText().toString()));
student.setMajor(add_major_edi.getText().toString());
return student;
}
private Student[] createMultiStudent(){
Student[] students = new Student[3];
Student student1 = new Student();
student1.setId(20183004);
student1.setName("小张");
student1.setSex("男");
student1.setAge(20);
student1.setMajor("通信工程");
Student student2 = new Student();
student2.setId(20183006);
student2.setName("小李");
student2.setSex("男");
student2.setAge(21);
student2.setMajor("物联网");
Student student3 = new Student();
student3.setId(20183005);
student3.setName("小王");
student3.setSex("女");
student3.setAge(19);
student3.setMajor("计算机");
students[0] = student1;
students[1] = student2;
students[2] = student3;
return students;
}
}
下面是效果展示
添加一条数据并用查询显示结果
添加多条数据并查询所有数据
删除一条数据并查询所有数据
更新一条数据并查询所有数据
查询一条数据
删除所有数据并查询所有数据
心得: 刚看到Room,我是一脸懵,慢慢的看Room的学习文档还是一脸懵,去找些案例更能理解了,再通过自己做案例理解的又会更透彻些。总之能实践是最好的学习方法了。
另:本文笔记参考文档:https://developer.android.google.cn/training/data-storage/room