RoomDB - Can you JOIN tables from different databases(RoomDB-您可以连接来自不同数据库的表吗)
问题描述
我想知道RoomDB是否支持来自不同数据库的两个表的联接。
假设我有以下实体和数据库:
@Entity
data class Foo(
@PrimaryKey
val fooId: Long,
val barId: Long,
val fooText: String
)
@Dao
interface FooDao {
....
}
@Database(
entities = [Foo::class],
version = 1,
exportSchema = false
)
abstract class FooDatabase : RoomDatabase() {
abstract fun fooDao() : FooDao
}
val fooDatabase = Room
.databaseBuilder(application, FooDatabase::class.java, "Foo.db")
.fallbackToDestructiveMigration()
.build()
@Entity
data class Bar(
@PrimaryKey
val id: Long,
val barText: String
)
@Dao
interface BarDao {
....
}
@Database(
entities = [Bar::class],
version = 1,
exportSchema = false
)
abstract class BarDatabase : RoomDatabase() {
abstract fun barDao() : BarDao
}
val barDatabase = Room
.databaseBuilder(application, BarDatabase::class.java, "Bar.db")
.fallbackToDestructiveMigration()
.build()
data class FooWithBar(
@Embedded
val foo: Foo,
@Relation(
parentColumn = "barId",
entityColumn = "id"
)
val bar: Bar
)
如果Foo
表与Bar
表位于不同的数据库中,是否可以编写可以获取联接模型FooWithBar
的查询?
我知道如果我在同一个数据库中同时有Foo
和Bar
实体,我可以在DAO中编写如下查询:
@Query("SELECT * FROM foo")
suspend fun getFooWithBar() : FooBar?
编译器将根据注释生成一个SQL查询,该查询将通过Foo.barId -> Bar.id
关系连接两个表(Foo
和Bar
)。
但我不知道是否可以跨不同数据库中的表执行这样的联接。
我知道如果我将这些表托管在同一个数据库中,我可以做到这一点,但我希望将我的数据库分开。
我要保留数据库的隔离是否表示有气味(&Q;&Q;)?
是否取决于域模型?如果是这样的话,对于何时将域模型分成不同的数据库,有什么好的经验法则呢?
推荐答案
我要保留数据库的隔离是否表示有气味(&Q;&Q;)?
是的,特别是在房间里。它引入了复杂性和低效。
如果Foo表驻留在与Bar表不同的数据库中,是否可以编写可以获取连接模型FooWithBar的查询?
对于您的简单示例是的,但是从示例中可以看出,您没有使用实际的SQL连接(没有附加),也就是说,您获得了foo对象并通过获取适当的条(或多个条)来模拟连接。
如果您尝试混合来自不同数据库的实体,则会遇到问题,例如
即使添加(在编辑时)DAO没有问题,例如:-
@Query("SELECT * FROM foo JOIN bar ON foo.barId = bar.id")
fun getAllFooWithBar(): List<FooWithBar>
根据(Android Studio截图):-
编译时会出现如下错误:-
E:AndroidStudioAppsSO67981906KotlinRoomDateappuild mpkapt3stubsdebugaaso67981906kotlinroomdateFooDao.java:22: error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such table: bar)
public abstract void getAllFooWithBar();
^E:AndroidStudioAppsSO67981906KotlinRoomDateappuild mpkapt3stubsdebugaaso67981906kotlinroomdateFooDao.java:22: error: Not sure how to convert a Cursor to this method's return type (void).
public abstract void getAllFooWithBar();
在单个查询中获取需要两个数据库中的表的任何内容将超出Room的作用域,因为每个数据库只知道自己的表。
但是,如果您将一个数据库附加到另一个数据库,那么您将在一个数据库中同时拥有两个数据库,但是Room无法理解它。因此,您基本上必须恢复使用SupportSQLiteDatabase(类似于使用原生Android SQlite(但有一些限制))。示例(非常简单)
foo实体
@Entity
data class Foo(
@PrimaryKey
val fooId: Long?,
val barId: Long,
val fooText: String
)
- 基本相同
FooDao
@Dao
interface FooDao {
@Insert
fun insert(foo: Foo): Long
@Query("SELECT * FROM foo")
fun getAllFoos(): List<Foo>
@Query("SELECT * FROM foo WHERE fooId=:fooId")
fun getFooById(fooId: Long): Foo
/* !!!!NO GO!!!!
@Query("SELECT * FROM foo JOIN bar ON foo.barId = bar.id")
fun getAllFooWithBar(): List<FooWithBar>
*/
}
- 一些简单的DAO
FooDatabase
@Database(
entities = [Foo::class],
version = 1,
exportSchema = false
)
abstract class FooDatabase : RoomDatabase() {
abstract fun fooDao() : FooDao
fun attachBar(context: Context): Boolean {
var rv: Boolean = false
if (instance != null) {
val dbs = this.openHelper?.writableDatabase
val barpath = context.getDatabasePath(BarDatabase.DBNAME)
if (dbs != null) {
dbs.execSQL("ATTACH DATABASE '$barpath' AS $BAR_SCHEMA_NAME")
rv = true
}
}
return rv
}
fun closeInstance() {
if(instance == null) return
if (this.isOpen()) {
this.close()
}
instance = null
}
companion object {
@Volatile
private var instance: FooDatabase? = null
fun getInstanceWithForceOption(context: Context, forceReopen: Boolean = false): FooDatabase {
if (forceReopen) instance?.closeInstance()
if (instance == null) {
instance = Room.databaseBuilder(context,FooDatabase::class.java, DBNAME)
.allowMainThreadQueries()
.addCallback(FOO_CALLBACK)
.build()
}
return instance as FooDatabase
}
fun getInstance(context: Context): FooDatabase {
return getInstanceWithForceOption(context, false)
}
val FOO_CALLBACK = object: RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
}
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
}
}
const val DBNAME: String = "foo.db"
const val BAR_SCHEMA_NAME = "bar_schema"
}
}
- 如果始终通过Attach访问,则不使用回调,但可以在onOpen中执行Attach
条形图实体
@Entity
data class Bar(
@PrimaryKey
val id: Long?,
val barText: String
)
- 基本相同
条刀
@Dao
interface BarDao {
@Insert
fun insert(bar: Bar): Long
@Query("SELECT * FROM bar")
fun getAllBars(): List<Bar>
@Query("SELECT * FROM Bar WHERE id=:id")
fun getBarById(id: Long): Bar
}
BarDatabase
@Database(
entities = [Bar::class],
version = 1,
exportSchema = false
)
abstract class BarDatabase : RoomDatabase() {
abstract fun barDao() : BarDao
fun closeInstance() {
if (this.isOpen()) {
this.close()
}
instance = null
}
companion object {
@Volatile
private var instance: BarDatabase? = null
fun getInstanceWithForceOption(context: Context, forceReopen: Boolean = false): BarDatabase {
if (forceReopen) instance?.closeInstance()
if (instance == null) {
instance = Room.databaseBuilder(context,BarDatabase::class.java, DBNAME)
.allowMainThreadQueries()
.addCallback(BAR_CALLBACK)
.build()
}
return instance as BarDatabase
}
fun getInstance(context: Context): BarDatabase {
return getInstanceWithForceOption(context, false)
}
val BAR_CALLBACK = object: RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
}
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
}
}
const val DBNAME: String = "bar.db"
}
}
- 再次回调不执行任何操作
FooWithBar
class FooWithBar {
var foo: Foo
var bar: Bar
constructor(fooId: Long, fooDao: FooDao, barDao: BarDao) {
this.foo = fooDao.getFooById(fooId)
this.bar = barDao.getBarById(foo.barId)
}
}
- 因为您不能同时获取Foo和Bar,所以这相当于通过通过FooDatabase获取Foo,然后通过BarDatabase获取关联的Bar来执行联接。
主要活动总而言之:-
class MainActivity : AppCompatActivity() {
lateinit var foodb: FooDatabase
lateinit var fooDao: FooDao
lateinit var bardb: BarDatabase
lateinit var barDao: BarDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
foodb = FooDatabase.getInstance(this)
fooDao = foodb.fooDao()
bardb = BarDatabase.getInstance(this)
barDao = bardb.barDao()
/* Add some data */
fooDao.insert(Foo(null,barDao.insert(Bar(null,"BAR1")),"FOO1"))
barDao.insert(Bar(null,"BAR UNUSED"))
fooDao.insert(Foo(null,barDao.insert(Bar(null,"BAR2")),"FOO2"))
/* Get equivalent of join (simple) using the FooWithBar */
val allFoosWithBars = mutableListOf<FooWithBar>()
for(foo: Foo in fooDao.getAllFoos()) {
allFoosWithBars.add(FooWithBar(foo.fooId!!,fooDao,barDao))
}
for(fwb: FooWithBar in allFoosWithBars) {
Log.d("FOOBARINFO","Foo is ${fwb.foo.fooText} Bar is ${fwb.bar.barText}")
}
//* Done with the Bar database Room wise
bardb.closeInstance()
foodb.attachBar(this) //<<<<< ATTACHES the Bar database to the Foo
/* Get a Supprort SQLite Database */
var sdb = foodb.openHelper.writableDatabase
/* Query Foo and the attached Bar */
var csr = sdb.query("SELECT * FROM foo JOIN ${FooDatabase.BAR_SCHEMA_NAME}.bar ON foo.barId = ${FooDatabase.BAR_SCHEMA_NAME}.bar.id")
DatabaseUtils.dumpCursor(csr)
csr.close()
}
}
结果
2021-06-16 16:35:04.045 D/FOOBARINFO: Foo is FOO1 Bar is BAR1
2021-06-16 16:35:04.045 D/FOOBARINFO: Foo is FOO2 Bar is BAR2
2021-06-16 16:35:04.092 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@ee9871b
2021-06-16 16:35:04.093 I/System.out: 0 {
2021-06-16 16:35:04.093 I/System.out: fooId=1
2021-06-16 16:35:04.093 I/System.out: barId=1
2021-06-16 16:35:04.093 I/System.out: fooText=FOO1
2021-06-16 16:35:04.093 I/System.out: id=1
2021-06-16 16:35:04.093 I/System.out: barText=BAR1
2021-06-16 16:35:04.093 I/System.out: }
2021-06-16 16:35:04.093 I/System.out: 1 {
2021-06-16 16:35:04.093 I/System.out: fooId=2
2021-06-16 16:35:04.093 I/System.out: barId=3
2021-06-16 16:35:04.093 I/System.out: fooText=FOO2
2021-06-16 16:35:04.093 I/System.out: id=3
2021-06-16 16:35:04.093 I/System.out: barText=BAR2
2021-06-16 16:35:04.094 I/System.out: }
2021-06-16 16:35:04.094 I/System.out: <<<<<
这篇关于RoomDB-您可以连接来自不同数据库的表吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:RoomDB-您可以连接来自不同数据库的表吗
基础教程推荐
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01