最佳SQL Builder-在Android上使用jOOQ

最佳SQL Builder。 在Android上使用jOOQ


引言


在开发Android应用程序时,使用SQLite数据库作为主要存储是很自然的。 通常,移动设备上的数据库具有非常简单的方案,并且由10-15个表组成。 几乎任何SQL Builder,ORM甚至是裸露的SQLite API都适用于此类情况。


但是,遗憾的是,并不是所有的开发人员都幸运,有时描述大型数据模型,使用存储过程,使用自定义数据类型配置工作或在非常厚的实体的查询中编写10 INNER JOIN是我们的本分。 因此,您的忠实仆人并不幸运,本文的材料由此而来。 好吧,艰难时期需要采取严厉措施。 因此,在Android上滚动jOOQ。


一切都会好的,但是


但是有两个事实有必要应对。 首先是在与jOOQ合作的一开始就处于等待状态:在意识形态阶段。 实际上,为了启动代码生成过程,您需要获取jooq插件将连接到的数据库。 这个问题很容易解决,我们创建了一个带有gradle任务描述的模板项目以进行生成,然后我们在本地创建数据库,在配置中指定路径,启动插件并将接收到的源复制到我们的项目中。


接下来,假设我们生成了所有必需的类。 我们只是无法将它们复制到Android项目中-将需要其他依赖项,第一个依赖项是javax注释。 两种选择都很常见。 添加库(org.glassfish:javax.annotation),或者-使用出色的工具-在范围内查找并替换。


看起来一切都很好,完成了所有预设,将类复制并导入到项目中。 也许您甚至可以启动该应用程序,并且它可能会运行。 如果您需要支持<24级的Android API-别上当了,这不是我们旅程的终点​​。 事实是,开放源代码版本中的jOOQ目前以多种方式使用Java 8,正如您所知,这是有条件地成为Android上的朋友。 这个问题也可以通过两种方式解决:要么购买jOOQ,写信给技术支持,然后痛哭地寻求Java 6或Java 7的版本(根据网络上的文章判断,他们已经拥有了),或者如果您像我一样,没有那么强硬拥有图书馆所有最新创新的需求以及支付的欲望,即第二种方式。 jOOQ不久前开始迁移到Java 8。 迁移之前的最新版本是3.6.0,这意味着我们可以使用参数groovy version = '3.6.0'的生成器,并支持旧版本的设备。


走在绝望道路上的发烧友们等待着的最后一件事。 原则上,在Android中,没有JDBC,这意味着该花些时间寻找第三方解决方案了。 幸运的是,有一个类似的库-SQLDroid。


仅此而已。 他们的主要阶段和动作都流利地描绘出来。 现在让我们继续执行代码,总体上一切都很合理,但是为了减少您的时间,我将提供自己项目中的示例。


代码生成


jOOQ插件设置将如下所示:


 buildScript { repositories { mavenCentral() } dependencies { classpath "nu.studer:gradle-jooq-plugin:$jooq_plugin_version" } } apply plugin: 'nu.studer.jooq' dependencies { jooqRuntime "org.xerial:sqlite-jdbc:$xerial_version" } jooq { version = '3.6.0' edition = 'OSS' dev(sourceSets.main) { jdbc { driver = 'org.sqlite.JDBC' url = 'jdbc:sqlite:/Path/To/Database/database.db3' } generator { name = 'org.jooq.util.DefaultGenerator' strategy { name = 'org.jooq.util.DefaultGeneratorStrategy' } database { name = 'org.jooq.util.sqlite.SQLiteDatabase' } generate { relations = true deprecated = false records = true immutablePojos = true fluentSetters = true } target { packageName = 'com.example.mypackage.data.database' } } } } 

安卓系统


所需依赖项:


 implementation "org.jooq:jooq:$jooq_version" implementation "org.sqldroid:sqldroid:$sqldroid_version" implementation "org.glassfish:javax.annotation:$javax_annotations_version" 

