-1

I'm trying to create a app of baking sell expenses, where I can input the values of the ingredients used on the baking process individually, and then input the values of each product using the values of the ingredients in each recipe, so I can see how much I can sell then to have a good profit. I have a database and methods working on my first table (Save, Delete, Update, Search...), but I can't make my second table (TABLE_RECEITA) uses the data that already exist from my first table (TABLE_PRODUTO), to complement my recipes.
I want to feed my second table on it's columns with the data from my first table columns, only changing the data of quantity, and value by dividing according to the amount used. I've tried to used foreign key and spinner but was not successful, but there's a high possibility that I did something wrong.

My Database:

package com.myapplication.umdocededaisy;

import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MyDatabase extends SQLiteOpenHelper {

    List<MateriaPrima> listaProduto = new ArrayList<>();
    List<Receita> listaReceita = new ArrayList<>();

    private final Context context;
    private static final String DATABASE_NAME = "BancoDoceDaisy.db";
    private static final int DATABASE_VERSION = 9;

    //Estruturas das Tabelas do banco de dados:

    //Tabela dos produtos - materia prima **(FIRST TABLE)**:
    private static final String TABLE_PRODUTO = "materia_prima";
    private static final String COLUMN_CODIGO = "codigo";
    private static final String COLUMN_PRODUTO = "produto";
    private static final String COLUMN_VALOR = "valor";
    private static final String COLUMN_QTD = "quantidade";
    private static final String COLUMN_TIPO = "tipo";
    //------------------------------------------------------
    //Tabela de receitas/massas - **(SECOND TABLE)**:
    private static final String TABLE_RECEITA = "receitas";
    private static final String COLUMN_TITULO = "titulo";
    private static final String COLUMN_ITEM1 = "item1";
    private static final String COLUMN_ITEM2 = "item2";
    private static final String COLUMN_ITEM3 = "item3";
    private static final String COLUMN_ITEM4 = "item4";
    private static final String COLUMN_ITEM5 = "item5";
    private static final String COLUMN_ITEM6 = "item6";
    private static final String COLUMN_ITEM7 = "item7";
    private static final String COLUMN_ITEM8 = "item8";
    private static final String COLUMN_ITEM9 = "item9";
    private static final String COLUMN_ITEM10 = "item10";
    private static final String COLUMN_ITEM11 = "item11";
    private static final String COLUMN_ITEM12 = "item12";
    private static final String COLUMN_ITEM13 = "item13";
    private static final String COLUMN_ITEM14 = "item14";
    private static final String COLUMN_VALOR1 = "valor1";
    private static final String COLUMN_VALOR2 = "valor2";
    private static final String COLUMN_VALOR3 = "valor3";
    private static final String COLUMN_VALOR4 = "valor4";
    private static final String COLUMN_VALOR5 = "valor5";
    private static final String COLUMN_VALOR6 = "valor6";
    private static final String COLUMN_VALOR7 = "valor7";
    private static final String COLUMN_VALOR8 = "valor8";
    private static final String COLUMN_VALOR9 = "valor9";
    private static final String COLUMN_VALOR10 = "valor10";
    private static final String COLUMN_VALOR11 = "valor11";
    private static final String COLUMN_VALOR12 = "valor12";
    private static final String COLUMN_VALOR13 = "valor13";
    private static final String COLUMN_VALOR14 = "valor14";
    private static final String COLUMN_QTD1 = "qtd1";
    private static final String COLUMN_QTD2 = "qtd2";
    private static final String COLUMN_QTD3 = "qtd3";
    private static final String COLUMN_QTD4 = "qtd4";
    private static final String COLUMN_QTD5 = "qtd5";
    private static final String COLUMN_QTD6 = "qtd6";
    private static final String COLUMN_QTD7 = "qtd7";
    private static final String COLUMN_QTD8 = "qtd8";
    private static final String COLUMN_QTD9 = "qtd9";
    private static final String COLUMN_QTD10 = "qtd10";
    private static final String COLUMN_QTD11 = "qtd11";
    private static final String COLUMN_QTD12 = "qtd12";
    private static final String COLUMN_QTD13 = "qtd13";
    private static final String COLUMN_QTD14 = "qtd14";


    MyDatabase(Context context) {
        super(context, DATABASE_NAME,null, DATABASE_VERSION);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String query = "CREATE TABLE "+ TABLE_PRODUTO +
                " (" + COLUMN_CODIGO + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COLUMN_PRODUTO + " TEXT, " +
                COLUMN_VALOR + " FLOAT, " +
                COLUMN_QTD + " FLOAT, " +
                COLUMN_TIPO + " TEXT); ";
        db.execSQL(query);

        String query2 = "CREATE TABLE "+ TABLE_RECEITA +
                " (" + COLUMN_TITULO + " TEXT PRIMARY KEY, " + COLUMN_ITEM1 + "TEXT," + COLUMN_ITEM2 + " TEXT, " +
                COLUMN_ITEM3 + " TEXT, " + COLUMN_ITEM4 + " TEXT, " + COLUMN_ITEM5 + " TEXT, " + COLUMN_ITEM6 + " TEXT, " +
                COLUMN_ITEM7 + " TEXT, " + COLUMN_ITEM8 + " TEXT, " + COLUMN_ITEM9 + " TEXT, " + COLUMN_ITEM10 + " TEXT, " +
                COLUMN_ITEM11 + " TEXT, " + COLUMN_ITEM12 + " TEXT, " + COLUMN_ITEM13 + " TEXT, " + COLUMN_ITEM14 + " TEXT, " +
                COLUMN_VALOR1 + "FLOAT, " + COLUMN_VALOR2 + "FLOAT, " +COLUMN_VALOR3 + "FLOAT, " + COLUMN_VALOR4 + "FLOAT, " +
                COLUMN_VALOR5 + "FLOAT, " + COLUMN_VALOR6 + "FLOAT, " + COLUMN_VALOR7 + "FLOAT, " + COLUMN_VALOR8 + "FLOAT, " +
                COLUMN_VALOR9 + "FLOAT, " + COLUMN_VALOR10 + "FLOAT, " + COLUMN_VALOR11 + "FLOAT, " + COLUMN_VALOR12 + "FLOAT, " +
                COLUMN_VALOR13 + "FLOAT, " + COLUMN_VALOR14 + "FLOAT, " + COLUMN_QTD1 + "FLOAT, " + COLUMN_QTD1 + "FLOAT, " +
                COLUMN_QTD1 + "FLOAT, " + COLUMN_QTD2 + "FLOAT, " + COLUMN_QTD3 + "FLOAT, " + COLUMN_QTD4 + "FLOAT, " +
                COLUMN_QTD5 + "FLOAT, " + COLUMN_QTD6 + "FLOAT, " + COLUMN_QTD7 + "FLOAT, " + COLUMN_QTD8 + "FLOAT, " +
                COLUMN_QTD9 + "FLOAT, " + COLUMN_QTD10 + "FLOAT, " + COLUMN_QTD11 + "FLOAT, " + COLUMN_QTD12 + "FLOAT, " +
                COLUMN_QTD13 + "FLOAT, " + COLUMN_QTD14 + "FLOAT); ";
                db.execSQL(query2);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRODUTO);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECEITA);
        onCreate(db);
    }

    void addProduto(MateriaPrima materiaPrima) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cv = new ContentValues();

        cv.put(COLUMN_PRODUTO, materiaPrima.getProduto());
        cv.put(COLUMN_VALOR, materiaPrima.getValor());
        cv.put(COLUMN_QTD, materiaPrima.getQuantidade());
        cv.put(COLUMN_TIPO, materiaPrima.getTipo());

        long result = db.insert(TABLE_PRODUTO, null, cv);
        if (result == -1) {
            Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context, R.string.strAddSucess, Toast.LENGTH_SHORT).show();
        }
        db.close();
    }

    void addReceita(Receita receita) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cv = new ContentValues();

        cv.put(COLUMN_TITULO, receita.getTitulo());
        cv.put(COLUMN_ITEM1, receita.getItem1());
        cv.put(COLUMN_ITEM2, receita.getItem2());
        cv.put(COLUMN_ITEM3, receita.getItem3());
        cv.put(COLUMN_ITEM4, receita.getItem4());
        cv.put(COLUMN_ITEM5, receita.getItem5());
        cv.put(COLUMN_ITEM6, receita.getItem6());
        cv.put(COLUMN_ITEM7, receita.getItem7());
        cv.put(COLUMN_ITEM8, receita.getItem8());
        cv.put(COLUMN_ITEM9, receita.getItem9());
        cv.put(COLUMN_ITEM10, receita.getItem10());
        cv.put(COLUMN_ITEM11, receita.getItem11());
        cv.put(COLUMN_ITEM12, receita.getItem12());
        cv.put(COLUMN_ITEM13, receita.getItem13());
        cv.put(COLUMN_ITEM14, receita.getItem14());
        cv.put(COLUMN_VALOR1, receita.getValor1());
        cv.put(COLUMN_VALOR2, receita.getValor2());
        cv.put(COLUMN_VALOR3, receita.getValor3());
        cv.put(COLUMN_VALOR4, receita.getValor4());
        cv.put(COLUMN_VALOR5, receita.getValor5());
        cv.put(COLUMN_VALOR6, receita.getValor6());
        cv.put(COLUMN_VALOR7, receita.getValor7());
        cv.put(COLUMN_VALOR8, receita.getValor8());
        cv.put(COLUMN_VALOR9, receita.getValor9());
        cv.put(COLUMN_VALOR10, receita.getValor10());
        cv.put(COLUMN_VALOR11, receita.getValor11());
        cv.put(COLUMN_VALOR12, receita.getValor12());
        cv.put(COLUMN_VALOR13, receita.getValor13());
        cv.put(COLUMN_VALOR14, receita.getValor14());
        cv.put(COLUMN_QTD1, receita.getQtd1());
        cv.put(COLUMN_QTD2, receita.getQtd2());
        cv.put(COLUMN_QTD3, receita.getQtd3());
        cv.put(COLUMN_QTD4, receita.getQtd4());
        cv.put(COLUMN_QTD5, receita.getQtd5());
        cv.put(COLUMN_QTD6, receita.getQtd6());
        cv.put(COLUMN_QTD7, receita.getQtd7());
        cv.put(COLUMN_QTD8, receita.getQtd8());
        cv.put(COLUMN_QTD9, receita.getQtd9());
        cv.put(COLUMN_QTD10, receita.getQtd10());
        cv.put(COLUMN_QTD11, receita.getQtd11());
        cv.put(COLUMN_QTD12, receita.getQtd12());
        cv.put(COLUMN_QTD13, receita.getQtd13());
        cv.put(COLUMN_QTD14, receita.getQtd14());

        long result = db.   insert(TABLE_RECEITA, null, cv);
        if (result == -1) {
            Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context, R.string.strAddSucess, Toast.LENGTH_SHORT).show();
        }
        db.close();
    }

    public List<MateriaPrima> buscaProduto() {
        String[] columns = {COLUMN_CODIGO, COLUMN_PRODUTO, COLUMN_VALOR, COLUMN_QTD, COLUMN_TIPO};
        SQLiteDatabase db = getReadableDatabase();
        @SuppressLint("Recycle") Cursor cursor = db.query(TABLE_PRODUTO, columns, null, null, null,null, null);

        while (cursor.moveToNext()) {
            int index1 = cursor.getColumnIndex(COLUMN_CODIGO);
            int codigo = cursor.getInt(index1);
            int index2 = cursor.getColumnIndex(COLUMN_PRODUTO);
            String produto = cursor.getString(index2);
            int index3 = cursor.getColumnIndex(COLUMN_VALOR);
            float valor = cursor.getFloat(index3);
            int index4 = cursor.getColumnIndex(COLUMN_QTD);
            float quantidade = cursor.getFloat(index4);
            int index5 = cursor.getColumnIndex(COLUMN_TIPO);
            String tipo = cursor.getString(index5);
            MateriaPrima produtos = new MateriaPrima(codigo, produto, valor, quantidade, tipo);
            listaProduto.add(produtos);
        }
        return listaProduto;
    }

    public List<Receita> buscaReceita() {
        String[] columns = {COLUMN_TITULO, COLUMN_ITEM1, COLUMN_VALOR, COLUMN_QTD, COLUMN_TIPO};
        SQLiteDatabase db = getReadableDatabase();
        @SuppressLint("Recycle") Cursor cursor = db.query(TABLE_RECEITA, columns, null, null, null,null, null);

        while (cursor.moveToNext()) {
            int index1 = cursor.getColumnIndex(COLUMN_TITULO);
            String titulo = cursor.getString(index1);
            int index2 = cursor.getColumnIndex(COLUMN_ITEM1);
            String item1 = cursor.getString(index2);
            /*int index3 = cursor.getColumnIndex(COLUMN_VALOR);
            float valor = cursor.getFloat(index3);
            int index4 = cursor.getColumnIndex(COLUMN_QTD);
            float quantidade = cursor.getFloat(index4);
            int index5 = cursor.getColumnIndex(COLUMN_TIPO);
            String tipo = cursor.getString(index5);*/
            Receita receitas = new Receita(titulo, item1); //, valor, quantidade, tipo);
            listaReceita.add(receitas);
        }
        return listaReceita;
    }

    void updateData(String row_id, String produto, String valor, String quantidade, String tipo){
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_PRODUTO, produto);
        cv.put(COLUMN_VALOR, valor);
        cv.put(COLUMN_QTD, quantidade);
        cv.put(COLUMN_TIPO, tipo);

        long result = db.update(TABLE_PRODUTO, cv, "codigo=?", new String[]{row_id});
        if(result == -1){
            Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
        } else{
            Toast.makeText(context, R.string.strSucess, Toast.LENGTH_SHORT).show();
        }

        db.close();
    }

   
    void deleteOneRow(String row_id){
        SQLiteDatabase db = this.getWritableDatabase();
        long result = db.delete(TABLE_PRODUTO, "codigo=?", new String[]{row_id});
        if(result== -1){
            Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context, R.string.strSucess, Toast.LENGTH_SHORT).show();
        }

    }

    void deleteAllData() {
        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("DELETE FROM " + TABLE_PRODUTO);
        db.close();
    }
}

