8

Good day Stack, i'm working on an Android project that uses Android's Room 1.0.0 Alpha 5, the main issue that i'm facing is that every time i need to call one of the DAO from room i need to do something like this:

Activity.java:

...
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "Storage").build();
Table1 table = new Table1();
table.setId(1);
table.setName("Hello");
new AccessDB().execute(1);

/* Generic AccessDB needed */
private class AccessDB extends AsyncTask<Integer,Void,List<Table1>> {

    @Override
    protected List<Table1> doInBackground(Integer... param) {
        switch(param[0]) {
            case 1:
                return db.Table1DAO().create();
            case 2:
                return db.Table1DAO().read();
        }
        return new ArrayList<>();
    }

    @Override
    protected void onPostExecute(List<Table1> list) {
        processData(list);
    }
}
...

I know that i can access Room DB from the main thread, and that would shrink the code, but i think that's not a good practice since that would lock the activity every time it has to handle data.

So if i need to insert or read data from "Table2" i would have to do the same all over again, it would be great if i could turn the entity types into generics like "T" or something like that and then make a generic "AccessDB". But since i'm not too familiar with Java... I'm currently struggling with this.

Here is some other code of the instances.

AppDatabase.java:

@Database(entities = {Table1.class, Table2.class, Table3.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract Table1DAO Table1DAO();
    public abstract Table2DAO Table2DAO();
    public abstract Table3DAO Table3DAO();
}

Table1.java:

@Entity
public class Table1 {
    /* setters & getters */
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;
}

Table1DAO.java:

@Dao public interface Table1DAO {
    @Query("SELECT * FROM Table1")
    List<Table1> read(Table1 table);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    List<Long> create(Table1... table);
}

Thank you all for your help.

Basher SG
  • 167
  • 1
  • 8

2 Answers2

8

You can use inheritance and create a BaseDao which will be implemented by all your child Dao. This way you won't need to write the common methods again and again.

interface BaseDao<T> {

/**
 * Insert an object in the database.
 *
 * @param obj the object to be inserted.
 */
@Insert
fun insert(obj: T)

/**
 * Insert an array of objects in the database.
 *
 * @param obj the objects to be inserted.
 */
@Insert
fun insert(vararg obj: T)

/**
 * Update an object from the database.
 *
 * @param obj the object to be updated
 */
@Update
fun update(obj: T)

/**
 * Delete an object from the database
 *
 * @param obj the object to be deleted
 */
@Delete
fun delete(obj: T)
}

Read more about it: https://gist.github.com/florina-muntenescu/1c78858f286d196d545c038a71a3e864#file-basedao-kt

Original credits to Florina.

Akshay Chordiya
  • 4,761
  • 3
  • 40
  • 52
  • 1
    I thought of something very similar, but i think that this is better, it seems that it can not have a default query too, but it helps a lot. – Basher SG Mar 20 '18 at 12:01
  • I only see inheritance for Dao in Kotlin. It does work in Java too, doesn't it? – mike_x_ Dec 11 '18 at 13:05
  • Yes, it works with Java too; since Java supports inheritance. – Akshay Chordiya Dec 12 '18 at 14:53
  • Is it true that we still have to create a specific DAO subtype for each queryable entity? Getting off with one generic DAO seems impossible, at least because we have to specify the table name for query methods. Why do we even have to write SQL for basic SELECTs? INSERTs, DELETEs and UPDATEs seem to do fine on their own! – Arthur Khazbs Mar 23 '23 at 16:56
2

I played around a bit with the answer of Akshay Chordiya, but needed two additions:

  • ability to insert/update List
  • return values to monitor insert/update success

Here is what I came up with:

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Update

/**
 * List of all generic DB actions
 * All use suspend to force kotlin coroutine usage, remove if not required
 */
@Dao
interface BaseDao<T> {

    // insert single
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(obj: T?): Long

    // insert List
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(obj: List<T>?) : List<Long>

    // update List
    @Update
    suspend fun update(obj: List<T>?): Int

}

@Dao
interface MyObjectDao : BaseDao<MyObject> {

    @Query("SELECT * from $TABLE_NAME WHERE $COL_ID = :id")
    suspend fun getById(id: Long): MyObject

}

Can then be called like:

val ids = MyObjectDao.insert(objectList)
Gunnar Bernstein
  • 6,074
  • 2
  • 45
  • 67