android - 关于为与 Room 中的另一个实体具有一对多关系的实体插入记录的问题

标签 android android-sqlite rx-java2 android-room

我目前正在为我的应用程序数据库使用 Android Room,并且我有一个名为 Transaction 的现有实体, 这是它的一个简化版本:

@Entity(tableName = "transactions", indices = {@Index(value = "id", unique = true)})
public class Transaction {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int txnId;

    private String type;
}

我需要添加一个名为 Product 的对象到 Transaction目的。每个Transaction对象可以有多个 Products .根据我读过的内容,我定义了我的 Product有一个对应于 txnId 的外键我的Transaction对象:

@Entity(tableName = "products",
    foreignKeys = @ForeignKey(entity = Transaction.class,
    parentColumns = "id",
    childColumns = "txn_id",
    onDelete = CASCADE))
public class Product {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int productId;

    private String productCode;

    private int quantity;

    @ColumnInfo(name = "txn_id")
    private int txnId;
}

我认为为了获得所有 ProductsTransaction 相关联记录一下,我在我的ProductDao中做了如下方法界面:

@Query("SELECT * FROM products WHERE txn_id LIKE :txn_id")
Observable<List<Product>> getProductsByTxnId(int txn_id);

现在我的问题来了:例如,如果我插入一个 Transaction到数据库,我将如何添加多个 Products为此Transaction ?我需要捕获 id 吗?对于那个特定的 Transaction ,设置为对应的txn_id在我的 Product对象之前插入它,或者如果你设置了 Product,Room 会自动正确地插入东西吗?实体有 foreignKey引用 Transaction类(class)?

最佳答案

how would I add multiple Products for that Transaction?

您可以使用映射/关联/关系(和其他名称)表(实体)来反射(reflect)多对多关系(即许多交易可以用于(与)单个产品,许多产品可以由一个产品使用单笔交易)。

这是一个主要包含 2 列的表格,一列用于与一个部分(交易)相关(唯一标识)的值,另一列用于与另一部分(产品)相关的值。

假设交易 1产品 2、3、4、5 和 6,以及交易 2 具有产品 1、3、5、7、9

映射表将包含:-

1 2
1 3
1 4
1 5
1 6
2 1
2 3
2 9
2 7
2 5

Do I need to grab a hold of the id for that particular Transaction, set it to the corresponding txn_id in my Product object before inserting it, or does Room automatically insert things correctly if you have setup the Product entity to have a foreignKey referencing the Transactionclass?

关于 SQLite 外键(Room or not),它只是定义了一个约束(规则),即子列值中的值必须是父列中的值。

没有执行确定值的魔术(如果您考虑一下,它怎么知道什么与什么相关)。您必须在插入时以编程方式确定值。

所以答案是你需要对 id

进行分级

映射和外键使用示例

也许考虑以下:-

DROP TABLE IF EXISTS Map1;
DROP TABLE IF EXISTS Map2;
DROP TABLE IF EXISTS Transactions;
DROP TABLE IF EXISTS Products;

