Как я могу вставить один элемент или список в позицию в БАЗЕ ДАННЫХ ANDROID

#android #android-sqlite #android-room

#Android #android-sqlite #android-room

Вопрос:

введите описание изображения здесь

Как вы можете видеть на рисунке, у меня есть два списка, песни и плейлисты. Итак, мне нужно добавить элемент или элементы в определенную позицию в КОМНАТЕ. И для этого я выполнил несколько шагов. например, я сначала подготовил список, как показано ниже

 playlist.add(index, songs.get(position));
 

Также при отладке я вижу, что этот элемент добавлен в правильный индекс.
Но теперь я сначала очищаю таблицу, а затем добавляю измененный СПИСОК ВОСПРОИЗВЕДЕНИЯ.

Но проблема в том, что вновь добавленный элемент всегда записывается в конце таблицы плейлистов. Так в чем же была моя ошибка? ПОЖАЛУЙСТА, ПОМОГИТЕ! ЛЮБЫЕ ПРЕДЛОЖЕНИЯ ПРИВЕТСТВУЮТСЯ.

 AppExecutors.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                myDao.clearPlaylist();                // CLEARING DB
                myDao.insertPlaylist(finalMList);     // ADDING NEW LIST
            }
        });
 

Надеюсь, вы поняли, что мне нужно! Спасибо.

Ответ №1:

Вы должны добавить столбец в список воспроизведения для хранения порядка воспроизведения, а при извлечении списка воспроизведения ORDER BY — порядок воспроизведения. Однако затем вам необходимо изменить порядок воспроизведения при вставке.

Переупорядочение может быть обработано ОБНОВЛЕНИЕМ до вставки, которое может быть ТРИГГЕРОМ (обратите внимание, что Room не имеет аннотаций для определения триггеров, поэтому вам придется вводить ТРИГГЕР либо через обратный вызов onCreate, либо onOpen).

Без ТРИГГЕРА

Ниже демонстрируется техника без использования триггера :-

 DROP TABLE IF EXISTS playlist;
DROP TABLE IF EXISTS song;
CREATE TABLE IF NOT EXISTS song (songid INTEGER PRIMARY KEY, songname TEXT);
INSERT INTO song (songname) VALUES('Song05'),('Song06'),('Song02'),('Song03'),('Song01'),('Song07'),('Song04');
/*
    song id's will be like:-
    
    Song01 = 5
    Song02 = 3
    Song03 = 4
    Song04 = 7
    Song05 = 1
    Song06 = 2
    Song07 = 6  
*/
CREATE TABLE IF NOT EXISTS playlist (id INTEGER PRIMARY KEY, songid, play_order);

/* Adding a song to the playlist */

/* Add Song03  as first in playlist */
UPDATE playlist 
    SET play_order = play_order   1 
        WHERE play_order >= 1 /*order_of_song_to_be_added*/
;
INSERT INTO playlist (songid,play_order) VALUES
    (4,1) /* Add Song03  as first in playlist */
;

/* Add Song05 as second */
UPDATE playlist 
SET play_order = play_order   1 
    WHERE play_order >= 2 /*order_of_song_to_be_added*/
;
INSERT INTO playlist (songid,play_order) VALUES
    (1,2) /* Add Song05 as second */
;

/* Add Song07 as third */
UPDATE playlist 
SET play_order = play_order   1 
    WHERE play_order >= 3 /*order_of_song_to_be_added*/;
INSERT INTO playlist (songid,play_order) VALUES
    (6,3) /* Add Song05 as second */
;

/* Add Song05 as third (moves Song07 to fourth) */
UPDATE playlist 
SET play_order = play_order   1 
    WHERE play_order >= 3 /*order_of_song_to_be_added*/;
INSERT INTO playlist (songid,play_order) VALUES
    (5,3) /* Add Song05 as second */
;


/* Add Song06 as first (moves all other songs up 1) */
UPDATE playlist 
SET play_order = play_order   1 
    WHERE play_order >= 1 /*order_of_song_to_be_added*/;
INSERT INTO playlist (songid,play_order) VALUES
    (2,1) /* Add Song05 as second */
;

