Files
mkdocs/docs/android面试/数据存储/Room数据库.md
2026-01-15 11:53:37 +08:00

359 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Room数据库
## 目录
- [Room架构](#room架构)
- [Entity定义](#entity定义)
- [DAO接口](#dao接口)
- [Database配置](#database配置)
- [Room迁移](#room迁移)
- [Room与LiveData](#room与livedata)
- [Room最佳实践](#room最佳实践)
- [面试常见问题](#面试常见问题)
---
## Room架构
### Room 组件
```
┌─────────────┐
│ Entity │ ←─── 数据模型
└──────┬──────┘
┌──────▼──────┐
│ DAO │ ←─── 数据访问接口
└──────┬──────┘
┌──────▼──────┐
│ Database │ ←─── 数据库配置
└─────────────┘
```
### Room 优势
1. **编译时检查**SQL 语句编译时检查
2. **类型安全**:类型安全的数据访问
3. **支持 LiveData**:自动更新 UI
4. **支持 RxJava**:响应式编程
5. **简化代码**:减少样板代码
---
## Entity定义
### Entity 注解
```java
// Entity数据模型
@Entity(tableName = "user")
public class User {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "user_name")
private String name;
private String email;
private int age;
// Getters and Setters
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 String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
```
### Entity 注解选项
```java
@Entity(
tableName = "user",
indices = {@Index("name"), @Index(value = {"name", "age"})},
foreignKeys = {
@ForeignKey(
entity = Department.class,
parentColumns = "id",
childColumns = "department_id"
)
}
)
public class User {
@PrimaryKey
private int id;
@ColumnInfo(name = "department_id")
private int departmentId;
}
```
---
## DAO接口
### DAO 定义
```java
// DAO数据访问对象
@Dao
public interface UserDao {
// 查询
@Query("SELECT * FROM user")
List<User> getAllUsers();
@Query("SELECT * FROM user WHERE id = :id")
User getUserById(int id);
@Query("SELECT * FROM user WHERE age > :age")
List<User> getUsersByAge(int age);
// 插入
@Insert
void insertUser(User user);
@Insert
void insertUsers(User... users);
// 更新
@Update
void updateUser(User user);
// 删除
@Delete
void deleteUser(User user);
@Query("DELETE FROM user WHERE id = :id")
void deleteUserById(int id);
}
```
### 复杂查询
```java
@Dao
public interface UserDao {
// 多表查询
@Query("SELECT * FROM user u INNER JOIN department d ON u.department_id = d.id")
List<UserWithDepartment> getUsersWithDepartment();
// 参数查询
@Query("SELECT * FROM user WHERE name LIKE :name AND age > :age")
List<User> searchUsers(String name, int age);
// 返回 LiveData
@Query("SELECT * FROM user")
LiveData<List<User>> getAllUsersLive();
// 返回 FlowableRxJava
@Query("SELECT * FROM user")
Flowable<List<User>> getAllUsersFlowable();
}
```
---
## Database配置
### Database 定义
```java
// Database数据库配置
@Database(
entities = {User.class, Department.class},
version = 1,
exportSchema = false
)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
public abstract DepartmentDao departmentDao();
private static AppDatabase instance;
public static synchronized AppDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(
context.getApplicationContext(),
AppDatabase.class,
"app_database"
).build();
}
return instance;
}
}
```
### Database 配置选项
```java
Room.databaseBuilder(context, AppDatabase.class, "database_name")
.allowMainThreadQueries() // 允许主线程查询(不推荐)
.fallbackToDestructiveMigration() // 破坏性迁移
.addMigrations(MIGRATION_1_2, MIGRATION_2_3) // 迁移
.build();
```
---
## Room迁移
### 数据库迁移
```java
// 版本1 → 版本2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE user ADD COLUMN phone TEXT");
}
};
// 版本2 → 版本3
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE address (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"user_id INTEGER," +
"address TEXT" +
")");
}
};
// 配置迁移
Room.databaseBuilder(context, AppDatabase.class, "database_name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build();
```
---
## Room与LiveData
### LiveData 集成
```java
// DAO 返回 LiveData
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
LiveData<List<User>> getAllUsers();
@Query("SELECT * FROM user WHERE id = :id")
LiveData<User> getUserById(int id);
}
// 观察数据变化
userDao.getAllUsers().observe(this, users -> {
adapter.updateUsers(users);
});
```
### RxJava 集成
```java
// 添加依赖
dependencies {
implementation 'androidx.room:room-rxjava2:2.3.0'
}
// DAO 返回 Flowable
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
Flowable<List<User>> getAllUsers();
}
// 使用
userDao.getAllUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(users -> {
adapter.updateUsers(users);
});
```
---
## Room最佳实践
### 1. 使用单例模式
```java
// 数据库使用单例
public static synchronized AppDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(...).build();
}
return instance;
}
```
### 2. 在后台线程操作
```java
// ✅ 正确:在后台线程操作
new Thread(() -> {
userDao.insertUser(user);
}).start();
// ❌ 错误:在主线程操作(除非使用 allowMainThreadQueries
userDao.insertUser(user);
```
### 3. 使用事务
```java
@Dao
public interface UserDao {
@Transaction
@Query("SELECT * FROM user")
List<User> getAllUsers();
}
```
---
## 面试常见问题
### Q1: Room 的优势?
**答案:**
1. 编译时检查 SQL
2. 类型安全
3. 支持 LiveData
4. 支持 RxJava
5. 简化代码
### Q2: Room 的组件?
**答案:**
1. **Entity**:数据模型
2. **DAO**:数据访问接口
3. **Database**:数据库配置
### Q3: Room 迁移?
**答案:**
- 使用 Migration 类处理数据库升级
- 定义迁移规则
- 在 Database 配置中添加迁移
### Q4: Room 和 SQLite 的区别?
**答案:**
- **Room**SQLite 的封装,提供类型安全、编译时检查
- **SQLite**:底层数据库,需要手动编写 SQL
---
*最后更新2024年*