如何以干净的方式创建由 ForeignKey 与 RoomDB 相关的行和子项? [英] How can I create row and children related by ForeignKey with RoomDB in a clean way?

查看:28
本文介绍了如何以干净的方式创建由 ForeignKey 与 RoomDB 相关的行和子项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题与我的最后一个问题有某种关系,因为它是同一个项目,但现在我正在尝试又向前迈进了一步.

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

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

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

关于自定义侦听器的事情,这是我最终为不同项目的不同问题选择的解决方案(在这个 老问题).看看那个老问题可能有助于添加一些关于我在 MVVM 架构中被误导的情况的上下文.但是,请注意当前的问题与 WebServices 无关,因为数据库在手机的应用程序中纯粹是本地的,没有外部来源.

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


创建方法"在我的 Repository 类中看起来像这样:

public void insertParent(Parent parent) {新的 InsertParentAsyncTask(parent_dao).execute(parent);}私有静态类 InsertParentAsyncTask 扩展了 AsyncTask{私有最终ParentDao parent_dao;私人 InsertParentAsyncTask(ParentDao parent_dao) {this.parent_dao = parent_dao;}@覆盖受保护的 Void doInBackground(Parent... parents) {parent_dao.insert(父母[0]);返回空;}}

尝试按照 Mario 的回答,我在我父母的 DAO 中更改了此方法:

//旧@插入无效插入(父父);//新(是的,我使用短类型作为父母的 ID)@插入短插入(父级);

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

私有静态类 InsertParentAsyncTask 扩展了 AsyncTask{私有最终ParentDao parent_dao;私人 InsertParentAsyncTask(ParentDao parent_dao) {this.parent_dao = parent_dao;}@覆盖protected Short doInBackground(Parent... parents) {简短的 parent_id;parent_id = parent_dao.insert(parents[0]);返回 parent_id;}@覆盖protected void onPostExecute(Short hanzi_id) {//去做 ???现在怎么办?}}

长话短说

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

//TODO 我知道 AsyncTask 已被弃用//我的 Repository 类使用这个public void insertParentAndChildren(Parent parent, String[] children_list) {new InsertParentAndChildrenAsyncTask(parent_dao, children_list).execute(parent);}私有静态类 InsertParentAndChildrenAsyncTask 扩展了 AsyncTask{私有最终ParentDao parent_dao;私人字符串[] children_list;私人 InsertParentAndChildrenAsyncTask(ParentDao parent_dao, String[] children_list) {this.parent_dao = parent_dao;this.children_list = children_list;}@覆盖protected Short doInBackground(Parent... parents) {简短的 parent_id;Long row_id = parent_dao.insert(parents[0]);parent_id = parent_dao.getIdForRowId(row_id);返回 parent_id;}@覆盖protected void onPostExecute(Short parent_id) {//第二个创建方法"为孩子for (int n = 0; n 

解决方案

我想你可以尝试将 SELECT last_insert_rowid() 作为对房间的查询(你就像这样写,不需要引用任何桌子).此语句返回最后一次插入数据库的 rowid.默认情况下,当您将 rowId 定义为自动增量整数时,大多数 sql 数据库将其用作主键.所以我猜你会在你的 DAO 中定义以下方法

@Query("SELECT last_insert_rowid()")公共抽象 int getLastId()@插入无效插入(父父)

然后您可以在事务中将其与插入语句一起使用.像这样

@Transactionpublic int insertAndGetPrimaryKey(Parent parent){插入(父);返回 getLastId();}

使用事务很重要,否则如果在您的应用程序中多个线程可能同时修改表,则传递的 ID 可能是错误的.

顺便说一句,我不会使用 short 作为主键.它不仅是短 XD(只有 32k 容量),而且标准是真正使用 Integer(40 亿容量).如果这些是 80 年代的 id 就可以了(那些是你在所有 XD 之后保存的 2 个完整字节)但现在一天的内存既便宜又丰富,正如我所说的那样,DB 默认使用整数.

This question is somehow related to my last question, because it is the same project but now I am trying to go one more step forward.

So, in my previous question I only had one table; this time I have two tables: the new second table is supposed to contain related attributes for the rows of the first table, in a OneToMany relationship. So, I store a ForeignKey in the second table that would store the Row ID of the first table's related row (obviously).

The problem is this: the intention is creating both registers (parent and child) at the same time, using the same form, and ParentTable uses AUTO_INCREMENT for his PrimaryKey (AKA ID).

Due to how RoomDb works, I do the creation using a POJO: but after insertion, this POJO won't give me the auto-generated ID as far as I know... so, the only workaround I am able to imagine is, when submitting the form, first make the INSERT for the parent, then using one of the form's fields that created the parent to make some kind of "SELECT * FROM parent_table WHERE field1 LIKE :field1", retrieving the ID, and then use that ID to create the child table's POJO and perform the next INSERT operation. However I feel something's not right about this approach, the last time I implemented something similar this way I ended up with a lot of Custom Listeners and a callback hell (I still have nightmares about that).

About the Custom Listeners thing, it is the solution I ended up choosing for a different problem for a different project (more details about it in this old question). Taking a look to that old question might help adding some context about how misguided I am in MVVM's architecture. However, please notice the current question has nothing to do with WebServices, because the Database is purely local in the phone's app, no external sources.

However, I am wondering: isn't this overkill (I mean the INSERT parent -> SELECT parentID -> INSERT child thing)? Is it inevitable having to do it this way, or there is rather a more clean way to do so?


The "create method" in my Repository class looks like this:

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;
    }
}

Trying to follow Mario's answer, I changed this method in my parent's DAO:

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

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

EDIT2: Now, I am trying to make changes to my Repository's insert AsyncTask, like this:

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?
    }
}

LONG STORY SHORT

It worked for me this way down here, but this ain't clean code (obviously):

// 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);
        }
    }
}

解决方案

I think you could try SELECT last_insert_rowid() as a query on room (you write it just like that no need to reference any table). This statement returns the rowid of the last insertion into your database. By default rowId is what most sql DBs use as primary keys when you define them as auto incremental integers. so I guess you would define the following method in your DAO

@Query("SELECT last_insert_rowid()")
public abstract int getLastId()

@Insert
void insert(Parent parent)

then you can use it together with your insert statement in a transaction. like so

@Transaction
public int insertAndGetPrimaryKey(Parent parent){
    insert(parent);
    return getLastId();
}

it is important to use transaction as else the id delivered could be the wrong one if in your app multiple threads could potentially modify the tables at the same time.

btw I would not use short for a primary key. not only is it short XD (only 32k capacity) but the satndard is really to use Integer (4 bn capacity). if these were the 80's id be all for it (those are 2 entire bytes that you are saving after all XD) but now a days memory is cheap and abundant and as i said integer is what DBs work with by default.

这篇关于如何以干净的方式创建由 ForeignKey 与 RoomDB 相关的行和子项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