Комната: заменить курсор SQLite в Dao?

У меня есть JobIntentService, который повторно устанавливает ожидающие уведомления о тревогах из базы данных SQLite. Он использует запрос и курсор для получения дат уведомлений из 4 разных столбцов в базе данных. Я конвертирую в базу данных Room и не знаю, как преобразовать курсор в метод Dao. Нужно ли мне использовать @Transaction, так как я получаю уведомления из нескольких столбцов в базе данных? Буду признателен за любые идеи или мысли о том, как построить в комнате.

Service

public class Service extends JobIntentService {

static final int JOB_ID = 9;

public static void enqueueWork(Context context, Intent work) {
    enqueueWork(context, RebootService.class, JOB_ID, work);
} 

@Override
protected void onHandleWork(@NonNull Intent intent) {

    AlarmManager alarmManager1;
    Intent brIntent1;
    PendingIntent pendingIntent1;

    SQLiteDB sqLiteDB = SQLiteDB.getInstance(this);
    Calendar cal1 = Calendar.getInstance();

    Cursor cursor = sqLiteDB.resetNotifications(); 

     try {
          if (cursor.getCount() > 0) { 
              cursor.moveToFirst(); 

              int dueDatentimeColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_DUEDATENTIME);
              int notifColumnIndex1 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_NOTIFTIME);
              int notif2ColumnIndex2 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_NOTIFTIME2);
              int randColumnIndex1 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_RANDINT);

              while (!cursor.isAfterLast()) {  

                  do {

                      long notifTime1 = cursor.getLong(notifColumnIndex1);
                      int randInt1 = cursor.getInt(randColumnIndex1);
                      cal1.setTime(new Date(notifTime1));

                      // Set up a system AlarmManager to fire a future alarm that sends a Notification
                      // even if the app is in the background or closed.
                      alarmManager1 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

                      if (cal1.getTimeInMillis() > System.currentTimeMillis()) {                                            
                          brIntent1 = new Intent(this, AlarmReceiver.class);
                       brIntent1.setAction("24Hour");

                       pendingIntent1 = PendingIntent.getBroadcast(this, randInt1, brIntent1,
                                    PendingIntent.FLAG_ONE_SHOT);

                      if (alarmManager1 != null && notifTime1 != -1) {
                          alarmManager1.set(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
                      }
...       
}

SQLiteDB.java

...
public Cursor resetNotifications() {

   SQLiteDatabase db = getReadableDatabase();

   String[] columns = new String[]{
                ItemContract.ItemEntry.COLUMN_NOTIFTIME,
                ItemContract.ItemEntry.COLUMN_NOTIFTIME2,
                ItemContract.ItemEntry.COLUMN_DUEDATENTIME,
                ItemContract.ItemEntry.COLUMN_RANDINT};

        return db.query(
                TABLE_NAME, 
                columns, // The columns to return
                null,      
                null,   
                null,      
                null,       
                null       
        ); 
}

This is the code I came up as a replacement:

public class RebootService extends JobIntentService {

// Unique job ID for this service
static final int JOB_ID = 10000;

// Convenience method for enqueueing work to this service.
public static void enqueueWork(Context context, Intent work) {
    enqueueWork(context, RebootService.class, JOB_ID, work);
}

private QuickcardRepository reposit1;

@Override
protected void onHandleWork(@NonNull Intent intent) {

    reposit1 = new QuickcardRepository(getApplication());

    Bundle extras = intent.getExtras(); // Returns the Intent *that started this Service.*
    if (extras != null) {

        String classname = extras.getString("TAG");

        if (classname != null && classname.equals("bootCompleted")) {

            AlarmManager alarmManager1;
            Intent brIntent1, brIntent2, brIntent3;
            PendingIntent pendingIntent1, pendingIntent2, pendingIntent3;

            Calendar cal1 = Calendar.getInstance();
            Calendar cal2 = Calendar.getInstance();
            Calendar cal3 = Calendar.getInstance();

            List<Quickcard> resetNotificationsList = reposit1.getNotifications();
            // Cycle through the Room database rows to get the Notifications data
            for (Quickcard quickcard: resetNotificationsList) {
                 // Quickcards without a Due date get a Due date in the database of -1.
                 // Therefore, only cycle through and get data for those quickcards that have
                //  a Due data and therefore have Notifications (reminders) where the actual
                //  "Due date" is != -1.
                 if (quickcard.getDuedatentime() != -1) {

                     // Set up the 24-Hour calendar object.
                     long notifTime1 = quickcard.getNotiftime();
                     int randInt1 = quickcard.getRandint();
                     cal1.setTime(new Date(notifTime1));

                     // Set up a system AlarmManager to fire a future alarm that sends a Notification
                     // even if the app is in the background or closed.  Have to add "context" here.
                     alarmManager1 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

                     // If the stated alarm trigger time is in the past, the alarm will be triggered immediately.
                     // So only set Alarms to fire Notifications for Duedates in the future
                     // (meaning > than the current System time).  Ignore those in the past.
                     if (cal1.getTimeInMillis() > System.currentTimeMillis()) {
                         // For the 24Hour Notifications.
                         // Set up a PendingIntent that will perform broadcast to the BroadcastReceiver.
                         brIntent1 = new Intent(this, AlarmReceiver.class);
                         brIntent1.setAction("24Hour");
                         // Need to use FLAG_ONE_SHOT on the PendingIntent, not FLAG_UPDATE_CURRENT.
                         // A random int is used to be able to set multiple alarms and then to be able to
                         // delete them later (if the user for ex., deletes the quickCards Duedate) using
                         // the same random int.
                         pendingIntent1 = PendingIntent.getBroadcast(this, randInt1, brIntent1,
                                 PendingIntent.FLAG_ONE_SHOT);

                         // Alarms have 3 properties below after "set(...)":
                         // 1) Alarm type:  RTC_WAKEUP type is chosen here to wake the device from sleep.
                         // 2) Trigger time: in this case, 24 hours before the Duedate/Duetime is reached.
                         // 3) Pending Intent:  A future Intent that is sent when the trigger time is reached.
                         int SDK_INT1 = Build.VERSION.SDK_INT;
                         if (SDK_INT1 >= Build.VERSION_CODES.M) {
                             // Wakes up the device in Doze Mode for API Level 23 and higher.
                             // The "... != -1" test only sets up pendingIntents for quickCards that have
                             // a Notification.  quickCards with no Duedate & Duetime are bypassed.
                             if (alarmManager1 != null && notifTime1 != -1) {
                                 alarmManager1.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(),
                                         pendingIntent1);
                             }
                         } else if (SDK_INT1 >= Build.VERSION_CODES.KITKAT) {
                             // Wakes up the device in Idle Mode for API Level 19 to 22.
                             // The "... != -1" test only sets up pendingIntents for quickCards that have
                             // a Notification.  quickCards with no Duedate & Duetime are bypassed.
                             if (alarmManager1 != null && notifTime1 != -1) {
                                 alarmManager1.setExact(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
                             }
                         } else {
                             // Old APIs Level 18 and below.
                             // The "... != -1" test only sets up pendingIntents for quickCards that have
                             // a Notification.  quickCards with no Duedate & Duetime are bypassed.
                             if (alarmManager1 != null && notifTime1 != -1) {
                                 alarmManager1.set(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
                             }
                         }
                     }
1
0
512
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я считаю, что @Сделка оборачивает код в транзакцию. Это уже сделано для всех, кроме @Запрос (если @Query не является запросом на обновление/удаление (если это запрос на обновление или удаление, он включается в транзакцию)).

Я считаю, что вопрос о том, должен ли быть заключен запрос SELECT в транзакцию (@Transaction @Query......), заключается в том, используется ли @Relation. Если это так, то список (списки) связанных/связанных элементов/объектов запускается как отдельные запросы, и, таким образом, запуск их всех в транзакции обеспечит согласованность данных. В противном случае существует вероятность того, что базовые данные могут быть изменены другими транзакциями, и, таким образом, результирующие данные могут быть несогласованными.

Сказать, что использование @Сделка там, где он не нужен, вряд ли повлияет, и может даже оказать положительное влияние, если он будет закодирован там, где он может быть непреднамеренно не закодирован.

Конечно, вы всегда можете вернуть курсор, используя Room. Вы можете взглянуть на Связывание таблиц с использованием базы данных Room в Android Studio, где есть несколько примеров.

Основываясь на вашем коде, в первую очередь на том, что у вас есть ItemContract с подклассом ItemEntry, тогда сущность для ItemEntry может быть в ItemEntry.java, согласно: -

@Entity
public class ItemEntry {

    @PrimaryKey(autoGenerate = true)
    private long id;
    @ColumnInfo(name = COLUMN_NOTIFTIME)
    private long notiftime;
    @ColumnInfo(name = COLUMN_NOTIFTIME2)
    private long notiftime2;
    @ColumnInfo(name = COLUMN_DUEDATENTIME)
    private long duedatentime;
    @ColumnInfo(name = COLUMN_RANDINT)
    public int randint;

    public ItemEntry(){

    }

    @Ignore
    public ItemEntry(long notiftime, long notiftime2, long duedatentime, int randint) {
        this.notiftime = notiftime;
        this.notiftime2 = notiftime2;
        this.duedatentime = duedatentime;
        this.randint = randint;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getNotiftime() {
        return notiftime;
    }

    public void setNotiftime(long notiftime) {
        this.notiftime = notiftime;
    }

    public long getNotiftime2() {
        return notiftime2;
    }

    public void setNotiftime2(long notiftime2) {
        this.notiftime2 = notiftime2;
    }

    public long getDuedatentime() {
        return duedatentime;
    }

    public void setDuedatentime(long duedatentime) {
        this.duedatentime = duedatentime;
    }

    public int getRandint() {
        return randint;
    }

    public void setRandint(int randint) {
        this.randint = randint;
    }
} 

Наряду с интерфейсом ItemEntryDao.java как: -

@Dao
interface ItemEntryDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long[] insertItemEntries(ItemEntry... itemEntries);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    long insertItemEntry(ItemEntry itemEntry);
    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateItemEnrties(ItemEntry... itemEntries);
    @Update(onConflict = OnConflictStrategy.IGNORE)
    int updateItemEntry(ItemEntry itemEntry);
    @Delete
    int deleteItemEntries(ItemEntry... itemEntries);
    @Delete
    int deleteItemEntry(ItemEntry itemEntry);
    @Query("SELECT * FROM ItemEntry")
    List<ItemEntry> resetNotifications();
}
  • @Query эквивалентен Cursor, НО возвращает список объектов ItemEntry.

Вышеприведенное можно использовать, например (который в основном копирует ваш код, но выводит извлеченные данные в журнал), например: -

public void onHandleWork() {

    ItemEntry ie = new ItemEntry();
    ie.setNotiftime(100);
    ie.setNotiftime2(200);
    ie.setDuedatentime(500000);
    ie.setRandint(567);
    mDB.getItemEntryDao().insertItemEntry(ie);
    List<ItemEntry> mylist = mDB.getItemEntryDao().resetNotifications();
    for (ItemEntry itementry: mylist) {
        Log.d("ITEMENTRY",
                "\n\tnotiftime= " + String.valueOf(itementry.getNotiftime()) +
                        "\n\tnotiftime2= " + String.valueOf(itementry.getNotiftime2()) +
                        "\n\tduedatetime= " + String.valueOf(itementry.getDuedatentime()) +
                        "\n\trandint= " + String.valueOf(itementry.getRandint())

        );
    }
}
  • mDB — это встроенный объект (т. е. экземпляр класса @Database)

Это приведет к (для первого запуска): -

05-28 14:31:14.587 7211-7211/aso.so56326640 D/ITEMENTRY:  notiftime= 100
      notiftime2= 200
      duedatetime= 500000
      randint= 567

Я понимаю. Любые мысли о том, как заменить приведенный выше код Cursor методом Dao?

AJW 28.05.2019 05:43

@AJW Добавлена ​​ссылка с несколькими примерами.

MikeT 28.05.2019 05:44

@AJW добавил пример, более конкретный/основанный на вашем коде.

MikeT 28.05.2019 06:43

Таким образом, в курсоре moveToNext() перебирает всю базу данных (все строки). В вашем ответе выше, выполняет ли «mDB.getItemEntryDao()...» итерацию по всей базе данных, чтобы получить данные для 4 столбцов и всех строк?

AJW 29.05.2019 01:57

Извините, while() в моем коде выше — это цикл, который перебирает курсор базы данных, а не moveToNext().

AJW 29.05.2019 02:04

@AJW mDB.getItemEntryDao() получает соответствующий DAO (объект доступа к данным, то есть класс с запросами/вставками/удалениями/обновлениями). Именно List<ItemEntry> mylist = mDB.getItemEntryDao().resetNotifications(); возвращает список объектов ItemEntry, что эквивалентно получению курсора. Затем цикл for выполняет итерацию по возвращенным объектам (если нет, то тело цикла не выполняется). Базовый цикл по базе данных выполняется по комнатам.

MikeT 29.05.2019 02:27

Понял. У меня есть много дополнительных столбцов в базе данных. Поэтому я не могу использовать "@Query("SELECT * FROM ItemEntry")". Как мне написать @Query, чтобы просто ВЫБРАТЬ данные базы данных только для 4 указанных столбцов?

AJW 29.05.2019 22:34

@AJW, почему ты не можешь? Возвращенные объекты ItemEntry будут содержать данные для 4 столбцов плюс другие столбцы. Я не знаю ни одного правила, которое говорит, что вы должны использовать каждое значение из возвращаемого объекта. Однако получение произвольных данных объясняется здесь android.arch.persistence.room.Query.

MikeT 29.05.2019 22:49

Извините, я должен был сказать, что не хотел бы возвращать данные всех столбцов, действительно из-за управления памятью. Я хочу просто получить данные для 4 столбцов, чтобы переустановить уведомления о тревоге приложения.

AJW 29.05.2019 23:34

@AJW, тогда просто следуйте и делайте, как говорится в ссылке, как сказано в предыдущем комментарии. Если у вас есть проблемы, то это другой вопрос. Я считаю, что я ответил на первоначальный вопрос, поэтому я считаю, что вы должны отметить это, а также, вероятно, также проголосовать.

MikeT 30.05.2019 00:19

Увлекся другими проблемами, и теперь я, наконец, возвращаюсь к решению этой проблемы. Ответ принят и одобрен (лучше поздно, чем никогда!). Ваше здоровье.

AJW 29.10.2019 01:35

Я попробовал код, который вы рекомендовали (см. редактирование выше, в котором добавлен весь новый код), и, к сожалению, уведомления не запускаются после перезагрузки устройства. Любые мысли или идеи?

AJW 31.10.2019 01:11

Другие вопросы по теме