My Activity to Save My Recipe (Second Table) using the ingredients saved on the first table:

package com.myapplication.umdocededaisy;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class AddItem extends AppCompatActivity {
    EditText et_produto, et_valor, et_qtd, et_sqtd, et_tipo, et_stipo;
    Button btnIncluir;
    String produto, valor, quantidade, tipo;
    List<MateriaPrima> listaProdutos;
    Spinner spinner;
    MyDatabase myDB = new MyDatabase(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_item);

        //Declarações objetos:
        et_produto = findViewById(R.id.et_produto);
        et_valor = findViewById(R.id.et_valor);
        et_qtd = findViewById(R.id.et_qtd);
        et_sqtd = findViewById(R.id.et_sqtd);
        et_tipo = findViewById(R.id.et_tipo);
        et_stipo = findViewById(R.id.et_stipo);
        btnIncluir = findViewById(R.id.btnIncluir);
        spinner = findViewById(R.id.spinner);

        //Chamada de Métodos:
        getAndSetIntentData();
        loadSpinnerData();
        loadSpinner();


        //Botões:
        //Save Button to Second Table:
        btnIncluir.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Receita receita = new Receita();
                

                myDB.addReceita(receita);
            }
        });
    }

    void getAndSetIntentData() {
        if (getIntent().hasExtra("produto") && getIntent().hasExtra("valor") &&
                getIntent().hasExtra("quantidade") && getIntent().hasExtra("tipo")){
            //Getting data:
            produto = getIntent().getStringExtra("produto");
            valor = getIntent().getStringExtra("valor");
            quantidade = getIntent().getStringExtra("quantidade");
            tipo = getIntent().getStringExtra("tipo");

            //Setting data:
            et_produto.setText(produto);
            et_valor.setText(valor);
            et_qtd.setText(quantidade);
            et_tipo.setText(tipo);
            et_stipo.setText(tipo);

        }else{
            Toast.makeText(this, R.string.strData0, Toast.LENGTH_SHORT).show();
        }
    }

