Skip to content

Repository files navigation

sqliteorz

一个轻量、无注解的 Android SQLite ORM,适合学习、原型和数据模型较简单的应用。 sqliteorz 基于 SQLiteOpenHelper 和反射提供建表、对象映射、CRUD、事务批量写入、 多数据库和版本迁移能力。

sqliteorz 的目标是保持简单。如果项目需要关系映射、复杂查询、响应式数据流或编译期 SQL 校验,建议使用 Room。

特性

  • 模型继承 DBBaseModel 即可使用,无需注解或代码生成。
  • 自动创建数据表,内置 idcreateTimeupdateTime 字段。
  • 支持单条及批量写入、条件查询、排序、分页、更新、删除和计数。
  • 查询条件通过 whereArgs 绑定,更新值通过 ContentValues 绑定。
  • 批量写入自动分批并使用事务。
  • 支持多个数据库、非破坏性迁移及显式破坏性重建。
  • 最低支持 Android 5.0(API 21),库本身没有第三方运行时依赖。

引入

在项目的 settings.gradle 中加入 JitPack:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url "https://jitpack.io" }
    }
}

在应用模块中添加依赖:

dependencies {
    implementation "com.github.lawnvi:sqliteorz:2.0.0"
}

快速开始

1. 定义模型

模型必须继承 DBBaseModel,并提供可访问的无参构造函数。Kotlin 构造参数都有默认值时, 编译器会生成可用的无参构造函数。

data class User(
    var name: String = "",
    var age: Int = 0,
    var enabled: Boolean = true,
) : DBBaseModel() {
    override fun getTableName() = "users"
}

建议显式覆写 getTableName(),避免包名或类名变化影响已有表名。

支持以下字段类型及对应的可空类型:

Kotlin 类型 SQLite 类型
IntShortLongBoolean INTEGER
FloatDouble REAL
CharString TEXT
ByteArray BLOB

2. 初始化

建议在 Application.onCreate() 中注册模型并初始化:

val driver = DBDriver(
    name = "app.db",
    version = 1,
).apply {
    registerModel(User::class.java)
}

OrzHelper.initialize(
    applicationContext,
    drivers = arrayOf(driver),
)

数据库默认保存在应用内部数据库目录,不需要存储权限。

3. 保存与查询

val user = User(name = "张三", age = 20)
check(user.save())

val adults = DBOrz.findModels<User>(
    where = "age >= ? AND enabled = ?",
    whereArgs = arrayOf("18", "1"),
    orderMap = linkedMapOf("age" to -1),
)

val userById = DBOrz.findOne<User>(id = user.id)
val count = DBOrz.count<User>(
    where = "age >= ?",
    whereArgs = arrayOf("18"),
)

orderMap 的值大于 0 表示升序,其他值表示降序。查询全部数据时显式使用 where = "1=1"

分页查询:

val page = DBOrz.findModels<User>(
    where = "1=1",
    orderMap = linkedMapOf("id" to -1),
    limit = 20,
    offset = 40,
)

所有外部输入都应通过 ?whereArgs 绑定,不要直接拼接到 SQL 中。

4. 更新与删除

已保存的模型可以直接操作当前记录:

user.update(mapOf("name" to "李四", "enabled" to false))
user.delete()

也可以按条件批量操作:

DBOrz.update<User>(
    setMaps = mapOf("enabled" to false),
    where = "age < ?",
    whereArgs = arrayOf("18"),
)

DBOrz.delete<User>(
    where = "enabled = ?",
    whereArgs = arrayOf("0"),
)

为避免误操作,更新和删除全部数据时必须显式传入 where = "1=1"

5. 批量保存

val users = List(1_000) { index ->
    User(name = "user-$index", age = 18 + index % 50)
}

DBOrz.saveModels(users)

批量数据会按每 500 条一组执行事务。同一批模型必须属于同一张表。

多数据库

每个模型只能注册到一个数据库。配置多个数据库时,需要显式指定默认数据库:

val mainDriver = DBDriver("main.db").apply {
    registerModel(User::class.java)
}
val cacheDriver = DBDriver("cache.db").apply {
    registerModel(CacheEntry::class.java)
}

OrzHelper.initialize(
    applicationContext,
    default = "main.db",
    drivers = arrayOf(mainDriver, cacheDriver),
)

sqliteorz 会根据模型注册关系自动选择对应数据库。

数据库升级

数据库版本增加时应注册迁移;没有迁移策略会直接抛出异常,避免静默丢失数据:

val driver = DBDriver(name = "app.db", version = 2)
    .migrateWith { db, oldVersion, _ ->
        if (oldVersion < 2) {
            db.execSQL("ALTER TABLE users ADD COLUMN email TEXT")
        }
    }

driver.registerModel(User::class.java)

仅对缓存等允许丢失数据的数据库启用破坏性重建:

driver.fallbackToDestructiveMigration()

字段重命名、删除、类型变化及索引变更都需要在迁移中自行处理。

自定义字段转换

需要加密、压缩等自定义写入逻辑时,可以覆写 toContentValues()

override fun toContentValues() = super.toContentValues().apply {
    put("content", encrypt(content))
}

如果同时需要自定义读取逻辑,可覆写 cursor2Model(),并确保读写格式一致。

从 1.x 升级

  • 原来的 fieldStr()valueStr() 扩展点应迁移到 toContentValues()
  • 模型主键 idInt 调整为 Long,使用主键变量的业务代码需要同步修改类型。
  • ByteArray 新数据使用原生 SQLite BLOB,读取时兼容旧版 Base64 文本。
  • 默认数据库改用应用内部数据库目录;检测到旧的应用专属外部目录数据库时会继续使用旧路径。
  • 升级前请备份真实业务数据库,并为每个数据库版本变化提供迁移。

已知限制

  • 主键使用 SQLite INTEGER PRIMARY KEY,在模型中映射为 Long
  • 表结构和对象映射依赖运行时反射,没有编译期字段或 SQL 校验。
  • 不提供表关联、对象关系、LiveData、Flow 或跨进程访问能力。
  • 查询条件使用 SQLite selection 字符串,复杂查询可直接使用原生 SQLite 或改用 Room。

About

a simple sqlite orm

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages