Как заставить метод вставки базы данных Room возвращать int через архитектуру mvvm?

Я хочу, чтобы идентификатор автоматически генерировался при выполнении операции вставки в базу данных Room. Я реализую архитектуру MVVM (Model-View-ViewModel), которая использует DAO для запуска запросов к базе данных Room. Я добавил уровень репозитория между моделью представления и DAO, чтобы создать AsyncTask для выполнения операций с базой данных. Как получить результат операции вставки (который является автоматически сгенерированным идентификатором вставленной строки) во фрагмент, который использует модель представления. Слои следующие: Fragment -> ViewModel -> Repository -> DAO.

ListFragment.java

public class ListFragment extends Fragment {
    private ReminderViewModel viewModel;
    private int id;
    ...
        viewModel = ViewModelProviders.of(this).get(ReminderViewModel.class);
    ...
        id = viewModel.insert(new TodoReminder(0, description, date, time));
    ...
}

ReminderViewModel.java

public class ReminderViewModel extends AndroidViewModel {
    private ReminderRepository repository;

    public ReminderViewModel(@NonNull Application application) {
        super(application);
        repository = new ReminderRepository(application);
    }

    public int insert(TodoReminder reminder) {
        repository.insert(reminder);
    }
}

ReminderRepository.java

public class ReminderRepository {
    private ReminderDAO reminderDAO;

    public ReminderRepository(Application application) {
        AppDatabase db = AppDatabase.getDatabase(application);
        reminderDAO = db.getReminderDAO();
    }

    public int insert(TodoReminder reminder) {
        new insertAsyncTask(reminderDAO).execute(reminder);
    }

    private static class InsertAsyncTask extends AsyncTask<TodoReminder, Void, Integer> {
        private ReminderDAO asyncTaskDAO;

        insertAsyncTask(ReminderDAO dao) {
            asyncTaskDAO = dao;
        }

        @Override
        protected Integer doInBackground(final TodoReminder... reminders) {
            return asyncTaskDAO.insert(reminders[0]);
        }
    }
}

НапоминаниеDAO.java

@Dao
public interface ReminderDAO {
    @Insert
    public int insert(TodoReminder... reminders);
}

ToDoReminder.java

public class TodoReminder implements Serializable {
    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;
    ...
}

Как мне получить int, возвращаемый из метода вставки ReminderDAO, и вернуть его из метода вставки в ReminderRepository?


person Rahul    schedule 09.07.2019    source источник


Ответы (2)


Вы можете создать таблицу database таким образом, чтобы id увеличивалась автоматически. В MySQL это делается с помощью ключевого слова auto_increment. В SQL Server это делается с помощью синтаксиса identity(1, 1). В Access это ключевое слово autoincrement. В Oracle и PostgreSQL это делается с помощью sequences. Если вам удастся это сделать, то вам не нужно будет вручную работать над увеличением этих значений. Если по каким-то причинам об этом не может быть и речи, то можно создать триггер before insert, который будет загружать максимальный id и прибавлять к нему 1, сохраняя результат в id. Или, если даже об этом не может быть и речи, то вы можете загрузить последний идентификатор, но он имеет разный синтаксис в разных базах данных. Или вы можете запустить такой запрос:

select max(id) + 1 from yourtable;

но остерегайтесь возможных проблем с производительностью и параллелизма.

person Lajos Arpad    schedule 09.07.2019
comment
Я ценю ваши предложения. Но мне нужен просто идентификатор, возвращаемый методом вставки базы данных комнаты. Мне просто нужно, чтобы идентификатор проходил через слои от бэкенда к интерфейсу. У вас есть какое-нибудь решение для этого? - person Rahul; 09.07.2019

У меня точно проблема. Это то, что я сделал.

Я создал интерфейс

public interface Task
 {
    void processInsert(long id) 
}

предоставить интерфейс для вставки fcn в репозиторий

public class Repository {

private Dao myDao;


