0
\$\begingroup\$

I'm conflicting about how I want my database architecture to be built. First of all, I use a singleton pattern for the database in order to insure one instance of it (so its thread-safe), and also for me to get a workable database reference wherever I have a context.

All across the application I make many different db operations, for example, some activities need to change only the 'Meals' table and some need to change 'Meals' & 'MealFoods' for example.

For each one of this tables I've built a helper class in order to separate the CRUD operations of each table apart from the DatabaseManager class (which extends SQLiteOpenHelper). This, of course is for the sake of simplicity and to get a cleaner code.

First approach:

This approach saves all the helper classes inside the DatabaseManager.

DatabaseManager.java:

public class DatabaseManager extends SQLiteOpenHelper {
    private static String dbName = "logs.db";
    private static final int dbVersion = 1;

    private final Context context;
    public MealsDBHelper mealsDBHelper;
    public MealFoodsDBHelper mealFoodsDBHelper;

    private DatabaseManager(@NonNull Context context) {
        super(context, dbName, null, dbVersio);
        this.context = context.getApplicationContext();//Saving as application context to avoid leaks
        mealsDBHelper = new MealsDBHelper(context);
        mealFoodsDBHelper = new MealFoodsDBHelper(context);
    }

    private static DatabaseManager instance;
    public static synchronized DatabaseManager getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseManager(context);
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //...
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //...
    }
}

Let's look at the MealsDBHelper class, which pretty much all it does is to help communicate CRUD operations with the database. (For example a user wants to change his meal name)

public class MealsDBHelper {
    public static final String MEALS_TABLE_NAME = "Meals";
    public static final String MEAL_ID_COLUMN = "Meal_ID";
    public static final String MEAL_NAME_COLUMN = "Meal_Name";
    public static final String MEAL_POS_COLUMN = "Meal_Pos";
    public static final String MEAL_DATE_COLUMN = "Date";


    private Context context; //A context object to pass on to the DatabaseManager.getInstance method in all the different methods inside this class
    public MealsDBHelper(Context context){
        this.context = context;
    }

    //For example one of few methods that do operations on the 'Meals' table in the database.
    public void updateMealName(long mealId, String meal_name) {
        UserDataDB.getInstance(context).getWritableDatabase().execSQL("UPDATE " + MEALS_TABLE_NAME + " SET " + MEAL_NAME_COLUMN + " = '" + meal_name + "' WHERE " + MEAL_ID_COLUMN + " = " + mealId);
    }
}

Now, no matter if the activity is modifying 1 or 2 or even 3 tables, I'm able to update the meal's name like so:

DatabaseManager.getInstance(context).mealsDBHelper.updateMealName(mealId, mealName);

Thats because the DatabaseManager contains a reference to all the other helper classes.

What I like about this approach is that I can simply access every table and do operations on it according to my needs, & what I don't like is that the DatabaseManager class holds reference for all the helpers, and I'm not sure if its best to do so..

Second approach:

This approach does not saves all the helper classes inside the DatabaseManager.

DatabaseManager.java:

public class DatabaseManager extends SQLiteOpenHelper {
    private static String dbName = "logs.db";
    private static final int dbVersion = 1;

    private final Context context;

    private DatabaseManager(@NonNull Context context) {
        super(context, dbName, null, dbVersio);
        this.context = context.getApplicationContext();//Saving as application context to avoid leaks
    }

    private static DatabaseManager instance;
    public static synchronized DatabaseManager getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseManager(context);
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //...
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //...
    }
}

Now, if my activity needs to modify both 'Meals' & 'MealFoods' tables I can construct the helpers in onCreate, such as:

public class AddFoodActivity extends AppCompatActivity{

    MealsDBHelper mealsDBHelper;
    MealFoodsDBHelper mealFoodsDBHelper;

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

        mealsDBHelper = new MealsDBHelper(this);
        mealFoodsDBHelper = new MealFoodsDBHelper(this);
    }

    //Then whenever I need to modify the table I use:
    mealsDBHelper.updateMealName(mealId, mealName);
}

What I like about this approach is that I can use a simple line to modify a table according to my needs, & what I don't like is that I need to define helper references for every activity, and it kind of makes the code inconsistent.

Basically are there any downsides for using one of the methods?

I'll admit I did leave a big chunk of code out of this post, but its only because I think it won't add a lot to your understanding of the problem, because its a more general one.

Thank you very much for any kind of help.

\$\endgroup\$
1
  • \$\begingroup\$ Welcome to the Code Review Community where we review working code and provide suggestions on how to improve that code. While we sometimes address software design in the reviews it is not something we focus on explicitly. Please read How do I ask a good question?. You might also want to check out the Software Engineering Community that does focus on software design. \$\endgroup\$ – pacmaninbw Jan 23 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.