我目前正在为我的应用程序数据库使用 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;
}
我认为为了获得所有 Products
与 Transaction
相关联记录一下,我在我的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 行,由于外键约束而失败。
结果
随着消息:-
INSERT INTO Map2 VALUES (5,6)
> FOREIGN KEY constraint failed
> Time: 0s
关于android - 关于为与 Room 中的另一个实体具有一对多关系的实体插入记录的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57090665/