现在,通过SQLiteOpenHelper使用jOOQ的包装器类的源代码。 通常,没有它可以做到这一点,但是(我认为)安全使用第一个和第二个API会更加方便。


 class DatabaseAdapter(private val context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { companion object { private const val DATABASE_NAME = "database" private const val DATABASE_VERSION = 1 @JvmStatic private val OPEN_OPTIONS = mapOf( "cache" to "shared", "journal_mode" to "WAL", "synchronous" to "ON", "foreign_keys" to "ON") } val connectionLock: ReentrantLock = ReentrantLock(true) val configuration: Configuration by lazy(mode = LazyThreadSafetyMode.NONE) { connectionLock.withLock { // ensure the database exists, // all upgrades are performed, // and connection is ready to be set val database = context.openOrCreateDatabase( DATABASE_NAME, Context.MODE_PRIVATE, null) if (database.isOpen) { database.close() } // register SQLDroid driver to be used for establishing connections // with our database DriverManager.registerDriver( Class.forName("org.sqldroid.SQLDroidDriver") .newInstance() as Driver) DefaultConfiguration() .set(SQLiteSource( context, OPEN_OPTIONS, "database", arrayOf("databases"))) .set(SQLDialect.SQLITE) } } override fun onCreate(db: SQLiteDatabase) { // acquire monitor until the database connection is created // this is important as otherwise transactions might be tryingg to run // concurrently that will lead to crashes connectionLock.withLock { // TODO: Create tables } } override fun onOpen(db: SQLiteDatabase) { // acquire monitor until the database connection is established // this is important as otherwise transactions might be tryingg to run // concurrently that will lead to crashes connectionLock.withLock { super.onOpen(db) } } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // acquire monitor until the database is upgraded // this is important as otherwise transactions might be tryingg to run // concurrently that will lead to crashes connectionLock.withLock { } } infix inline fun <reified T> transaction(noinline f: (Configuration) -> T): Observable<T> = Observable.create { emitter -> val tryResult = Try { connectionLock.withLock { DSL.using(configuration).transactionResult(f) } } when (tryResult) { is Try.Success -> { emitter.onNext(tryResult.value) emitter.onComplete() } is Try.Failure -> { emitter.onError(tryResult.exception) } } } fun invalidate() { connectionLock.withLock { // TODO: Drop tables, vacuum and create tables } } private class SQLiteSource(val context: Context, val options: Map<String, String>, val database: String, val fragments: Array<out String>): DroidDataSource() { override fun getConnection(): Connection = openConnection(options) private fun openConnection(options: Map<String, String> = emptyMap()): Connection { return DriverManager.getConnection(StringBuilder().apply { append("jdbc:sqldroid:") append(context.applicationInfo.dataDir) append("/") append(buildFragments(fragments)) append(database) append("?") append(buildOptions(options)) }.toString()) } private fun buildFragments(fragments: Array<out String>) = when (fragments.isEmpty()) { true -> "" false -> "${fragments.joinToString("/")}/" } private fun buildOptions(options: Map<String, String>) = options.mapTo(mutableListOf<String>()) { entry -> "${entry.key}=${entry.value}" } .joinToString(separator = "&") } } 

UPD:向懒惰的初始化添加了mode = LazyThreadSafetyMode.NONE ,谢谢konstantin_berkow


而不是结论


事实证明,在Android中设置jOOQ并不是一个复杂的过程。 一次就足够了,然后您就可以安全地从旧项目中进行复制粘贴了。


并给使用jOOQ的人带来一笔小小的奖金。 从示例中可以看到,在打开连接时,将使用缓存模式。 什么是tsimes? Android SDK SQLite API不提供以这种模式使用数据库的功能,这极大地限制了我们在应用程序中进行进程间通信的组织。 现在-您可以安全地使用此模式,它本身可以用作过渡到此出色框架的原因。

Source: https://habr.com/ru/post/zh-CN422303/


All Articles