I don't know how to write the code to save those changes, I don't know if I have to change something on my database insert method or just here, because my insert method is writing just like the one on my first table.

My AddProduto (where I save my ingredients one first table):

package com.myapplication.umdocededaisy;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;

public class AddProduto extends AppCompatActivity {

    EditText editProduto, editValor, editQuantidade, editTipo;
    FloatingActionButton btnVoltar;
    Button btnSalvar;
    InputMethodManager inputManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_produto);

        //Declarações objetos:
        editProduto = findViewById(R.id.editProduto);
        editValor = findViewById(R.id.editValor);
        editQuantidade = findViewById(R.id.editQuantidade);
        editTipo = findViewById(R.id.editTipo);
        btnVoltar = findViewById(R.id.btnVoltar);
        btnSalvar = findViewById(R.id.btnSalvar);
        inputManager = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);

        btnVoltar.setOnClickListener(v -> {
            Intent voltar = new Intent(AddProduto.this, MainActivity.class);
            startActivity(voltar);
        });

        btnSalvar.setOnClickListener(v -> {
            if(editProduto.getText().toString().isEmpty() || editValor.getText().toString().isEmpty() ||
                    editQuantidade.getText().toString().isEmpty() || editTipo.getText().toString().isEmpty())
            {
                Toast.makeText(AddProduto.this, R.string.strEmpty, Toast.LENGTH_SHORT).show();
            }else{
            MyDatabase myDB = new MyDatabase(AddProduto.this);
                MateriaPrima materiaPrima = new MateriaPrima();
                materiaPrima.setProduto(editProduto.getText().toString());
                materiaPrima.setValor(Float.parseFloat(editValor.getText().toString()));
                materiaPrima.setQuantidade(Float.parseFloat(editQuantidade.getText().toString()));
                materiaPrima.setTipo(editTipo.getText().toString());
                myDB.addProduto(materiaPrima);
        }
            reset();
            editProduto.requestFocus(); //focar no campo especificado
            inputManager.hideSoftInputFromWindow(btnSalvar.getWindowToken(), 0);
    });

    }
    void reset(){
        editProduto.setText(" ");
        editValor.setText(" ");
        editQuantidade.setText(" ");
        editTipo.setText(" ");
}
}