SELECT play_order, songname 
FROM playlist JOIN song ON song.songid = playlist.songid
ORDER BY play_order;
    
DROP TABLE IF EXISTS playlist;
DROP TABLE IF EXISTS song;
 
  • т.е. при каждой вставке все значения play_order, превышающие или равные новому play_order, увеличиваются на 1, а затем вставляется новая запись плейлиста.

Вышеуказанные результаты :-

введите описание изображения здесь

Обновление insert @Dao будет выглядеть следующим образом :-

 @Query("UPDATE playlist SET play_order = play_order   1 WHERE play_order >=:insertion_point")
void prepareForInsertion(long insertion_point);
 
  • обратите внимание, что ОБНОВЛЕНИЕ будет НЕ @Update, а @Query, поскольку удобство @Update не подходит.

Тогда у вас будет что-то вроде

 myDao.clearPlaylist();
for(Playlist p: finalMlist) {
    myDao.prepareForInsertion(p.play_order);
    myDao.insertPlaylist(p);
}
 

или у вас может быть метод в классе @Dao (если это абстрактный класс, а не интерфейс), например :-

 @Transaction
@Query("") 
void insertPlaylistEntry(Playlist playlist) {
    prepareForInsertion(p.play_order);
    insertPlaylist(p);
}
 

С помощью ТРИГГЕРА

 DROP TABLE IF EXISTS playlist;
DROP TABLE IF EXISTS song;

CREATE TABLE IF NOT EXISTS song (songid INTEGER PRIMARY KEY, songname TEXT);
INSERT INTO song (songname) VALUES('Song05'),('Song06'),('Song02'),('Song03'),('Song01'),('Song07'),('Song04');
/*
    song id's will be like:-
    
    Song01 = 5
    Song02 = 3
    Song03 = 4
    Song04 = 7
    Song05 = 1
    Song06 = 2
    Song07 = 6  
*/
CREATE TABLE IF NOT EXISTS playlist (id INTEGER PRIMARY KEY, songid, play_order);
CREATE TRIGGER IF NOT EXISTS playlist_before_insert_reorder 
    BEFORE INSERT ON playlist
    BEGIN
        UPDATE playlist SET play_order = play_order   1 WHERE play_order >= new.play_order;
    END
;
    
INSERT INTO playlist (songid,play_order) VALUES
    (4,1) /* Add Song03  as first in playlist */
;
INSERT INTO playlist (songid,play_order) VALUES
    (1,2) /* Add Song05 as second */
;
INSERT INTO playlist (songid,play_order) VALUES
    (6,3) /* Add Song05 as second */
;

INSERT INTO playlist (songid,play_order) VALUES
    (5,3) /* Add Song05 as second */
;

INSERT INTO playlist (songid,play_order) VALUES
    (2,1) /* Add Song05 as second */
;

SELECT play_order, songname 
FROM playlist JOIN song ON song.songid = playlist.songid
ORDER BY play_order;
 

Как вы можете видеть, триггер выполняет свою работу :-

введите описание изображения здесь

Рабочий пример с использованием триггера

Песня :-

 @Entity(tableName = Song.TABLE_NAME)
class Song {
    public static final String TABLE_NAME = "song";
    public static final String COL_ID = TABLE_NAME "id";
    public static final String COL_SONGNAME = TABLE_NAME "_name";
    @PrimaryKey
    @ColumnInfo(name = COL_ID)
    Long id = null;
    @ColumnInfo(name = COL_SONGNAME)
    String name;

    Song(){}
    @Ignore
    Song(String name) {
        this.id = null;
        this.name = name;
    }
}
 

Список воспроизведения :-

 @Entity(
        tableName = Playlist.TABLE_NAME,
        foreignKeys = {
                @ForeignKey(
                        entity = Song.class,
                        parentColumns = Song.COL_ID,
                        childColumns = Playlist.COL_SONG_MAP,
                        onDelete = ForeignKey.CASCADE,
                        onUpdate = ForeignKey.CASCADE
                )
        }
)
class Playlist {
    public static final String TABLE_NAME = "playlist";
    public static final String COL_ID = TABLE_NAME "id";
    public static final String COL_SONG_MAP = TABLE_NAME "_" Song.TABLE_NAME "_map";
    public static final String COL_PLAY_ORDER = TABLE_NAME "_play_order";

