关于为与 Room 中的另一个实体具有一对多关系的实体插入记录的问题
Question about inserting records for an entity that has one-to-many relationship with another entity in 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
定义为具有对应于 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 会自动插入如果您已将 Product
实体设置为具有引用 Transaction
class?
的 foreignKey
,则一切正常
how would I add multiple Products for that Transaction?
您可以使用 mapping/associative/relationship(和其他名称)table(实体)来反映 many-many 关系(即许多交易可以用于(与)单个产品许多产品可由单个交易使用)。
这是一个 table,主要有 2 列,一列用于与一个部分(交易)相关(唯一标识)的值,另一列用于与另一个(产品)相关的值。
假设 交易 1 有 产品 2,3,4,5 & 6,以及具有 产品 1,3,5,7,9
的 交易 2
映射 table 将包含:-
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's
映射和外键使用示例
也许考虑以下:-
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 */
;
代码:-
- 删除现有的 tables 如果它们存在(注意由于 FK 的顺序)
- 创建交易和产品 tables.
- 向交易和产品添加一些数据 tables。
- 不使用外键创建 Map1 映射 table。
- 即它们不是必需的。
- 使用外键创建 Map2 映射 table。
- 加载具有相同数据的映射 table。
- 根据to/using映射提取数据table Map1产生结果1.
- 根据to/usinf映射tableMap2提取数据产生结果2。
- 与结果 1 相同。
- 插入一个 rouge 行 t0 Map1,试图将 ID 为 5 的 non-existing 事务映射到 ID 为 6
的 non-existing 产品
- 像以前一样从 Map1 中提取数据
- 相同,即 rouge(无用)行被忽略
- 向 Map2 插入一个 rouge 行,由于外键约束而失败。
结果
随留言:-
INSERT INTO Map2 VALUES (5,6)
> FOREIGN KEY constraint failed
> Time: 0s
我目前正在为我的应用程序数据库使用 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 会自动插入如果您已将 Product
实体设置为具有引用 Transaction
class?
foreignKey
,则一切正常
how would I add multiple Products for that Transaction?
您可以使用 mapping/associative/relationship(和其他名称)table(实体)来反映 many-many 关系(即许多交易可以用于(与)单个产品许多产品可由单个交易使用)。
这是一个 table,主要有 2 列,一列用于与一个部分(交易)相关(唯一标识)的值,另一列用于与另一个(产品)相关的值。
假设 交易 1 有 产品 2,3,4,5 & 6,以及具有 产品 1,3,5,7,9
的 交易 2映射 table 将包含:-
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's
映射和外键使用示例
也许考虑以下:-
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 */
;
代码:-
- 删除现有的 tables 如果它们存在(注意由于 FK 的顺序)
- 创建交易和产品 tables.
- 向交易和产品添加一些数据 tables。
- 不使用外键创建 Map1 映射 table。
- 即它们不是必需的。
- 使用外键创建 Map2 映射 table。
- 加载具有相同数据的映射 table。
- 根据to/using映射提取数据table Map1产生结果1.
- 根据to/usinf映射tableMap2提取数据产生结果2。
- 与结果 1 相同。
- 插入一个 rouge 行 t0 Map1,试图将 ID 为 5 的 non-existing 事务映射到 ID 为 6 的 non-existing 产品
- 像以前一样从 Map1 中提取数据
- 相同,即 rouge(无用)行被忽略
- 向 Map2 插入一个 rouge 行,由于外键约束而失败。
结果
随留言:-
INSERT INTO Map2 VALUES (5,6)
> FOREIGN KEY constraint failed
> Time: 0s