Can anyone help me? I've looked everywhere but didn't found anything that can be applied to my case. Thanks in advance.

1 Answers1

1

I don't know how to write the code to save those changes,

The design of your database appears to be introducing complexities that would make inserting and retrieving data into/from the second table quite complex.

By the look of it your second table consists of many columns where a relationship could replace the repeated columns (materia/ingredients) and adding such a relationship could perhaps simplify matters.

To look at this you have materia (ingredients) such as Milk Flour Sugar etc. An ingredient can be used in many recipes and a recipe can use many ingredients. As such you likely want a many-many relationship between recipes and ingredients.

So I'd suggest that you consider a mapping table that allows you have to a many to many relationship, so 3 tables instead of 2. Perhaps consider the following:-

CREATE TABLE IF NOT EXISTS materia_prima (codigo INTEGER PRIMARY KEY /* AUTOINCREMENT */,produto TEXT, VALOR FLOAT, QTD FLOAT, TIPO TEXT);
CREATE TABLE IF NOT EXISTS receitas (TITULO TEXT PRIMARY KEY);
CREATE TABLE IF NOT EXISTS receitas_materia (
    titulo_map TEXT REFERENCES receitas(titulo) ON DELETE CASCADE ON UPDATE CASCADE,
    materia_map INTEGER REFERENCES materia_prima(codigo) ON DELETE CASCADE ON UPDATE CASCADE, receitas_materia_qtd FLOAT, 
    PRIMARY KEY(titulo_map,materia_map));
  • The materia_prima table is as it was (except there is no need for the inefficient AUTOINCREMENT so that's been commented out)
  • The receitas table has been greatly simplified (just 1 column)
  • The receitas_materia table is the mapping table that is used to signify that an ingredient is used by a recipe.
    • Foreign Key constraints (the REFERENCES clause) have been added to enforce referential integrity.
    • The ON DELETE/UPDATE says to propagate changes from the parent to the child.
  • A third column has been added to denote the quantity used by the recipe.

So taking this further the following data is added :-

First some ingredients including their cost per measure:-

INSERT INTO materia_prima VALUES 
    (null,'Plain Flour',0.54,1,'Gram'),
    (null,'SR Flour',0.55,1,'Gram'),
    (null,'Eggs',1.34,1,'Each'),
    (null,'Milk',0.2,1,'MilliLitre'),
    (null,'Sugar',0.65,1,'Gram');

Second Some recipes :-

INSERT INTO receitas VALUES ('Bread'),('Cake'),('Scone');

Now the ingredients used by recipes (just Bread and Cakes) :-

INSERT INTO receitas_materia VALUES 
    ('Bread',1 /*map to Plain Flour*/,200 /* uses 200 grams */),
    ('Bread',3,2),
    ('Bread',4,50),
    ('Bread',5,10),
    
    ('Cake',2,400),
    ('Cake',3,3),
    ('Cake',4,100),
    ('Cake',5,300)
;
  • So instead of many columns and the complexity of trying to determine what goes into what column. You simply add as many ingredients per recipe as is required (and let the queries do the work).

A Query such as :-

SELECT *, valor * receitas_materia_qtd AS cost 

FROM receitas JOIN receitas_materia ON receitas_materia.titulo_map = receitas.titulo JOIN materia_prima ON materia_prima.codigo = receitas_materia.materia_map;

Could be used to list the product and it's ingredients including the cost per ingredient e.g. using the above the results is:-

enter image description here

  • e.g. Bread uses 200g of sugar which costs 0.54 per gram so therefore the cost of sugar for bread is 108.

You would appear to want the total cost per item so a query something like :-

SELECT titulo, group_concat(produto||' ('||receitas_materia_qtd||' '||tipo||')') AS materias, sum(valor * receitas_materia_qtd) AS total_cost 
FROM receitas JOIN receitas_materia ON receitas_materia.titulo_map = receitas.titulo JOIN materia_prima ON materia_prima.codigo = receitas_materia.materia_map
GROUP BY titulo;

Could be used, this results in :-

enter image description here

Android based Example

The following is a basic example that allows ingredients to be added to a recipe via a spinner. The current ingredients are also listed allowing an ingredient to also be removed by long clicking the ingredient.

The example utilises Cursor Adapters which are simpler to work with.

First is MyDatabase which uses the suggested design and includes methods the various methods:-

public class MyDatabase extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "BancoDoceDaisy.db";
    private static final int DATABASE_VERSION = 9;
    SQLiteDatabase db;

    public static final String TABLE_PRODUTO = "materia_prima";
    public static final String COLUMN_PRODUTO_CODIGO = BaseColumns._ID; /*CHANGED Use the standard Android ID column name */
    public static final String COLUMN_PRODUTO = "produto";
    public static final String COLUMN_VALOR = "valor";
    public static final String COLUMN_QTD = "quantidade";
    public static final String COLUMN_TIPO = "tipo";

    public static final String TABLE_RECEITA = "receitas";
    public static final String COLUMN_RECEITA_CODIGO = BaseColumns._ID;
    public static final String COLUMN_TITULO = "titulo";

    public static final String TABLE_RECEITA_MATERIA_MAP = TABLE_RECEITA + "_" + TABLE_PRODUTO + "_map";
    public static final String COLUMN_RECEITA_MAP = TABLE_RECEITA + "_map";
    public static final String COLUMN_PRODUTO_MAP = TABLE_PRODUTO + "_map";
    public static final String COLUMN_RECEITA_QTD = TABLE_RECEITA + "_" + COLUMN_QTD;
    public static final String DERIVED_COLUMN_COST = "cost";
    private static final String cost_calculate = TABLE_PRODUTO + "." + COLUMN_VALOR + " * " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_QTD + " AS " + DERIVED_COLUMN_COST;
    public static final String DERIVED_COLUMN_TOTAL_COST = "total_cost";
    private static final String total_cost_calculate = "SUM(" +  TABLE_PRODUTO + "." + COLUMN_VALOR + " * " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_QTD + ") AS " + DERIVED_COLUMN_TOTAL_COST;
    private final String join_receitamateria_map =
            " JOIN " + TABLE_RECEITA_MATERIA_MAP +
                    " ON " + TABLE_RECEITA + "." + COLUMN_RECEITA_CODIGO +
                    " = " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_MAP;
    private final String join_produto =
            " JOIN " + TABLE_PRODUTO +
                    " ON " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_PRODUTO_MAP +
                    " = " + TABLE_PRODUTO + "." + COLUMN_PRODUTO_CODIGO;

    private static final String fkey_options = " ON DELETE CASCADE ON UPDATE CASCADE ";

    private static volatile MyDatabase instance = null;

    private MyDatabase(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        db = this.getWritableDatabase();
    }

    public static MyDatabase getDatabaseInstance(Context context) {
        if (instance == null) {
            instance = new MyDatabase(context);
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

        String query = "CREATE TABLE "+ TABLE_PRODUTO +
                " (" + COLUMN_PRODUTO_CODIGO + " INTEGER PRIMARY KEY, " +
                COLUMN_PRODUTO + " TEXT, " +
                COLUMN_VALOR + " FLOAT, " +
                COLUMN_QTD + " FLOAT, " +
                COLUMN_TIPO + " TEXT); ";
        db.execSQL(query);
        query = "CREATE TABLE IF NOT EXISTS " + TABLE_RECEITA + "(" +
                COLUMN_RECEITA_CODIGO + " INTEGER PRIMARY KEY ," +  /* ADDED to make things simpler android wise */
                COLUMN_TITULO + " TEXT UNIQUE" +
                ")";
        db.execSQL(query);
        query = "CREATE TABLE IF NOT EXISTS "  +TABLE_RECEITA_MATERIA_MAP + "(" +
                COLUMN_RECEITA_MAP + " INTEGER REFERENCES " + TABLE_RECEITA + "(" + COLUMN_PRODUTO_CODIGO + ")" + fkey_options  + "," +
                COLUMN_PRODUTO_MAP + " INTEGER REFERENCES " + TABLE_PRODUTO + "(" + COLUMN_RECEITA_CODIGO + ")" + fkey_options + "," +
                COLUMN_RECEITA_QTD + " FLOAT, " +
                "PRIMARY KEY(" + COLUMN_RECEITA_MAP + "," + COLUMN_PRODUTO_MAP + ")" +
                ")";
        db.execSQL(query);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {

    }

    public long insertProduto(String produto,Float valor, Float qtd, String tipo) {
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_PRODUTO,produto);
        cv.put(COLUMN_VALOR,valor);
        cv.put(COLUMN_QTD,qtd);
        cv.put(COLUMN_TIPO,tipo);
        return db.insert(TABLE_PRODUTO,null,cv);
    }

    public long insertReceita(String titulo) {
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_TITULO,titulo);
        return db.insert(TABLE_RECEITA,null,cv);
    }

    public long insertReceitaMateria(long receitaMap, long produtoMap, Float qtd) {
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_RECEITA_MAP,receitaMap);
        cv.put(COLUMN_PRODUTO_MAP,produtoMap);
        cv.put(COLUMN_RECEITA_QTD,qtd);
        return db.insert(TABLE_RECEITA_MATERIA_MAP,null,cv);
    }

    public int deleteMateriaFromReceita(long receita, long produtoCodigo) {
        return  db.delete(TABLE_RECEITA_MATERIA_MAP,COLUMN_RECEITA_MAP + " =? AND " + COLUMN_PRODUTO_MAP + "=?",new String[]{String.valueOf(receita), String.valueOf(produtoCodigo)});
    }

    public Cursor getProduto() {
        return db.query(TABLE_PRODUTO,null,null,null,null,null,null);
    }
    public Cursor getReceita() {
        return db.query(TABLE_RECEITA,null,null,null,null,null,null);
    }
    public Cursor getReceitaProduto(Long receita) {
        return db.query(
                TABLE_RECEITA + join_receitamateria_map + join_produto,
                new String[]{"*",cost_calculate},
                TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_MAP + "=?",new String[]{receita.toString()},null,null,null);
    }

    public String getReceitanameByCodigo(long codigo) {
        String rv = "NOT KNOWN";
        Cursor csr =db.query(TABLE_RECEITA,new String[]{COLUMN_TITULO},COLUMN_RECEITA_CODIGO + "=?",new String[]{String.valueOf(codigo)},null,null,null);
        if (csr.moveToFirst()) {
            rv = csr.getString(csr.getColumnIndex(COLUMN_TITULO));
        }
        csr.close();
        return rv;
    }

}