    @PrimaryKey
    @ColumnInfo(name = COL_ID)
    Long id = null;
    @ColumnInfo(name = COL_SONG_MAP, index = true)
    long songid;
    @ColumnInfo(name = COL_PLAY_ORDER, index = true)
    long play_order;

    Playlist(){}
    @Ignore
    Playlist(long songid, long play_order){
        this.id = null;
        this.songid = songid;
        this.play_order = play_order;
    }
}
 

PlaylistWithSong POJO, чтобы собрать оба вместе

 class PlaylistWithSong {
    @Embedded
    Playlist playlist;
    @Embedded
    Song song;
}
 

AllDao

 @Dao
abstract class AllDao {

    @Insert
    abstract long insert(Song song);
    @Insert
    abstract long insert(Playlist playList);
    @Insert
    abstract long[] insertSongs(List<Song> songList);
    @Insert
    abstract long[] insertPlaylists(List<Playlist> playListList);
    @Query("DELETE FROM "   Playlist.TABLE_NAME)
    abstract int clearPLayList();
    @Query("SELECT * FROM "   Playlist.TABLE_NAME  
            " JOIN "   Song.TABLE_NAME  
            " ON "   Song.TABLE_NAME   "."   Song.COL_ID   "="   Playlist.TABLE_NAME   "."   Playlist.COL_SONG_MAP  
            " ORDER BY "   Playlist.COL_PLAY_ORDER   " ASC")
    abstract List<PlaylistWithSong> getOrderedPlayList();
}
 

База данных класса @Database :-

 @Database(entities = {Song.class,Playlist.class},version = 1)
abstract class TheDatabase extends RoomDatabase {
    abstract AllDao getAllDao();

    private static volatile TheDatabase instance = null;
    static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(
                    context, TheDatabase.class,"thedatabase.db"
            )
                    .allowMainThreadQueries()
                    .addCallback(CALLBACK)
                    .build();
        }
        return instance;
    }

    /*
        CREATE TRIGGER IF NOT EXISTS playlist_before_insert_reorder
            BEFORE INSERT ON playlist
            BEGIN
                UPDATE playlist SET play_order = play_order   1 WHERE play_order >= new.play_order;
            END
        ;
     */
    private static final String playlist_reorder_trigger =
            "CREATE TRIGGER IF NOT EXISTS "   Playlist.COL_PLAY_ORDER   "_before_insert_reorder "  
                    "BEFORE INSERT ON "   Playlist.TABLE_NAME   " BEGIN "  
                    "UPDATE "   Playlist.TABLE_NAME   " "  
                    "SET "   Playlist.COL_PLAY_ORDER   "="   Playlist.COL_PLAY_ORDER   "   1 "  
                    "WHERE "   Playlist.COL_PLAY_ORDER   " >= new."   Playlist.COL_PLAY_ORDER  ";"  
                    "END;";

    static RoomDatabase.Callback CALLBACK = new RoomDatabase.Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
            db.execSQL(playlist_reorder_trigger);
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
            /* Just in case not created in onCreate e.g. migration */
            db.execSQL(playlist_reorder_trigger);
        }

        @Override
        public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };
}
 

Finally

An Activity that demonstrates the asnwer above :-

 public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    AllDao dao;

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


        db = TheDatabase.getInstance(this);
        dao = db.getAllDao();

        long s5id = dao.insert(new Song("Song05"));
        long s6id = dao.insert(new Song("Song06"));
        long s2id = dao.insert(new Song("Song02"));
        long s3id = dao.insert(new Song("Song03"));
        long s1id = dao.insert(new Song("Song01"));
        long s7id = dao.insert(new Song("Song07"));
        long s4id = dao.insert(new Song("Song04"));

        List<Playlist> playlistList = Arrays.asList(
                new Playlist(s3id,1),
                new Playlist(s5id,2),
                new Playlist(s7id,3),
                new Playlist(s1id,3),
                new Playlist(s6id,1));

        dao.clearPLayList();
        dao.insertPlaylists(playlistList);
        for (PlaylistWithSong p: dao.getOrderedPlayList()) {
            Log.d("DBINFO","Song is "   p.song.name   " play order is "   p.playlist.play_order);
        }
    }
}
 

