328 lines
6.3 KiB
Markdown
328 lines
6.3 KiB
Markdown
|
|
# 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年*
|