Next is an initial activity MainActivity (designed to only run once) that adds some materia/produtos (ingredients) and some blank (no ingredients) receitas (recipes). After which the AddProduto activity is invoked passing the Cake recipe to the activity :-

public class MainActivity extends AppCompatActivity {

    MyDatabase db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = MyDatabase.getDatabaseInstance(this);

        db.insertProduto("Plain Flour",0.54F,1F,"Grams");
        db.insertProduto("SR Flour",0.55F,1F,"Grams");
        db.insertProduto("Eggs",1.34F,1f,"Each");
        db.insertProduto("Milk",0.2F,1F,"ml");
        db.insertProduto("Sugar",0.65F,1F,"Grams");

        db.insertReceita("Bread");
        long selectedId = db.insertReceita("Cake");
        db.insertReceita("Scone");

        Intent intent = new Intent(this,AddProduto.class);
        intent.putExtra(MyDatabase.TABLE_RECEITA,selectedId);
        startActivity(intent);
    }
}

Finally is the AddProduto activity. This has a spinner that allows selection of an ingredient(s). Selecting an ingredient adds the ingredient to the recipe and the recipe's ingredients are then refreshed to show the added ingredient. If an ingredient in the list of ingredients is long clicked it is removed from the recipe.

Note only very basic management is incorporated. e.g. really the ingredients in the spinner should exclude those already in the recipe.