that is:-

  1. добавляет несколько песен (псевдо без разбора в отношении порядка)
  2. создает список воспроизведения в виде массива списков воспроизведения с порядком воспроизведения.
  3. вставляет список воспроизведения
  4. извлекает список воспроизведения и выводит песни в ожидаемом порядке согласно

:-

 2021-12-01 08:41:49.706 D/DBINFO: Song is Song06 play order is 1
2021-12-01 08:41:49.706 D/DBINFO: Song is Song03 play order is 2
2021-12-01 08:41:49.706 D/DBINFO: Song is Song05 play order is 3
2021-12-01 08:41:49.707 D/DBINFO: Song is Song01 play order is 4
2021-12-01 08:41:49.707 D/DBINFO: Song is Song07 play order is 5
 
  • т.е. Песня 07, первоначально вставленная в порядке 3, была перемещена в порядок 5, потому что а) вставка Song01 в 3 переместила Song07 на 4, а затем б) вставка Song06 в 1 переместила все из (1-4) в (2-5).

Комментарии

Я не поддерживаю PLAY_ORDER.

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

Но при вставке в базу данных ROOM новый элемент записывается в конце таблицы. почему?

Не существует понятия спереди, в конце или между ними. При извлечении данных вы используете ORDER BY (столбец или столбцы), если вы не укажете ПОРЯДОК, тогда ПОРЯДОК будет таким, как считает SQLite (что часто будет через первичный ключ). Таким образом, для вашего случая вам НУЖЕН столбец, в котором указан желаемый ПОРЯДОК (порядок воспроизведения).

Если таблица (список воспроизведения) имеет псевдоним rowid (один столбец для первичного ключа, который имеет целочисленный тип), то значение этого столбца, если оно не задано специально, обычно будет на 1 больше, чем наибольшее значение столбца (1, если нет других строк).

Кроме того, если вы указали autogenerate = true (что соответствует АВТОИНКРЕМЕНТУ SQLite), то это значение будет на 1 или более больше, чем самое высокое из когда-либо использованных значений, и, следовательно, если вы удалите все строки и список воспроизведения, в который сверхурочно было вставлено 103 строки, тогда следующая строка будет 104, даже если это последнее значение.только строка.

  • использование autogenerate = true aka AUTOINCREMENT , согласно ссылке, практически определенно не требуется и поэтому практически определенно неэффективно.

Если у вас есть autogenerate = false (подразумевается, не указывая autogenerate = true ), то, если все строки будут удалены, первое значение будет равно 1 (скорее всего).

Комментарии:

1. Большое вам спасибо за ответ. Я не поддерживаю PLAY_ORDER. У меня просто есть список. Поэтому, когда я вставляю элемент в список, он действительно там. Но при вставке в базу данных ROOM новый элемент записывается в конце таблицы. почему?

2. Допустим, у нас есть список воспроизведения[ 1, 2, 3, 4, 5] Теперь мы хотим добавить один элемент в список, подобный этому списку воспроизведения[1, 2, 104, 3, 4, 5] Итак, здесь 104 — это новый пункт. И, как и в этом порядке, мне нужно сохранить весь этот список. Но 104 всегда записывается в конце индекса ТАБЛИЦЫ ROOM. Пожалуйста, объясните, почему.

3. Объяснение @SubhojitHalder заключается в том, что вы подразумеваете порядок, который является порядком вставки, и что вы не указываете порядок. Не имея столбца, который соответствует нужному вам порядку.

4. Я этого не понял.

5. Большое вам спасибо за ваше полное объяснение. Я только что добился того, что мне было нужно! Итак, что я сделал, это просто сбросил _ID (primary_key) по setID класса МОДЕЛИ внутри ЦИКЛА FOR. И теперь ROOM может хранить мой упорядоченный список. Еще раз спасибо. Это был отличный вопрос.