How to fix "each bind variable in the query must have a matching method parameter" error in Android java?

DaGee :

I tried to implement a simple RoomDB in an android app which only has 1 table, and Task objects in it, preloaded through the databaseBuilder's callback. When I try to build the app I get the each bind variable in the query must have a matching method parameter error, but if I hit run the second time it compiles the app, but I can't insert anything into the database, and if I try to query the preloaded task by its id (0) it gives back null.

This is my Task class, which I want to store in the DB:

@Entity(tableName = "tasks")
public class Task {

    @PrimaryKey(autoGenerate = true)
    private int taskId;
    @ColumnInfo(name = "Description")
    private String description;

    public Task(String description) {
        this.description = description;
    }

    public void setTaskId(int taskId) {
        this.taskId = taskId;
    }


    public int getTaskId() {
        return taskId;
    }

    public String getDescription() {
        return description;
    }

    @NonNull
    @Override
    public String toString() {
        return "Task object, id: "+taskId;
    }
}

And this is my TaskDAO:

@Dao
public interface TaskDAO {

    @Query("SELECT * FROM tasks")
    List<Task> getAll();

    @Query("SELECT * FROM tasks WHERE taskId = (:taskId)")
    Task getTaskById(int taskId);

    @Insert
    void insertAll(Task...tasks);

}

And this is the database class:

@Database(entities = {Task.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    private static AppDatabase INSTANCE;

    public abstract TaskDAO taskDAO();

    public static AppDatabase getAppDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "tasks.db")
                    .addCallback(new Callback() {
                        @Override
                        public void onCreate(@NonNull SupportSQLiteDatabase db) {
                            super.onCreate(db);
                            new PopulateInitialData(INSTANCE).execute();
                        }
                    }).build();
        }
        return INSTANCE;
    }

    private static class PopulateInitialData extends AsyncTask<Void, Void, Void> {

        private final TaskDAO taskDAO;

        public PopulateInitialData(AppDatabase database){
            taskDAO = database.taskDAO();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            Task task1 = new Task("Task data");

            taskDAO.insertAll(new Task[] {task1});
            return null;
        }
    }
}

And this is how I call the DB and try to query a task:

AppDatabase roomDB = AppDatabase.getAppDatabase(getApplicationContext());
new Thread(new Runnable() {
    @Override
    public void run() {
        Task task = roomDB.taskDAO().getTaskById(0)); //task is null
    }
}).start();
MikeT :

The insertAll method expects an array of Task objects (Task[]) not a single Task object.

If you want to insert a single Task object then you could add a new insert method into the DAO e.g.

@Insert
void insertOneTask(Task task);

Alternately use a single element array with the current insertAll method.

e.g.

taskDAO.insertAll(new Task[]{task1});

but I can't insert anything into the database, and if I try to query the preloaded task by its id (0) it gives back null.

That is because there will never, under normal circumstances, be a task with an id of 0. The first id will be 1.

If you used the following code

            List<Task> alltasks = roomDB.taskDAO().getAll();
            for (Task t: alltasks) {
                Log.d("TASKINFO","Task =" + t.getDescription() + " ID = " + t.getTaskId());
            }

after/before or instead of Task task = roomDB.taskDAO().getTaskById(0)); //task is null

It would show you that the task id is 1.

e.g. 2019-09-29 20:00:20.463 D/TASKINFO: Task =Task data ID = 1

To explain ROOM will create the table for the Tasks using :-

CREATE TABLE IF NOT EXISTS `tasks` (`taskId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `Description` TEXT)

The taskid column, will be an alias of the rowid column and if not supplied a value, or the supplied value is null. It will then auto-generate a value for the rowid and therefore it's alias. The value will be 1 greater than the the higher of the last recorded value or the highest value currently in the database. If there are no such values then the value will be 1.

If you used

Task task = roomDB.taskDAO().getTaskById(1));

The the inserted Task would be returned.

Generally you should never make assumptions about the value of an id, rather it should be obtained programatically, typically from a list, but with ROOM you would typically deal with Objects (e.g.the getAll() and the following for loop as above)

Testing

The code was tested using the following :-

  • Task.java copied asis and unchanged
  • TaskDAO.java copied asis and unchanged.
  • AppDatabase.java copied asis and unchanged.

  • And this is how I call the DB and try to query a task:

    • Dropped this code into MainActivity.java

    • Made roomDB a class variable, (AppDatabase roomDB) just instantiating it in the onCreate method.

    • Also corrected error (extra parenthesis) and

    • Added getAll and loop code from above.

as per :-

public class MainActivity extends AppCompatActivity {

    AppDatabase roomDB;

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

        roomDB = AppDatabase.getAppDatabase(getApplicationContext());
        new Thread(new Runnable() {
            @Override
            public void run() {
                Task task = roomDB.taskDAO().getTaskById(0); //task is null
                List<Task> alltasks = roomDB.taskDAO().getAll();
                for (Task t: alltasks) {
                    Log.d("TASKINFO","Task =" + t.getDescription() + " ID = " + t.getTaskId());
                }
            }
        }).start();
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=308855&siteId=1