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

328 lines
6.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.
# SQLite数据库
## 目录
- [SQLite基础](#sqlite基础)
- [SQLite操作](#sqlite操作)
- [SQLite性能优化](#sqlite性能优化)
- [SQLite事务](#sqlite事务)
- [SQLite索引](#sqlite索引)
- [SQLite最佳实践](#sqlite最佳实践)
- [面试常见问题](#面试常见问题)
---
## SQLite基础
### SQLite 简介
```java
// SQLite轻量级关系型数据库
// - 嵌入式数据库
// - 无需服务器
// - 支持 SQL 语法
// - 适合移动应用
```
### 创建数据库
```java
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "my_database.db";
private static final int DATABASE_VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建表
db.execSQL("CREATE TABLE user (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT NOT NULL," +
"email TEXT," +
"age INTEGER" +
")");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 升级数据库
db.execSQL("DROP TABLE IF EXISTS user");
onCreate(db);
}
}
```
---
## SQLite操作
### 插入数据
```java
// 方式1使用 execSQL
db.execSQL("INSERT INTO user (name, email, age) VALUES (?, ?, ?)",
new String[]{"John", "john@example.com", "25"});
// 方式2使用 insert
ContentValues values = new ContentValues();
values.put("name", "John");
values.put("email", "john@example.com");
values.put("age", 25);
long id = db.insert("user", null, values);
```
### 查询数据
```java
// 查询所有
Cursor cursor = db.query("user", null, null, null, null, null, null);
// 条件查询
Cursor cursor = db.query("user",
new String[]{"id", "name", "email"},
"age > ?",
new String[]{"18"},
null, null, "name ASC");
// 使用 SQL
Cursor cursor = db.rawQuery("SELECT * FROM user WHERE age > ?", new String[]{"18"});
// 遍历结果
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String email = cursor.getString(cursor.getColumnIndex("email"));
}
cursor.close();
```
### 更新数据
```java
// 方式1使用 execSQL
db.execSQL("UPDATE user SET name = ? WHERE id = ?",
new String[]{"Jane", "1"});
// 方式2使用 update
ContentValues values = new ContentValues();
values.put("name", "Jane");
int count = db.update("user", values, "id = ?", new String[]{"1"});
```
### 删除数据
```java
// 方式1使用 execSQL
db.execSQL("DELETE FROM user WHERE id = ?", new String[]{"1"});
// 方式2使用 delete
int count = db.delete("user", "id = ?", new String[]{"1"});
```
---
## SQLite性能优化
### 1. 使用索引
```java
// 创建索引
db.execSQL("CREATE INDEX idx_name ON user(name)");
```
### 2. 使用事务
```java
// 批量操作使用事务
db.beginTransaction();
try {
for (int i = 0; i < 1000; i++) {
db.insert("user", null, values);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
```
### 3. 使用预编译语句
```java
// 使用 SQLiteStatement
SQLiteStatement stmt = db.compileStatement(
"INSERT INTO user (name, email) VALUES (?, ?)");
stmt.bindString(1, "John");
stmt.bindString(2, "john@example.com");
stmt.executeInsert();
stmt.close();
```
### 4. 避免 N+1 查询
```java
// ❌ 问题N+1 查询
List<User> users = getUsers();
for (User user : users) {
List<Order> orders = getOrdersByUserId(user.getId()); // N 次查询
}
// ✅ 解决:使用 JOIN
SELECT u.*, o.* FROM user u LEFT JOIN order o ON u.id = o.user_id
```
---
## SQLite事务
### 事务使用
```java
// 开始事务
db.beginTransaction();
try {
// 执行多个操作
db.insert("user", null, values1);
db.insert("user", null, values2);
db.insert("user", null, values3);
// 标记事务成功
db.setTransactionSuccessful();
} finally {
// 结束事务(如果未标记成功,会回滚)
db.endTransaction();
}
```
### 事务优势
```java
// 1. 原子性:要么全部成功,要么全部失败
// 2. 性能:批量操作性能更好
// 3. 一致性:保证数据一致性
```
---
## SQLite索引
### 创建索引
```java
// 单列索引
db.execSQL("CREATE INDEX idx_name ON user(name)");
// 复合索引
db.execSQL("CREATE INDEX idx_name_age ON user(name, age)");
// 唯一索引
db.execSQL("CREATE UNIQUE INDEX idx_email ON user(email)");
```
### 索引使用
```java
// 索引会加速查询
// 但会增加写入开销和存储空间
// 适合创建索引的列:
// 1. 经常用于 WHERE 条件的列
// 2. 经常用于 JOIN 的列
// 3. 经常用于 ORDER BY 的列
```
---
## SQLite最佳实践
### 1. 使用事务
```java
// 批量操作使用事务
db.beginTransaction();
try {
// 批量操作
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
```
### 2. 及时关闭 Cursor
```java
// ✅ 正确
Cursor cursor = db.query(...);
try {
// 使用 cursor
} finally {
cursor.close();
}
// ❌ 错误:忘记关闭
Cursor cursor = db.query(...);
// 使用 cursor
// 忘记关闭,可能导致内存泄漏
```
### 3. 使用预编译语句
```java
// 重复执行的 SQL 使用预编译语句
SQLiteStatement stmt = db.compileStatement("INSERT INTO user (name) VALUES (?)");
```
### 4. 数据库升级
```java
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 根据版本号升级
if (oldVersion < 2) {
// 升级到版本2
}
if (oldVersion < 3) {
// 升级到版本3
}
}
```
---
## 面试常见问题
### Q1: SQLite 的特点?
**答案:**
- 轻量级关系型数据库
- 嵌入式数据库,无需服务器
- 支持 SQL 语法
- 适合移动应用
### Q2: 如何优化 SQLite 性能?
**答案:**
1. 使用索引
2. 使用事务
3. 使用预编译语句
4. 避免 N+1 查询
### Q3: SQLite 事务的作用?
**答案:**
1. 原子性:保证操作要么全部成功,要么全部失败
2. 性能:批量操作性能更好
3. 一致性:保证数据一致性
### Q4: 什么时候创建索引?
**答案:**
- 经常用于 WHERE 条件的列
- 经常用于 JOIN 的列
- 经常用于 ORDER BY 的列
---
*最后更新2024年*