将多个项目添加到 SQLite 数据库 Android
Posted
技术标签:
【中文标题】将多个项目添加到 SQLite 数据库 Android【英文标题】:Adding Multiple Items to SQLite Database Android 【发布时间】:2021-10-30 07:23:21 【问题描述】:我正在创建一个 android 食品配送应用程序,其中菜单项需要在添加到购物车时添加到数据库中。为此,我使用了带有 SQLiteAssethelper 库的 SQL 数据库。
现在的问题:
请仔细理解。
在我的应用程序中,有一些菜单项可以添加一半和完整选项。就像,Chowmin 有一半和完整的选项,有不同的价格。现在的问题是,假设用户想要订购 1.5 盘炒面,这意味着 1 盘全盘和半盘。 但是,每当用户首先单击一半或全部并将它们添加到购物车时,它首先会根据用户的输入添加项目。 但是,当同一用户尝试单击另一个添加额外数量的选项时,数据库中先前添加的项目会更新为添加的数量。
这就是问题所在。这不是我想要的结果。我想为同一个菜单项添加两个单独的选项。下面是我的 SQL 数据库类。请帮忙。 这是唯一阻止我完成梦想项目的事情。
下面我将给出用于添加、更新和检查数据库中项目的函数。如果还有什么想要的,请评论。
Database.java
// 向数据库添加项目的函数
public void addToCart(Order order)
SQLiteDatabase db = getReadableDatabase();
String query = String.format("INSERT OR REPLACE INTO CartDetail(UserPhone,ProductId,ProductName,Quantity,Price,HalfPrice,FullPrice,Half,Full,ResId,Image) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
order.getUserPhone(),
order.getProductId(),
order.getProductName(),
order.getQuantity(),
order.getPrice(),
order.getFullPrice(),
order.getHalfPrice(),
order.getFull(),
order.getHalf(),
order.getResId(),
order.getImage());
db.execSQL(query);
//获取所有项目的函数
public List<Order> getCarts(String userPhone)
SQLiteDatabase db = getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String[] sqlSelect = "UserPhone", "ProductName", "ProductId", "Quantity", "Price", "HalfPrice", "FullPrice", "Half", "Full", "ResId", "Image";
String sqlTable = "CartDetail";
qb.setTables(sqlTable);
Cursor c = qb.query(db, sqlSelect, "UserPhone=?", new String[]userPhone, null, null, null);
final List<Order> result = new ArrayList<>();
if (c.moveToFirst())
do
result.add(new Order(
c.getString(c.getColumnIndex("UserPhone")),
c.getString(c.getColumnIndex("ProductId")),
c.getString(c.getColumnIndex("ProductName")),
c.getString(c.getColumnIndex("Quantity")),
c.getString(c.getColumnIndex("Price")),
c.getString(c.getColumnIndex("HalfPrice")),
c.getString(c.getColumnIndex("FullPrice")),
c.getString(c.getColumnIndex("Half")),
c.getString(c.getColumnIndex("Full")),
c.getString(c.getColumnIndex("ResId")),
c.getString(c.getColumnIndex("Image"))
));
while (c.moveToNext());
return result;
//数据库中物品的更新和增加数量的功能
public void updatecart(Order order)
SQLiteDatabase db = getReadableDatabase();
String query = String.format("UPDATE CartDetail SET Quantity = '%s' WHERE UserPhone = '%s'
AND ProductId='%s'", order.getQuantity(), order.getUserPhone(), order.getProductId());
db.execSQL(query);
//增加购物车物品
public void increatecart(int itemcount, String userPhone, String foodId)
SQLiteDatabase db = getReadableDatabase();
@SuppressLint("DefaultLocale") String query = String.format("UPDATE CartDetail SET
Quantity = Quantity+'%d' WHERE UserPhone = '%s' AND ProductId='%s'", itemcount, userPhone,
foodId);
db.execSQL(query);
【问题讨论】:
请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example。 【参考方案1】:您的 WHERE 子句似乎没有唯一标识单个 CartDetail 行,如果有半价和全价订单的组合,因此会更新所有符合用户电话和 Productid 条件的行,当它看起来好像应该使用另一列来区分要更新的是半价行还是全价行。
但是,在没有明确说明如何使用这些列的情况下,以上只是一个假设。可能是您对各自的数量使用完整列和半列,在这种情况下,应更新各自的列,并且数量列似乎是多余的。
关于冗余问题,您似乎有一个 productid,它表明可能有一个产品表将/应该包含产品特定数据,并且您可能会不必要地复制该数据。换句话说,您要考虑规范化。
非常基于您的列,我建议考虑更改架构和使用两个表(如果还没有两个表)。例如,也许考虑以下内容:-
一个产品表,其中包含产品特定数据,例如产品名称(例如炒面 ....)、唯一标识符 (id)、全价和半价(满足半价不减半)及图片信息等其他信息。
还有一个 CartDetail 表,其中包含购物车的详细信息,这可以是每个产品的单行并相应更新该行,也可以是最终形成整个产品订单的行(其他行用于其他产品)。
后者似乎更复杂,但它确实允许更详细地查看订单(如有必要)。产品表可以基于以下架构:-
CREATE TABLE IF NOT EXISTS product (productid INTEGER PRIMARY KEY, productname TEXT UNIQUE, fullprice REAL, halfprice REAL, resid INTEGER, productimage BLOB);
为了测试/演示,我们可以使用 :-
添加 3 个产品INSERT INTO product
VALUES
(1,'chow mein', 10.00, 6.00,12345678, null),
(2,'peking duck',12.50,8.00,23456789,null),
(3,'honey chicken',11.50,7.25,34567890,null)
;
请注意,半价实际上并不是半价,而是高于半价的情况。
演示中不考虑残差和图像。
CartDetail 表可以基于以下架构:-
CREATE TABLE IF NOT EXISTS cartdetail (cartdetailid INTEGER PRIMARY KEY, userphone TEXT, productid, quantityfull INTEGER, quantityhalf INTEGER);
cartdetailid 可用于唯一标识详细信息,在演示中未使用。但是,拥有该列并没有什么坏处,因为它是 rowid 列(通常是隐藏列)的别名,因此没有开销。
如您所见,列已减少,产品信息全部通过 productid 检索,如下所示。
第一个演示展示了在更复杂/复杂的事务方法中使用上述方法,即添加行以调整顺序。
例如,初始订单可以是:-
INSERT INTO cartdetail (userphone,productid,quantityfull,quantityhalf)
VALUES ('0000000000',1,1,1) /* initial order of 1.5 chow meins aka 1 full and 1 half*/
;
评论说明
然后可以进行调整(这里对初始订单进行了3次调整,添加了2个新产品并调整了原来的):-
这里使用 :-
进行了一次调整/* one way by adding to cart i.e. adding additional details */
INSERT INTO cartdetail (userphone,productid,quantityfull,quantityhalf)
VALUES ('0000000000',1,0,3); /* update by adding 3 half chowmeins */
所以现在代替原来的 1 半餐有 4 半餐(1 现有加上 3 新)。
然后:-
INSERT INTO cartdetail (userphone,productid,quantityfull,quantityhalf)
VALUES ('0000000000',1,0,-1) /* removes one half meal */,
('0000000000',3,1,0) /* add 1 full honey chicken */,
('0000000000',2,0,1) /* and also 1 half peking duck */
;
在 Android 上,这些很可能是单独的 INSERTS
要获取产品详细信息,您可以加入这两个表,例如使用:-
SELECT * FROM cartdetail JOIN product ON product.productid = cartdetail.productid;
结果:-
但是,从整体的角度来看,上述内容并不是那么用户友好,更用户友好/信息查询可能是:-
SELECT
userphone,
productname,
sum(quantityfull) + (CAST(sum(quantityhalf) AS REAL) / 2) AS total,
sum(quantityfull) AS fullMeals,
sum(quantityhalf) AS halfmeals,
(sum(quantityfull) * fullprice) + (sum(quantityhalf) * halfprice) AS totalprice
FROM cartdetail JOIN product ON product.productid = cartdetail.productid
GROUP BY cartdetail.productid
;
这将产生以下结果:-
请注意,结果游标中的列名将根据 AS 子句(使用时),因此游标将具有列:-
userphone、productname、total、fullmeals、halfmeals 和 totalprice第二个演示,使用相同的结构/模式,使用对现有产品的更新,这更接近于您正在做的事情。
首先,第一次演示中的购物车详细信息全部使用 :-
清除/* Clear the cart */
DELETE FROM cartdetail;
初始订单使用 :-
/* add initial order */
INSERT INTO cartdetail (userphone,productid,quantityfull,quantityhalf)
VALUES ('0000000000',1,0,3) /* update by adding 3 half chowmeins */,
('0000000000',3,1,0) /* add 1 full honey chicken */,
('0000000000',2,0,1) /* and also 1 half peking duck */
;
使用与第一个演示(第二个查询)完全相同的查询,结果是:-
可以通过更新产品的行来进行调整,而不是添加行(交易),例如通过使用:-
/* Now increase the full chow mein's by 1 */
UPDATE cartdetail
SET
quantityfull = quantityfull + 1 /*?? number of full meals to change by (could be negative)*/,
quantityhalf = quantityhalf + 0 /*?? half meal adjustment amount */
WHERE userphone = '0000000000' AND productid = 1 /*?? product to adjust */
;
使用相同的查询(第二个查询)然后你得到:-
以上是转换为 Android 作为演示应用程序。两个类,一个包含所有方法的 DatabaseHelper,以及一个用于演示的活动 MainActivity。
数据库助手:-
class DatabaseHelper extends SQLiteOpenHelper
public static final String DBNAME = "thedatabase.db";
private static final int DBVERSION = 1;
private static volatile DatabaseHelper instance = null;
SQLiteDatabase db;
private DatabaseHelper(Context context)
super(context, DBNAME, null, DBVERSION);
db = this.getWritableDatabase();
public static DatabaseHelper getInstance(Context context)
if (instance == null)
instance = new DatabaseHelper(context);
return instance;
@Override
public void onCreate(SQLiteDatabase db)
db.execSQL(CartDetail.createSQL);
db.execSQL(Product.createSQL);
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1)
// Product Table Methods
public long addProduct(
String productName,
Float fullPrice,
Float halfPrice,
int resourceId,
byte[] image
)
ContentValues cv = new ContentValues();
cv.put(Product.NAME_COLUMN,productName);
cv.put(Product.FULLPRICE_COLUMN,fullPrice);
cv.put(Product.HALFPRICE_COLUMN,halfPrice);
cv.put(Product.IMAGE_RESOURCEID_COLUMN,resourceId);
cv.put(Product.IMAGE_COLUMN,image);
return db.insert(Product.TABLENAME,null,cv);
public long addProduct(
String productName,
Float fullPrice,
Float halfPrice
)
return this.addProduct(productName,fullPrice,halfPrice,0,new byte[0]);
public long updateProduct(
long productId,
String newProductName_else_null,
Float newFullPrice_else_null,
Float newHalfPrice_else_null,
Integer newResourceId_else_null, byte[] newImage_else_emptybytearray
)
ContentValues cv = new ContentValues();
if (newProductName_else_null != null)
cv.put(Product.NAME_COLUMN,newProductName_else_null);
if (newFullPrice_else_null != null)
cv.put(Product.FULLPRICE_COLUMN,newFullPrice_else_null);
if (newHalfPrice_else_null != null)
cv.put(Product.HALFPRICE_COLUMN,newHalfPrice_else_null);
if (newResourceId_else_null != null)
cv.put(Product.IMAGE_RESOURCEID_COLUMN,newResourceId_else_null);
if (newImage_else_emptybytearray.length > 0)
cv.put(Product.IMAGE_COLUMN, newImage_else_emptybytearray);
if (cv.size() < 1) return 0; // dont update if nothing to update
return db.update(Product.TABLENAME,cv,Product.ID_COLUMN + "=?",new String[]String.valueOf(productId));
//CartDetail methods
public long adjustQuantity(String userphone, long productid,int fullMealAdjustment, int halfMealAdjustment)
String sql = "UPDATE " + CartDetail.TABLENAME + " SET " + CartDetail.FULL_MEAL_QUANTITY_COLUMN + "=" + CartDetail.FULL_MEAL_QUANTITY_COLUMN + "+?," +
CartDetail.HALF_MEAL_QUANTITY_COLUMN + "=" + CartDetail.HALF_MEAL_QUANTITY_COLUMN + "+? " +
" WHERE " + CartDetail.USERPHONE_COLUMN + "=? AND " + CartDetail.PRODUCT_ID_REFERENCE_COLUMN + "=?;";
db.execSQL(
sql,
new String[]
String.valueOf(fullMealAdjustment),
String.valueOf(halfMealAdjustment),
userphone,
String.valueOf(productid)
);
return 0;
public long insertCartDetail(String userPhone, long productId, int fullMealQuantity, int halfMealQuantity)
ContentValues cv = new ContentValues();
cv.put(CartDetail.USERPHONE_COLUMN,userPhone);
cv.put(CartDetail.PRODUCT_ID_REFERENCE_COLUMN,productId);
cv.put(CartDetail.FULL_MEAL_QUANTITY_COLUMN,fullMealQuantity);
cv.put(CartDetail.HALF_MEAL_QUANTITY_COLUMN,halfMealQuantity);
return db.insert(CartDetail.TABLENAME,null,cv);
public void logOrderDetails(String userphone)
/*
SELECT
userphone,
productname,
sum(quantityfull) + (CAST(sum(quantityhalf) AS REAL) / 2) AS total,
sum(quantityfull) AS fullMeals,
sum(quantityhalf) AS halfmeals,
(sum(quantityfull) * fullprice) + (sum(quantityhalf) * halfprice) AS totalprice
FROM cartdetail JOIN product ON product.productid = cartdetail.productid
GROUP BY cartdetail.productid
*/
/* From clause aka table */
String table = CartDetail.TABLENAME +
" JOIN " + Product.TABLENAME + " ON " + CartDetail.TABLENAME + "." + PRODUCT_ID_REFERENCE_COLUMN + "=" + Product.TABLENAME + "." + Product.ID_COLUMN;
/* Columns */
String[] columns = new String[]
CartDetail.USERPHONE_COLUMN,
Product.NAME_COLUMN,
"sum(" + CartDetail.FULL_MEAL_QUANTITY_COLUMN + ") + (CAST(sum(" + CartDetail.HALF_MEAL_QUANTITY_COLUMN + ") AS REAL) / 2) " +
" AS " + CartDetail.PRODUCT_COMBINDED_QUANTITY,
"sum(" + CartDetail.FULL_MEAL_QUANTITY_COLUMN + ") AS " + CartDetail.FULL_MEAL_QUANTITY_COLUMN,
"sum(" + CartDetail.HALF_MEAL_QUANTITY_COLUMN + ") AS " + CartDetail.HALF_MEAL_QUANTITY_COLUMN,
"(sum(" + CartDetail.FULL_MEAL_QUANTITY_COLUMN + ") * " + Product.FULLPRICE_COLUMN + ") " +
"+ (sum(" + CartDetail.HALF_MEAL_QUANTITY_COLUMN + ") * " + Product.HALFPRICE_COLUMN + ") " +
" AS " + CartDetail.TOTAL_PRODUCT_PRICE_COLUMN,
Product.HALFPRICE_COLUMN,
Product.FULLPRICE_COLUMN,
Product.IMAGE_RESOURCEID_COLUMN,
Product.IMAGE_COLUMN
;
Cursor csr = db.query(
table,columns,
CartDetail.USERPHONE_COLUMN + "=?",
new String[]userphone,
CartDetail.TABLENAME + "." + CartDetail.PRODUCT_ID_REFERENCE_COLUMN,
null,
null
);
while (csr.moveToNext())
Log.d(
"ORDERDETAIL",
"User is " + csr.getString(csr.getColumnIndex(USERPHONE_COLUMN)) +
"\n\tProduct is " + csr.getString(csr.getColumnIndex(Product.NAME_COLUMN)) +
"\n\t\tTotal Meals (as Full) is " + csr.getString(csr.getColumnIndex(CartDetail.PRODUCT_COMBINDED_QUANTITY)) +
"\n\t\t# of Full Meals ordered = " + csr.getString(csr.getColumnIndex(CartDetail.FULL_MEAL_QUANTITY_COLUMN)) +
" @ $" + csr.getString(csr.getColumnIndex(Product.FULLPRICE_COLUMN)) + " per meal" +
"\n\t\t# of Half Meals ordered = " + csr.getString(csr.getColumnIndex(CartDetail.HALF_MEAL_QUANTITY_COLUMN)) +
" @ $" + csr.getString(csr.getColumnIndex(Product.HALFPRICE_COLUMN)) + " per meal" +
"\n\tCombined Cost for " + csr.getString(csr.getColumnIndex(Product.NAME_COLUMN)) + " is $" + csr.getString(csr.getColumnIndex(CartDetail.TOTAL_PRODUCT_PRICE_COLUMN)) +
"\n\t\tetc."
);
csr.close();
class CartDetail
public static final String TABLENAME = "cartdetail";
public static final String ID_COLUMN = BaseColumns._ID;
public static final String TIMESTAMP_COLUMN = "timestamp";
public static final String USERPHONE_COLUMN = "userphone";
public static final String PRODUCT_ID_REFERENCE_COLUMN = "productid";
public static final String FULL_MEAL_QUANTITY_COLUMN = "fullquantity";
public static final String HALF_MEAL_QUANTITY_COLUMN = "halfquantity";
// Derived columns
public static final String TOTAL_PRODUCT_PRICE_COLUMN = "totalproductprice";
public static final String PRODUCT_COMBINDED_QUANTITY = "combinedquantity";
public static final String createSQL = "CREATE TABLE IF NOT EXISTS " + TABLENAME + "(" +
ID_COLUMN + " INTEGER PRIMARY KEY," +
TIMESTAMP_COLUMN + " INTEGER DEFAULT CURRENT_TIMESTAMP, " +
USERPHONE_COLUMN + " TEXT, " +
PRODUCT_ID_REFERENCE_COLUMN + " INTEGER, " +
FULL_MEAL_QUANTITY_COLUMN + " INTEGER, " +
HALF_MEAL_QUANTITY_COLUMN + " INTEGER " +
")";
class Product
public static final String TABLENAME = "product";
public static final String ID_COLUMN = BaseColumns._ID;
public static final String NAME_COLUMN = "productname";
public static final String FULLPRICE_COLUMN = "fullprice";
public static final String HALFPRICE_COLUMN = "halfprice";
public static final String IMAGE_RESOURCEID_COLUMN = "resourceid";
public static final String IMAGE_COLUMN = "image";
public static final String createSQL = "CREATE TABLE IF NOT EXISTS " + TABLENAME + "(" +
ID_COLUMN + " INTEGER PRIMARY KEY, " +
NAME_COLUMN + " TEXT UNIQUE, " +
FULLPRICE_COLUMN + " REAL, " +
HALFPRICE_COLUMN + " REAL, " +
IMAGE_RESOURCEID_COLUMN + " INTEGER, " +
IMAGE_COLUMN + " BLOB " +
")";
和MainActivty下订单然后调整订单,将订单详情写入日志:-
public class MainActivity extends AppCompatActivity
DatabaseHelper db;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = DatabaseHelper.getInstance(this);
long chowmien = db.addProduct("Chow Mien",15.00F,8.50F);
long pekingduck = db.addProduct("Peking Duck",25.00F,14.50F);
long honeychicken = db.addProduct("Honey Chicken",14.00F,8.00F);
String userphone = "0000000000";
db.insertCartDetail(userphone,chowmien,1,3);
db.insertCartDetail(userphone,pekingduck,0,2);
db.insertCartDetail(userphone,honeychicken,2,2);
db.logOrderDetails(userphone);
db.adjustQuantity(userphone,honeychicken,-1,1);
db.logOrderDetails(userphone);
结果根据日志:-
在 1 个全炒面和 3 个半炒面、2 个半北京烤鸭和 2 个全半个蜂蜜鸡的初始订单之后:-
2021-09-02 20:13:31.085 D/ORDERDETAIL: User is 0000000000
Product is Chow Mien
Total Meals (as Full) is 2.5
# of Full Meals ordered = 1 @ $15 per meal
# of Half Meals ordered = 3 @ $8.5 per meal
Combined Cost for Chow Mien is $40.5
etc.
2021-09-02 20:13:31.085 D/ORDERDETAIL: User is 0000000000
Product is Peking Duck
Total Meals (as Full) is 1
# of Full Meals ordered = 0 @ $25 per meal
# of Half Meals ordered = 2 @ $14.5 per meal
Combined Cost for Peking Duck is $29
etc.
2021-09-02 20:13:31.085 D/ORDERDETAIL: User is 0000000000
Product is Honey Chicken
Total Meals (as Full) is 3
# of Full Meals ordered = 2 @ $14 per meal
# of Half Meals ordered = 2 @ $8 per meal
Combined Cost for Honey Chicken is $44
etc.
然后在调整后(将 2 个全和 2 个半蜜鸡减少到 1 个全(-1)和 3 个半):-
2021-09-02 20:13:31.086 D/ORDERDETAIL: User is 0000000000
Product is Chow Mien
Total Meals (as Full) is 2.5
# of Full Meals ordered = 1 @ $15 per meal
# of Half Meals ordered = 3 @ $8.5 per meal
Combined Cost for Chow Mien is $40.5
etc.
2021-09-02 20:13:31.087 D/ORDERDETAIL: User is 0000000000
Product is Peking Duck
Total Meals (as Full) is 1
# of Full Meals ordered = 0 @ $25 per meal
# of Half Meals ordered = 2 @ $14.5 per meal
Combined Cost for Peking Duck is $29
etc.
2021-09-02 20:13:31.087 D/ORDERDETAIL: User is 0000000000
Product is Honey Chicken
Total Meals (as Full) is 2.5
# of Full Meals ordered = 1 @ $14 per meal
# of Half Meals ordered = 3 @ $8 per meal
Combined Cost for Honey Chicken is $38
etc.
【讨论】:
以上是关于将多个项目添加到 SQLite 数据库 Android的主要内容,如果未能解决你的问题,请参考以下文章
如何将 SQLite (SQLite.NET) 添加到我的 C# 项目中
Xamarin SQLite教程Xamarin.iOS项目添加引用