简单的Android SQLite使用
最简单的SQLite
在 Android 开发中,SQLite是一个轻量级的关系型数据库管理系统,经常用于存储和管理应用程序的数据。如果你刚刚学习Android数据库的使用,你一定要学习SQLite的使用。以下是一个简单的示例,展示了如何在 Android 应用中创建 SQLite 数据库、创建表、插入数据以及查询数据。
创建 SQLite 数据库、创建表
public class DBHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME = "mydatabase";private static final int DATABASE_VERSION = 1;// 构造方法public DBHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {// 创建表String createTableQuery = "CREATE TABLE mytable ("+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"+ "name TEXT,"+ "age INTEGER)";db.execSQL(createTableQuery);}
}
在活动或其他地方使用数据库(插入数据以及查询数据)
public class MainActivity extends AppCompatActivity {private DBHelper dbHelper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);dbHelper = new DBHelper(this);// 插入数据示例insertData("Alice", 25);insertData("Bob", 30);// 查询数据示例String data = queryData();Toast.makeText(this, "Data: " + data, Toast.LENGTH_SHORT).show();}private void insertData(String name, int age) {SQLiteDatabase db = dbHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("name", name);values.put("age", age);db.insert("mytable", null, values);db.close();}private String queryData() {SQLiteDatabase db = dbHelper.getReadableDatabase();String[] projection = {"name", "age"};Cursor cursor = db.query("mytable", projection, null, null, null, null, null);StringBuilder data = new StringBuilder();while (cursor.moveToNext()) {String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));data.append("Name: ").append(name).append(", Age: ").append(age).append("\n");}cursor.close();db.close();return data.toString();}
}
其中涉及的知识点简要如下:
- SQLiteOpenHelper: 这个类是用于管理数据库的创建和版本管理的工具类。通过继承这个类,可以方便地管理数据库的创建和升级。
(1)onCreate(SQLiteDatabase db): 在数据库第一次创建时调用,通常用于创建表和初始化数据。
(2)onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): 当数据库版本发生变化时调用,用于执行数据库的升级操作。 - SQLiteDatabase: 这个类提供了对 SQLite 数据库的操作方法,如插入、查询、更新、删除等。
(1)getWritableDatabase(): 获取可写数据库实例,用于执行插入、更新、删除等操作。
(2)getReadableDatabase(): 获取可读数据库实例,用于执行查询操作。
(3)insert(), update(), delete(), query(): 分别用于插入、更新、删除、查询数据。 - ContentValues: 这个类用于存储数据库表中的键值对,对应于数据库表中的一行数据。通常用于插入数据时使用。
- Cursor: 查询数据库时返回的结果集,用于遍历查询结果并获取数据。
如果你没有安装SQLite,存储的数据你要在哪找呢?
如果你没有使用任何 SQLite 数据库查看工具或者终端命令行,在 Android 应用程序中,SQLite 数据库通常存储在应用的私有目录中。这个目录的路径包含你的应用程序的包名,具体位置如下:
如果你在模拟器上运行应用程序,你可以使用 Android Studio 的 Device File Explorer来查看应用的数据目录。
数据库文件通常位于 /data/data/your.app.package/databases/ 目录下,其中 your.app.package 是你的应用程序的包名。
有人会疑问这些类的命名后缀为何吗(题外话,哈哈)
为什么有这些后缀?
- 清晰性和可维护性:良好的命名约定可以使代码更易于理解和维护。使用适当的后缀可以快速地表明类的作用和功能,例如 Helper表示辅助类,Contract 表示约定类,从而帮助其他开发者或自己在未来更容易地理解和修改代码。
- 遵循约定俗成:虽然这些后缀不是 Android开发的强制规定,但它们是一种良好的编程实践,符合普遍的命名约定和习惯。这样可以增加代码的一致性,减少团队成员之间理解代码意图的差异。
(嘿嘿嘿,不会以为自己命名,熬到交付就老板不敢裁掉你了吧,毕竟如果交给别人,别人看不懂,嘘~别瞎试)
数据库相关的只能使用这些后缀吗?
除了 Helper 和 Contract,在特定的框架或架构设计下,当然还可能会使用其他后缀或命名约定,例如:
- Repository:用于封装数据访问和持久化逻辑,如 UserRepository。
- Manager 或 Handler:用于管理特定数据或处理特定操作,如 DataManager 或 TaskHandler。
- DAO(Data Access Object):用于定义数据访问接口和操作,如 UserDao。
- Provider:用于提供数据或服务,如 ContentProvider。
- Adapter:用于适配数据或操作,如 ListAdapter。
- Listener:用于处理事件或响应,如 OnClickListener。
ROOM框架下的SQLite
呜呜呜,终于到文章的重点了。既然我们简单的使用SQLite已经无法满足你求知的心,那我们来进一步来一点高级货,香得勒
什么是ROOM框架
Room 是 Android 官方提供的一个持久化库,是一个用于在 SQLite 数据库上进行抽象化和简化Android应用中数据库操作的一个ORM框架。它通过将数据库表映射为Java或Kotlin中的实体类(Entity),以及提供数据访问对象(DAO)来简化数据库操作。ROOM框架支持编译时SQL验证,减少了运行时错误,并且简化了数据库迁移的过程。它允许开发者使用更高级别的抽象概念来操作数据库,从而简化了数据存储和访问的过程。
组件
- 实体类(Entity)
实体类用于表示数据库中的表。每个实体类都需要用@Entity注解标记,以指明它是一个数据库表。
实体类中的字段默认会被映射为表中的列,除非使用@Ignore注解来忽略某个字段。
每个实体类都必须至少定义一个主键字段,用于唯一标识表中的每一行。主键字段可以使用@PrimaryKey注解来标记。
@Entity(tableName = "users")
public class User {@PrimaryKey(autoGenerate = true)public int id;@ColumnInfo(name = "first_name")public String firstName;@ColumnInfo(name = "last_name")public String lastName;
}
- 数据访问对象(DAO)
DAO是ROOM框架中的核心组件,它定义了与数据库交互的方法,如增删改查(CRUD)操作。
DAO通常是一个接口,里面包含了多个使用@Insert、@Delete、@Update、@Query等注解标记的方法。
ROOM框架在编译时会为这些DAO接口自动生成实现类,使得开发者无需编写繁琐的SQL语句。
@Dao
public interface UserDao {@Query("SELECT * FROM users")List<User> getAllUsers();@Insertvoid insert(User user);@Deletevoid delete(User user);@Updatevoid update(User user);
}
- 数据库类(Database)
数据库类用于定义数据库的配置,并作为应用与数据库交互的主要入口点。
数据库类必须是一个继承自RoomDatabase的抽象类,并使用@Database注解来标记。
在@Database注解中,需要指定数据库中包含的实体类(通过entities属性),以及数据库的版本号(通过version属性)。
数据库类中还需要定义一些抽象方法,这些方法返回DAO接口的实例,以便在应用中使用这些DAO来与数据库交互。
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyAppDatabase extends RoomDatabase {public abstract UserDao userDao();
}
除此之外,你还可以设计这些:
仓库类(推荐使用哦)
虽然ROOM框架本身不直接要求仓库类,但使用仓库模式是一种将数据访问逻辑与应用业务逻辑分离的好方法。仓库类通常封装了对DAO的调用,并可能还包含缓存逻辑或数据转换逻辑。
示例:UserRepository.java,封装了与User相关的所有数据库操作,并提供更高级别的数据访问接口,让你的代码更加专业。
ViewModel和LiveData/Flow(推荐推荐,与UI交互)
虽然它们不是ROOM框架直接的一部分,但ViewModel和LiveData/Flow是Android Jetpack组件,常与ROOM一起使用来实现响应式UI更新。ViewModel负责准备和管理UI相关的数据,而LiveData或Flow则用于在数据发生变化时通知UI进行更新。
示例:UserViewModel.java,包含LiveData或Flow对象,这些对象在User数据发生变化时更新UI。
天哪,这个真的很管用,尤其如果你要使用MVVM和MVP整体架构的时候。
使用步骤
- 添加依赖:在 build.gradle 文件中添加 Room 的依赖。
implementation "androidx.room:room-runtime:2.5.0"
annotationProcessor "androidx.room:room-compiler:2.5.0"
- 定义实体类:使用 @Entity 注解定义数据库中的表结构。
(如上组件举例) - 定义 DAO:使用 @Dao 注解定义数据访问对象,定义需要的数据库操作方法。
- 创建数据库:继承 RoomDatabase,定义抽象方法来获取 DAO 实例,并使用 @Database 注解配置数据库。
- 使用数据库:通过获取数据库实例和 DAO 对象,执行数据操作。
主要特性和优点
-
编译时检查:Room 在编译时会检查 SQL 查询的语法和类型,这可以帮助开发者在编译期间发现潜在的错误。
(这其实在某一点上要求你需要提前设计好数据库表,不然作为一个一个小白,你编写完运行后,再来修改你的表,你会看到很多报错,不致命,但心梗,哈哈哈。) -
简化数据库操作:通过使用注解和少量的代码,开发者可以轻松地执行数据库操作,而不必编写大量的样板代码。
(除去一些ROOM框架无法支持的数据库操作语言,你可能在你的Android studio里看不到SQLite的代码,它自动帮你补充完整,多香啊) -
LiveData 支持:Room 可以与 Android 架构组件中的 LiveData 结合使用,使得数据库中数据的变化可以自动通知 UI 层,从而简化了数据的观察和更新流程。(这个很重要哦,需要了解)
-
轻量级:Room 库本身很小,并且不会增加 APK 的大小太多,适合用于移动设备。
(对于一些小应用真的再合适不过啦)
其他知识
数据库迁移
ROOM框架支持数据库迁移,当数据库结构发生变化时(如添加新字段、修改表名等),可以通过编写迁移类并指定迁移的起始版本号和目标版本号来实现平滑迁移。
查询优化
ROOM框架支持在编译时验证SQL查询语句,减少运行时错误。此外,还可以通过在实体类中使用@Index注解为字段添加索引,以提高查询效率。
验证SQL查询语句
@Dao
public interface UserDao { @Query("SELECT * FROM users WHERE name = :name") //这儿 User findByName(String name);
}
为字段添加索引
@Entity(indices = {@Index(value = {"firstName", "lastName"}, unique = true)})
public class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; // 其他字段和方法
}
- 在这个示例中,@Index注解被用来为firstName和lastName字段组合创建一个唯一索引。这意味着在users表中,firstName和lastName的组合值必须是唯一的。这不仅可以提高基于这些字段的查询效率,还可以确保数据的唯一性。
协程支持
ROOM框架与Kotlin协程(Coroutine)无缝集成,可以在协程中执行数据库操作,避免在主线程中进行耗时操作导致的界面卡顿问题。
Room与Kotlin协程扩展
ROOM提供了对Kotlin协程的扩展支持,使得数据库操作可以更加简洁地以异步方式执行。开发者可以在DAO接口的方法中使用suspend修饰符,并在调用这些方法时使用协程上下文来执行它们。
Room与Flow
ROOM还提供了与Kotlin Flow的集成,允许开发者以响应式编程的方式处理数据库查询结果。通过使用Flow,开发者可以轻松地监听数据库查询结果的变化,并在结果更新时自动更新UI。
好了,溜了,溜了