    public void insertMyObject(MyOBject object,Task myInterface ) {

         new insertAysncTask(myDao,myInterface).execute(object);
      }

    private static class insertAsyncTask extends AysncTask<MyObject,Void,Long>{
        private Dao mDao;
        private Task mTask;

        public insertAysncTask(Dao dao, Task task) {

            this.mDao = dao;
            this.mTask=task;
        }


        @Override
        protected Long doInBackground(MyObject... myObjects) {
            return mDao.insertMyObject(myObjects[0]);

        }

        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            mTask.processInsert(aLong);
        }
    }


}

в классе DAO fcn должен иметь возвращаемый тип Long

public interface MyDao {
    @Insert
    Long insertMyObject(MyObject object);

пусть ViewModel реализует интерфейс

 public class MyObjectViewModel extends AndroidViewModel implements Task{

    private Repository mRepository;

    public MyObjectViewModel(@NonNull Application application) {
        super(application);
        mRepository = new Repository(application);
    }

    @Override
    public void processInsert(Long id) {

      // code for processing the id returned from the db insert

    }

    public void insertMyObject(MyObject object){

        mRepository.insertMyObject(object,this);}


}

в действии/фрагменте вызовите вставку ViewModel

mViewModel.insertMyObject(object);

Обновить

Если вы хотите вернуть идентификатор активности или фрагменту, тогда фрагмент/активность реализует интерфейс вместо модели представления.

ListFragment extends Fragment implements Task{
.....

@Override
public void processInsert(Long id){
   //process id here
  }

//call the insert fcn passing in the reference to the fragment

mViewModel.insertMyObject(object,this)
}

Измените вставку fcn в модели представления, чтобы принять ссылку на интерфейс

public void insertMyObject(MyObject object, Task myInterface){

        mRepository.insertMyObject(object,myInterface);
}

при таком подходе асинтаск в вставке fcn репозитория будет содержать ссылку на действие/фрагмент, и если действие/фрагмент будет уничтожено до завершения асинтаска, это будет проблемой. Я думаю, что лучше делать обработку в модели представления, если это возможно. Фрагменты/действия должны иметь дело только с пользовательским интерфейсом, а не с обработкой данных.

Альтернативный метод Альтернативой может быть использование LiveData с наблюдателем.

public class Repository{

private Dao myDao;
private MutableLiveData<Long> dbInsertId = new MutableLiveData<>();

public void insertMyObject(MyObject object){

    insertAysnc(object)
  }

private void insertAysnc(final MyObject object){

   new Thread(new Runnable() {
            @Override
            public void run() {
                Long id=myDao.insertMyObject(object); //call Dao insert fcn
              dbInsertId.postValue(id); //set the value of the livedata to the valued returned from the DB insert fcn
            }
        }).start();
  }

public LiveData<Long> getDbInsertedId(){
        return dbInsertId;
    }

}

В ViewModel определите этот fcn

public LiveData<Long> getDbInsertedId(){
        return mRepository.getDbInsertedId();// call the repository getId fcn
    }

в настройке onCreate Activity/Fragment наблюдателя на LiveData

mViewModel= ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getDbInsertedId().observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
               // do something with the long value returned from the database
            }
        });

person mem100    schedule 17.08.2019
comment
Как я получу вставленный идентификатор во фрагменте? что-то вроде "int id = mViewModel.insertMyObject(object);" - person Rahul; 17.08.2019
comment
также я получаю эту ошибку: «Не знаю, как обрабатывать возвращаемый тип метода вставки. длинная вставка (TodoReminder... напоминания);' в классе дао. Пожалуйста помоги - person Rahul; 17.08.2019
comment
Тип возвращаемого значения должен быть Long (при вставке одного объекта) или Long [ ] (при вставке нескольких объектов). Обратите внимание, что регистр чувствителен к длинному, а не к длинному. - person mem100; 18.08.2019