#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:-
- добавляет несколько песен (псевдо без разбора в отношении порядка)
- создает список воспроизведения в виде массива списков воспроизведения с порядком воспроизведения.
- вставляет список воспроизведения
- извлекает список воспроизведения и выводит песни в ожидаемом порядке согласно
:-
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
akaAUTOINCREMENT
, согласно ссылке, практически определенно не требуется и поэтому практически определенно неэффективно.
Если у вас есть 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 может хранить мой упорядоченный список. Еще раз спасибо. Это был отличный вопрос.