The amount/quantity of the ingredient is set to be 100 for simplicity/brevity of the demo/example:-

public class AddProduto extends AppCompatActivity {

    TextView receita;
    Spinner materia_add;
    ListView materia_in;
    SimpleCursorAdapter adapter_list, adapter_spinner;
    MyDatabase db;
    Cursor csr_add, csr_in;
    long current_receita;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_produto);
        db = MyDatabase.getDatabaseInstance(this);

        receita = this.findViewById(R.id.Receita);
        current_receita = this.getIntent().getLongExtra(MyDatabase.TABLE_RECEITA,0);
        receita.setText(db.getReceitanameByCodigo(current_receita));
        materia_add = this.findViewById(R.id.materia_add);
        materia_in = this.findViewById(R.id.materia_in);
        setOrRefreshSpinner();
        setOrRefreshReceitaList();

    }

    // Handling the Spinner
    private void setOrRefreshSpinner() {
        csr_add = db.getProduto();
        if (adapter_spinner == null) {
            adapter_spinner = new SimpleCursorAdapter(
                    this,
                    android.R.layout.simple_list_item_2,
                    csr_add,
                    new String[]{MyDatabase.COLUMN_PRODUTO, MyDatabase.COLUMN_VALOR},
                    new int[]{android.R.id.text1, android.R.id.text2},0
            );
            materia_add.setAdapter(adapter_spinner);
            materia_add.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    db.insertReceitaMateria(current_receita,l,100F); // Made up value for demo
                    setOrRefreshReceitaList(); // Update the List of materia in the receita
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {

                }
            });
        } else {
            adapter_spinner.swapCursor(csr_add);
        }
    }

    // Handling the recipe ListView
    private void setOrRefreshReceitaList() {
        csr_in = db.getReceitaProduto(current_receita);
        if (adapter_list == null) {
            adapter_list = new SimpleCursorAdapter(
                    this,
                    android.R.layout.simple_list_item_2,
                    csr_in,
                    new String[]{MyDatabase.COLUMN_PRODUTO,MyDatabase.DERIVED_COLUMN_COST},
                    new int[]{android.R.id.text1,android.R.id.text2},
                    0
            );
            materia_in.setAdapter(adapter_list);
            materia_in.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
                    db.deleteMateriaFromReceita(current_receita,l);
                    setOrRefreshReceitaList();
                    return true;
                }
            });
        } else {
            adapter_list.swapCursor(csr_in);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        csr_in.close();
        csr_add.close();
    }
}

