【问题标题】:Question about inserting records for an entity that has one-to-many relationship with another entity in Room关于在 Room 中为与另一个实体具有一对多关系的实体插入记录的问题
【发布时间】:2019-07-18 08:55:38
【问题描述】:

我目前正在为我的应用程序数据库使用 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 定义为具有与Transaction 对象的txnId 对应的外键:

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

我认为为了获取与Transaction 记录关联的所有Products,我在ProductDao 界面中创建了以下方法:

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

现在我的问题来了:例如,如果我在数据库中插入一个Transaction,我将如何为那个Transaction 添加多个Products?我是否需要为那个特定的Transaction 抓住id,在插入它之前将它设置为我的Product 对象中的相应txn_id,或者如果你设置了@,Room 会自动正确插入东西吗? 987654342@ 实体有一个 foreignKey 引用 Transactionclass?

【问题讨论】:

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


    【解决方案1】:

    如何为该交易添加多个产品?

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

    这是一个主要包含 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
    

    我是否需要获取该特定交易的 id, 之前将其设置为我的 Product 对象中的相应 txn_id 插入它,或者 Room 会自动正确插入东西,如果 您已将 Product 实体设置为具有引用 事务类?

    关于 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。
    • 根据/使用映射表 Map2 提取数据,生成结果 2。
      • 与结果 1 相同。
    • 插入 rouge 行 t0 Map1,尝试将 id 为 5 的不存在 Transaction 映射到 id 为 6 的不存在 Product
    • 像以前一样从 Map1 中提取数据
      • 相同,即忽略胭脂(无用)行
    • 向 Map2 插入 rouge 行,由于外键约束而失败。

    结果

    带有消息:-

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

    【讨论】:

    • 非常感谢您的深入回复。
    猜你喜欢
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 2016-01-13
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多