Android开发-数据库SQLite
一、SQLite 基础:Android 内置的轻量级数据库
1. 什么是 SQLite?
- 轻量级、嵌入式、零配置的 SQL 数据库引擎。
- 数据存储在设备的私有目录中(
/data/data/<package_name>/databases/
)。 - 支持标准的 SQL 语法(如
CREATE
,INSERT
,UPDATE
,DELETE
,SELECT
)。 - 单文件数据库,无需独立的服务器进程。
2. 原生 SQLite 操作(不推荐直接使用)
// 继承 SQLiteOpenHelper
public class MyDatabaseHelper extends SQLiteOpenHelper {private static final String DB_NAME = "app.db";private static final int DB_VERSION = 1;public MyDatabaseHelper(Context context) {super(context, DB_NAME, null, DB_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {String CREATE_TABLE_USER = "CREATE TABLE users (" +"id INTEGER PRIMARY KEY AUTOINCREMENT, " +"name TEXT NOT NULL, " +"age INTEGER, " +"email TEXT UNIQUE)";db.execSQL(CREATE_TABLE_USER);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS users");onCreate(db);}
}
⚠️ 问题:代码繁琐、易出错、SQL 字符串拼接风险、无编译时检查。
二、Room 持久化库:现代化的 SQLite 抽象
Room
在 SQLite 基础上提供了三个核心组件:
@Entity
:定义数据库表和实体类。@Dao
(Data Access Object):定义数据访问方法(增删改查)。@Database
:数据库持有者,作为访问底层数据源的入口。
1. 添加依赖
在 app/build.gradle
中添加:
dependencies {def room_version = "2.6.1"implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version" // For Java// Kotlin 使用 kapt// kapt "androidx.room:room-compiler:$room_version"// 可选:Room Kotlin 协程支持implementation "androidx.room:room-ktx:$room_version"// 可选:RxJava 支持implementation "androidx.room:room-rxjava3:$room_version"
}
2. 定义 Entity(实体类)
@Entity(tableName = "users")
public class User {@PrimaryKey(autoGenerate = true)public long id;@ColumnInfo(name = "name")public String name;@ColumnInfo(name = "age")public int age;@ColumnInfo(name = "email", index = true) // 添加索引public String email;// 必须提供空参构造函数或 Room 兼容的构造函数public User() {}public User(String name, int age, String email) {this.name = name;this.age = age;this.email = email;}// Getter 和 Setter ...
}
✅ 关键注解:
@Entity
:标记为数据库表。@PrimaryKey
:主键,autoGenerate=true
自动增长。@ColumnInfo
:指定列名、是否索引等。@Ignore
:忽略该字段,不存入数据库。
3. 定义 DAO(数据访问对象)
@Dao
public interface UserDao {// 插入@Insertlong insertUser(User user); // 返回插入行的主键@InsertList<Long> insertUsers(List<User> users); // 批量插入// 查询@Query("SELECT * FROM users")List<User> getAllUsers();@Query("SELECT * FROM users WHERE age > :minAge")List<User> getUsersOlderThan(int minAge);@Query("SELECT * FROM users WHERE name LIKE :name")List<User> findUsersByName(String name);@Query("SELECT * FROM users WHERE id = :userId")User getUserById(long userId);// 更新@Updateint updateUser(User user); // 返回更新的行数// 删除@Deleteint deleteUser(User user); // 返回删除的行数@Query("DELETE FROM users WHERE age < :minAge")int deleteUsersYoungerThan(int minAge);
}
✅ 优势:
@Query
:编写 SQL 查询,支持参数绑定(:paramName
)。@Insert
,@Update
,@Delete
:无需写 SQL,Room 自动生成。- 编译时检查:SQL 语法错误在编译时报错,非运行时崩溃。
4. 定义 Database
@Database(entities = {User.class}, version = 1, exportSchema = false)
@TypeConverters({Converters.class}) // 注册类型转换器
public abstract class AppDatabase extends RoomDatabase {public static final String DATABASE_NAME = "app_database";// 抽象方法,返回 DAO 实例public abstract UserDao userDao();// 单例模式private static volatile AppDatabase INSTANCE;public static AppDatabase getInstance(Context context) {if (INSTANCE == null) {synchronized (AppDatabase.class) {if (INSTANCE == null) {INSTANCE = Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class, DATABASE_NAME)// .allowMainThreadQueries() // ❌ 仅用于测试,禁止在主线程查询.addCallback(roomCallback) // 数据库创建/升级回调.build();}}}return INSTANCE;}// 数据库回调private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {@Overridepublic void onCreate(@NonNull SupportSQLiteDatabase db) {super.onCreate(db);// 数据库创建时执行,如插入初始数据new PopulateDbAsync(INSTANCE).execute();}};// 异步填充初始数据static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {private UserDao userDao;PopulateDbAsync(AppDatabase db) {userDao = db.userDao();}@Overrideprotected Void doInBackground(Void... voids) {userDao.insertUser(new User("张三", 25, "zhangsan@email.com"));userDao.insertUser(new User("李四", 30, "lisi@email.com"));return null;}}
}
三、高级特性与最佳实践
1. 类型转换器(TypeConverters)
Room 默认不支持存储复杂类型(如 Date
, List<String>
, 自定义对象)。
public class Converters {@TypeConverterpublic static Date fromTimestamp(Long value) {return value == null ? null : new Date(value);}@TypeConverterpublic static Long dateToTimestamp(Date date) {return date == null ? null : date.getTime();}// 存储 List<String> 为 JSON 字符串@TypeConverterpublic static List<String> fromString(String value) {if (value == null || value.isEmpty()) return Collections.emptyList();return Arrays.asList(value.split(","));}@TypeConverterpublic static String toString(List<String> list) {return list == null ? null : TextUtils.join(",", list);}
}
在 @Database
上使用 @TypeConverters({Converters.class})
。
2. 数据库升级(Migration)
当 version
增加时,必须提供 Migration
策略。
static final Migration MIGRATION_1_2 = new Migration(1, 2) {@Overridepublic void migrate(@NonNull SupportSQLiteDatabase database) {// 例如:添加新列database.execSQL("ALTER TABLE users ADD COLUMN phone TEXT");}
};// 在 databaseBuilder 中添加
.addMigrations(MIGRATION_1_2)
⚠️ 重要:必须为每一对版本(如 1->2, 2->3)提供 Migration,或使用
fallbackToDestructiveMigration()
(慎用,会丢失数据)。
3. 异步操作(推荐)
绝对禁止在主线程进行数据库操作!
(1) 使用 ExecutorService
private ExecutorService executor = Executors.newFixedThreadPool(4);executor.execute(() -> {User user = AppDatabase.getInstance(context).userDao().getUserById(1);// 在子线程中处理结果,再切回主线程更新 UI
});
(2) 使用 Kotlin 协程(推荐)
// 在 DAO 中返回 Flow 或 suspend 函数
@Query("SELECT * FROM users")
fun getAllUsers(): Flow<List<User>> // Flow 支持实时更新@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUserById(id: Long): User
(3) 使用 RxJava
@Query("SELECT * FROM users")
Observable<List<User>> getAllUsers();
4. 关系查询(@Relation)
Room 支持简单的关联查询(1对1,1对多)。
public class UserWithBooks {@Embeddedpublic User user;@Relation(parentColumn = "id",entityColumn = "user_id")public List<Book> books;
}
5. 全文搜索(FTS)
使用 @Fts3
或 @Fts4
注解创建全文搜索表。
四、最佳实践总结
实践 | 说明 |
---|---|
✅ 使用 Room 而非原生 SQLite | 更安全、更高效、更易维护 |
✅ 异步操作 | 使用协程、RxJava 或 Executor |
✅ 数据库版本管理 | 提供 Migration ,避免数据丢失 |
✅ 合理使用索引 | 在频繁查询的列上添加 index = true |
✅ 避免 N+1 查询 | 使用 @Relation 或 JOIN 查询一次性获取关联数据 |
✅ 编译时检查 | 依赖 room-compiler ,及时发现 SQL 错误 |
✅ 数据观察 | 使用 LiveData 或 Flow 实现数据变更自动刷新 UI |
五、Room vs 原生 SQLite vs 其他方案
方案 | 优点 | 缺点 | 推荐度 |
---|---|---|---|
原生 SQLite | 完全控制,性能极致 | 代码繁琐,易出错,无编译检查 | ⭐ |
Room | 类型安全,编译检查,抽象层,性能好 | 需学习注解 | ⭐⭐⭐⭐⭐ |
Realm | 实时同步,跨平台,易用 | 商业许可,APK 体积大 | ⭐⭐⭐ |
ObjectBox | 速度极快,NoSQL | 学习成本,社区较小 | ⭐⭐⭐ |
✅ 结论:对于绝大多数 Android 应用,Room 是首选。
六、动手实践:构建一个笔记应用
- 创建
Note
Entity(包含 id, title, content, timestamp)。 - 定义
NoteDao
(增删改查,按标题搜索)。 - 创建
NoteDatabase
。 - 使用
ViewModel
和LiveData
观察数据变化。 - 在
RecyclerView
中展示笔记列表。
七、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!