CREATE TABLE IF NOT EXISTS Transactions (id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE IF NOT EXISTS Products (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Transactions (name) VALUES('Take'),('Give'),('Buy'),('Sell');
INSERT INTO Products(name) VALUES('Tea'),('Coffee'),('Water'),('Beer');
/* Mapping table without foreign keys */
CREATE TABLE IF NOT EXISTS Map1 (
    txn_id INTEGER,
    prd_id INTEGER, 
    UNIQUE(txn_id,prd_id) /* <- prevents duplication */
);
/* Mapping Table with foreign keys and cascading deletes and updates */
CREATE TABLE IF NOT EXISTS Map2 (
    txn_id INTEGER 
        REFERENCES Transactions(id) 
            ON DELETE CASCADE /* if parent is deleted then all children that map to that parent are deleted (only the maps rows)  */
            ON UPDATE CASCADE, /* like wise for updates (only if the id is updated) */
    prd_id INTEGER 
        REFERENCES Products(id) 
            ON DELETE CASCADE 
            ON UPDATE CASCADE, 
    UNIQUE(txn_id,prd_id)
);
INSERT INTO Map1 VALUES(1,1),(1,4),(2,3),(2,2),(3,1),(3,2),(3,3),(3,4),(4,2);
INSERT INTO Map2 VALUES(1,1),(1,4),(2,3),(2,2),(3,1),(3,2),(3,3),(3,4),(4,2);

/*Result 1 Mapped via Map1 table without foreign keys */
SELECT Transactions.name AS TransactionName, Products.name AS ProductName, 
    'Transaction '||txn_id||' maps to Product '||prd_id AS mapping
FROM Transactions 
JOIN Map1 ON Transactions.id = Map1.txn_id
JOIN Products ON Map1.prd_id = Products.id
;

/* Result 2 Mapped via Map2 table that has Foreign keys (no difference) to Result1 */
SELECT Transactions.name AS TransactionName, Products.name AS ProductName,
    'Transaction '||txn_id||' maps to Product '||prd_id AS mapping
FROM Transactions 
JOIN Map2 ON Transactions.id = Map2.txn_id
JOIN Products ON Map2.prd_id = Products.id
;

/* Add a rouge mapping entry to Map1 */
INSERT INTO Map1 VALUES (5,6); /* oooops niether transaction or Product exists */

/* Result 3 no issues with useless rouge entry */
SELECT Transactions.name AS TransactionName, Products.name AS ProductName, 
    'Transaction '||txn_id||' maps to Product '||prd_id AS mapping
FROM Transactions 
JOIN Map1 ON Transactions.id = Map1.txn_id
JOIN Products ON Map1.prd_id = Products.id
;

/* Try adding rouge entry to Map 2. Does not work */
INSERT INTO Map2 VALUES (5,6); /* oooops niether transaction or Product exists */
;

代码:-

  • 删除现有表(如果存在)(注意 FK 的顺序)
  • 创建交易表和产品表。
  • 向 Transactions 和 products 表添加一些数据。
  • 不使用外键创建 Map1 映射表。
    • 即它们不是必需的。
  • 使用外键创建 Map2 映射表。
  • 加载具有相同数据的映射表。
  • 根据/使用映射表 Map1 提取数据产生结果 1。
  • 根据/usinf映射表Map2提取数据产生结果2。
    • 与结果 1 相同。
  • 插入一个 rouge 行 t0 Map1,试图将 id 为 5 的不存在的 Transaction 映射到 id 为 6 的不存在的 Product
  • 像以前一样从 Map1 中提取数据
    • 相同,即 rouge(无用)行被忽略
  • 向 Map2 插入一个 rouge 行,由于外键约束而失败。

结果

enter image description here

enter image description here

enter image description here

随着消息:-

INSERT INTO Map2 VALUES (5,6)
> FOREIGN KEY constraint failed
> Time: 0s

关于android - 关于为与 Room 中的另一个实体具有一对多关系的实体插入记录的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57090665/

相关文章:

android - 如何从 SQLite 将一个用户数据查看到 TextView 中?

android - Sqlite 数据库搜索中的空格导致问题

android - 如何使用 RxJava2 使用 IntentService 线程

android - react 原生 : How to open OS settings in Android and iOS?

android - 如何在 Android Studio 中完全禁用 NDK 支持

android - com.android.billingclient :billing-ktx dispatch to Dispatchers. IO 中的 kotlin 扩展在内部起作用吗?

android - 将 Green DAO 与内容提供者一起使用

java - 为什么 rx-java 邮政编码失败

rx-java - 我应该使用响应式(Reactive)编程(RxJava)来解决复杂问题吗?

android - 是否有用于编写 iOS、Android(甚至 Windows Phone)的单一框架?