如何以干净的方式创建通过外键与 Room DB 相关的行和子项?

2024-03-25

这个问题在某种程度上与我的最后一个问题 https://stackoverflow.com/q/69384937/3692177,因为这是同一个项目,但现在我正努力向前迈进一步。

所以,在我之前的问题中,我只有一张桌子;这次我有两个表:新的第二个表应该包含第一个表的行的相关属性,处于 OneToMany 关系中。因此,我在第二个表中存储一个外键,该表将存储第一个表的相关行的行 ID(显然)。

问题是这样的:目的是使用相同的形式同时创建两个寄存器(父寄存器和子寄存器),并且 ParentTable 使用 AUTO_INCRMENT 作为其主键(又称为 ID)。

由于 RoomDb 的工作方式,我使用 POJO 进行创建:但插入后,据我所知,该 POJO 不会给我自动生成的 ID...所以,我能想象的唯一解决方法是,提交表单时,首先为父级进行 INSERT,然后使用创建父级的表单字段之一进行某种“SELECT * FROM Parent_table WHERE field1 LIKE :field1”,检索 ID,然后使用该 ID创建子表的POJO并执行下一个INSERT操作。然而,我觉得这种方法有些不对劲,上次我以这种方式实现类似的东西时,我最终得到了很多自定义侦听器和回调地狱(我仍然对此做噩梦)。