Result

When first run the AddProduto activity is displayed (after MainActivity starts it) :-

enter image description here

  • Note that handling the fact that the Spinner automatically selects an Item has not been included so an ingredient is added before any selection is made (see Android Spinner : Avoid onItemSelected calls during initialization in regrad to avoiding this).

  • Purple is the Spinner

  • teal is the recipe's list of ingredients (with the total cost of the ingredient i.e. 100 * the ingredient cost).

Select Eggs from the Spinner :-

enter image description here

And then after selection (adding Eggs) :-

enter image description here

LongClick Plain Flour in the List of ingredients :-

enter image description here

MikeT
  • 51,415
  • 16
  • 49
  • 68
  • I didn't understand your declaration for PRIMARY KEY on the new map table: PRIMARY KEY(titulo_map,materia_map)); ??? / Also I want to add the data through the methods not manually adding them. – Martha Andrade Jul 11 '21 at 22:28
  • @MarthaAndrade it's a composite primary key aka a key that consists of multpile columns that a recipe/ingredient combination is unique e.g. you cannot have a recipe, such as Bread that has Flour and Flour. Methods are simple enough to introduce basically they just invoke the SQL. I may, time permitting provide a more android based example. – MikeT Jul 12 '21 at 01:10
  • @MarthaAndrade have added a basic working example App which includes the addition of ingredients to a recipe via a spinner (and deletion of ingredients by long clicking an ingredient via the listed ingredients). – MikeT Jul 12 '21 at 04:32
  • thanks for the help, but it continues not to work – Martha Andrade Jan 30 '22 at 22:30