Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
根据这个抽象层,我们不需要编写复杂的数据库操作代码,只需要关心抽象层的设计。
在应用或模块的 build.gradle 文件中添加所需工件的依赖项:
dependencies {
def room_version = "2.2.2"//当前官方的Room最新版本
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
如果你是用java构造应用程序,那么请删掉Guava、Kotlin的依赖,这样会让你编译的速度快一些。
数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
使用 @Database 注释的类应满足以下条件:
1. 是扩展 RoomDatabase 的抽象类。
2. 在注释中添加与数据库关联的实体列表。
3. 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。
4. 在运行时,可以通过调用 Room.databaseBuilder()
或 Room.inMemoryDatabaseBuilder()
获取 Database 的实例。
Entity:表示数据库中的表。
DAO:包含用于访问数据库的方法。
AsyncTask: 主线程中直接操作数据库是会报错的,我们可以使用构建AsyncTask子类来使数据库操作在后台执行。
我们测试一个用户的增删改查操作,来了解如何操作SQLite数据库
注解 | 作用 |
---|---|
@Entity | 表明这是一个Entity |
@PrimaryKey | 表明这个field是主键,autoGenerate = true代表自动生成 |
@ColumnInfo | 表明这是表的列 |
@Entity
public class User {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name ="username")
private String username;
@ColumnInfo(name = "password")
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
@Dao
public interface UserDao {
@Insert
void insertUser(User... user);
@Update
void updateUser(User user);
@Delete
void deleteUser(User user);
@Query("DELETE FROM USER")
void deleteAllUsers();
@Query("SELECT * FROM USER ORDER BY ID DESC")
List<User> getAllUsers();
}
需要注意的是,第一个insertUser
方法中我们使用User... user
作为参数,意义在于我们之后可以放任意个数的参数。
因为我们我们的CRUD都直接依赖于数据库的实例,我们希望在APP的任意处都可以获取唯一的数据库实例,这里使用了单例模式来获取数据库实例。
@Database(entities = {User.class},version = 1,exportSchema = false)
public abstract class UserDatabase extends RoomDatabase {
private static UserDatabase userDatabase;
static UserDatabase getInstance(Context context)
{
if(userDatabase==null)
{
synchronized (UserDatabase.class)
{
userDatabase=Room.databaseBuilder(context.getApplicationContext(),UserDatabase.class,"user_database").build();
}
}
return userDatabase;
}
//如果你还建了其他的表,也应该声明相关表的接口
public abstract UserDao getUserDao();
}
public class MainActivity extends AppCompatActivity {
UserDatabase userDatabase;
UserDao userDao;
Button buttonInsert, buttonClear, buttonDelete, buttonQuery, buttonUpdate;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
/**
* 插入
*/
buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userDao.insertUser(new User("user1", "123456"));
}
});
/**
* 查询所有
*/
buttonQuery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<User> allUsers = userDao.getAllUsers();
String str = "";
for (int i = 0; i < allUsers.size(); i++) {
User user = allUsers.get(i);
str += user.getId()+"-"+user.getUsername() + "-" + user.getPassword() + "\n";
}
textView.setText(str);
}
});
/**
* 清空所有
*/
buttonClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userDao.deleteAllUsers();
}
});
/**
* 删除一个
*/
buttonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
User user = new User("user1", "123456");
user.setId(1);
userDao.deleteUser(user);
}
});
/**
* 更新id为1的用户
*/
buttonUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
User user = new User("newuser", "123456");
user.setId(1);
userDao.updateUser(user);
}
});
}
private void init() {
buttonInsert = findViewById(R.id.button_insert);
buttonDelete = findViewById(R.id.button_delete);
buttonQuery = findViewById(R.id.button_query);
buttonClear = findViewById(R.id.button_clear);
textView = findViewById(R.id.textView2);
buttonUpdate = findViewById(R.id.button_update);
userDatabase = UserDatabase.getInstance(this);
userDao = userDatabase.getUserDao();
}
}
当我满心欢喜想要测试CRUD时,bug来了,run爆了这样一条异常
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
大概意思就是说你不可以在主线程中操作数据库,正如在前面所说,我们需要AsyncTask
类,从而在后台线程中执行数据库操作。
这里以使用改造AsyncTask类insert方法为例,当然对于本例我们应该亦需要新建update、delete等方法的AsyncTask类。
//三个参数分别为 操作的对象、进度、结果
public class InsertAsyncTasks extends AsyncTask<User,Void,Void>
{
private UserDao userDao;
public InsertAsyncTasks(UserDao userDao) {
this.userDao = userDao;
}
@Override
protected Void doInBackground(User... users) {
userDao.insertUser(users);
return null;
}
@Override
protected void onProgressUpdate(Void... values) {
//进度发生更新时
super.onProgressUpdate(values);
}
@Override
protected void onPreExecute() {
//执行之前
super.onPreExecute();
}
}
这样我们在Activity中的插入操作代码就应该这样写,即new一个InsertAsyncTasks
对象,并执行execute()
方法
/**
* 插入
*/
buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new InsertAsyncTasks(userDao).execute();
// userDao.insertUser(new User("user1", "123456"));
}
});
数据的插入是成功的,但是,有一个问题,我们的数据操作都在Activity中完成,是Activty的职责是管理生命周期。我们需要引入Repository概念来解决这个问题。
Repository并不是一个Room中的组件,而是一个概念,从而把数据库操作分离出来。
public class UserRepository {
private UserDao userDao;
public UserRepository(Context context) {
UserDatabase userDatabase=UserDatabase.getInstance(context);
userDao=userDatabase.getUserDao();
}
void insertUser(User... users)
{
new InsertAsyncTasks(userDao).execute(users);
}
}
上面代码依旧是以insert为例子,所以在Activity中我们便可以这样操作
/**
* 插入
*/
buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
User user=new User("user1", "123456");
userRepository.insertUser(user);
}
});