关于自定义侦听器的事情,它是我最终为不同项目的不同问题选择的解决方案(更多详细信息请参阅此老问题 https://stackoverflow.com/questions/52442384/how-can-i-make-some-class-running-in-the-main-thread-await-concurrently-for-ot/52493936#52493936)。看一下这个老问题可能有助于添加一些背景信息,说明我在 MVVM 架构中是多么被误导。但是,请注意当前的问题与 WebServices 无关,因为数据库纯粹位于手机应用程序的本地,没有外部来源。

然而,我想知道:这不是矫枉过正吗(我的意思是INSERT parent -> SELECT parentID -> INSERT child事物)?这样做是不可避免的,还是有更干净的方法?


我的存储库类中的“创建方法”如下所示:

public void insertParent(Parent parent) {
    new InsertParentAsyncTask(parent_dao).execute(parent);
}

private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Void> {
    private final ParentDao parent_dao;
    private InsertParentAsyncTask(ParentDao parent_dao) {
        this.parent_dao = parent_dao;
    }

    @Override
    protected Void doInBackground(Parent... parents) {
        parent_dao.insert(parents[0]);
        return null;
    }
}

为了遵循 Mario 的答案,我在父母的 DAO 中更改了这个方法:

// OLD
@Insert
void insert(Parent parent);

// NEW (yes, I use short type for parent's ID)
@Insert
short insert(Parent parent);

EDIT2:现在,我尝试对存储库的插入 AsyncTask 进行更改,如下所示:

private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Short> {
    private final ParentDao parent_dao;
    private InsertParentAsyncTask(ParentDao parent_dao) {
        this.parent_dao = parent_dao;
    }

    @Override
    protected Short doInBackground(Parent... parents) {
        short parent_id;
        parent_id = parent_dao.insert(parents[0]);
        return parent_id;
    }

    @Override
    protected void onPostExecute(Short hanzi_id) {
        // TODO ??? What now?
    }
}

长话短说

它在这里对我有用,但这不是干净的代码(显然):

// TODO I am aware that AsyncTask is deprecated
// My Repository class uses this
public void insertParentAndChildren(Parent parent, String[] children_list) {
    new InsertParentAndChildrenAsyncTask(parent_dao, children_list).execute(parent);
}

private static class InsertParentAndChildrenAsyncTask extends AsyncTask<Parent, Void, Short> {
    private final ParentDao parent_dao;
    private String[] children_list;
    private InsertParentAndChildrenAsyncTask(ParentDao parent_dao, String[] children_list) {
        this.parent_dao = parent_dao;
        this.children_list = children_list;
    }

    @Override
    protected Short doInBackground(Parent... parents) {
        short parent_id;
        Long row_id = parent_dao.insert(parents[0]);
        parent_id = parent_dao.getIdForRowId(row_id);
        return parent_id;
    }

    @Override
    protected void onPostExecute(Short parent_id) {
        // Second "create method" for children
        for (int n = 0; n < children_list.length; n++) {
            Child child = new Child();
            child.setParentId( parent_id );
            child.setMeaning( children_list[n] );
            // My Repository has this method as well
            insertChildStaticMethod(child);
        }
    }
}

你走在正确的轨道上。一种干净的方法是将其包装在如下函数中:

fun saveParent(parent: Parent): Int {
    val rowId = parentDao.insert(parent) // Returns Long rowId
    val parentId = parentDao.getIdForRowId(rowId) // SELECT id FROM table_parent WHERE rowid = :rowId
    return parentId
}

该函数可以是存储库类的一部分,以使其更加干净。 DAO 中的函数可以返回 rowId 和 Parent.ID,如下所示:

@Insert
fun insert(parent: Parent): Long

@Query("SELECT ID FROM table_parent WHERE rowid = :rowId")
fun getIdForRowId(rowId: Long): short

如果您想首先使用基本功能,则可以在使用以下命令构建数据库时在主线程上调用 Room 数据库函数allowMainThreadQueries():

MyApp.database =  Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()

这样,您可以将后台处理推迟到以后。如果您对该主题有具体问题,最好单独提出问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何以干净的方式创建通过外键与 Room DB 相关的行和子项? 的相关文章

  • 中间件 API 的最佳实践是什么? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我们正在开发一个中间件 SDK 采用 C 和 Java 语言 供游戏开发人员 动画软件开发人员 阿凡达开
  • 如何在命令提示符中检查 JAVA_OPTS 值?

    我们的应用程序部署 JBoss 服务器然后抛出错误 PermGen space 然后在 jboss bat 和配置文件中设置 permgen 变量中的 java OPTS JAVA OPTs 中是否有值 assige 如何检查 如何在命令提
  • Oreo(API 26)-drawOverlay + 在状态栏上绘制

    在android oreo中 我无法使用WindowManager LayoutParams TYPE SYSTEM ERROR不再需要并且必须使用WindowManager LayoutParams TYPE APPLICATION OV
  • 更改 RowLayout SWT Java 中元素的顺序

    有没有办法更改在行布局中创建的元素的顺序 我想将其显示在元素中 首先显示 例如 如果我创建 element1 则 element2 element3 element4 我想看到的布局为 元素4 元素3 元素2 元素1 这意味着最后创建的元素
  • 错误:包 com.google.android.gms.appstate 不存在

    由于此错误 无法编译我的 android 项目 BaseGameUtils src main java com google example games basegameutils GameHelper java Error 32 39 e
  • Android 应用程序 Phonegap 中的 Admob 实现

    我将一个 jquery 脚本转换为 Android 应用程序 现在我想将 admob 添加到其中 这可能吗 谷歌从那时起禁止在移动应用程序中使用 AdSense 在移动应用程序中使用 AdSense 移动广告违反了 AdSense 政策 移
  • 删除 ArrayList 对象问题

    我在处理作业时遇到从 ArrayList 中删除对象的问题 如果我使用 正常 for 循环 它的工作原理如下 public void returnBook String isbn for int i 0 i lt booksBorrowed
  • setKeyListener 将覆盖 setInputType 并更改键盘

    大家好 我在两个设备之间遇到问题 在实践中使用InputType和KeyListener我正在操纵一个EditText让它从数字键盘接收逗号和数字 有关更多背景信息 请检查我之前的question https stackoverflow c
  • 如何使用 Hibernate Session.doWork(...) 进行保存点/嵌套事务?

    我正在使用 JavaEE JPA 托管事务与 Oracle DB 和 Hibernate 并且需要实现某种嵌套事务 据我所知 此类事情不受开箱即用的支持 但我应该能够为此目的使用保存点 正如建议的https stackoverflow co
  • 三星 tab2 平板电脑的 Android 开发

    是否可以使用 Eclipse 在 Samsung Tab 2 平板电脑中开发 Android 应用程序 需要安装什么驱动吗 当然可以 你必须安装标准的android开发工具 SDK ADT ad eclipse Juno最新版本 从这里下载
  • 如何将任务添加到 gradle 中的主要“构建”任务

    当我尝试使用以下代码将任务添加到主构建任务时 rootProject tasks getByName build dependsOn mytask 当我跑步时它抱怨gradle w build输出 Where Build file line
  • java中wav文件转换为字节数组

    我的项目是 阿塞拜疆语音的语音识别 我必须编写一个程序来转换wav文件到字节数组 如何将音频文件转换为byte 基本上如第一个答案中的片段所描述 但不是BufferedInputStream use AudioSystem getAudio
  • Listview里面只有一个Element

    您好 我正在尝试将列表视图放入列表视图中的列表视图中 唯一的问题是只有第一个列表视图正确显示所有元素 此后的每个列表视图仅包含一个元素 UPDATE 创建我自己的不可滚动列表视图解决了这个问题 https stackoverflow com
  • Jenkins 管道和 java.nio.file.* 方法的问题

    我正在尝试使用 java nio file 中的方法在 Jenkins 管道中执行一些基本文件操作 无论代码存在于哪个节点块中 代码都在主节点上执行 在管道中 我已经验证了各个节点块都是正确的 它们唯一地标识了特定的节点 但是 pathEx
  • 设计抽象类时是否应该考虑序列化问题?

    一般来说这个问题来自Eclipse建议在抽象类上添加串行版本UID 由于该类是抽象类 因此该类的实例永远不会存在 因此它们永远不会被序列化 只有派生类才会被序列化 所以我的问题是放置一个安全 SuppressWarnings serial
  • 将视图放置在 ConstraintLayout 之外

    我想将视图放置在ConstraintLayout用滑动动画来制作它们的动画 我尝试过设置像这样的约束constraintBottom toTopOf parent 但是View留在容器内 请注意 我希望通过使用内置动画的约束来实现此目的 而
  • java数据结构模拟数据树

    我需要帮助定义使用什么方法 我有一个 SOAP 响应 给我一个 xml 文件 我需要在屏幕上显示 3 个相关列表 当您在第一个列表中选择一个项目时 相应的选择将出现在第二个列表中 依此类推 我只对从 xml 流中提取数据后如何有效地组织数据
  • 如何为 Android Q 打开具有特定专辑或文件夹的默认图库应用程序?

    我尝试打开图库中的特定文件夹 如下代码所示 但它对我不起作用 并且出现错误无法找到物品 fun openDirectoryInGallery context Context directory String val intent Inten
  • 按照说明后“找不到您尝试购买的商品”

    所以我按照以下说明进行操作http developer android com google play billing billing admin html http developer android com google play bi
  • RecyclerView 不调用 onCreateViewHolder 或 onBindView

    没有收到任何错误 所有数据似乎都有效 由于某种原因 没有调用与视图相关的方法 我已确定以下事项 getItemCount 是唯一被调用的适配器方法 并且返回一个正整数值 我知道这将是你们将要查看的区域 构造函数正在被调用 成员变量有效 Pa

随机推荐