#android #database #android-studio #android-room
Вопрос:
Я занимаюсь Android Studio всего месяц, и я должен сказать, что я немного запутался в базе данных комнат, извините, если вопрос звучит запутанно.
Я использую базу данных комнат, как указано, вот мои классы, связанные с базой данных:
@Entity(tableName="board")
public class BoardItem {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name="board_id")
private int boardId;
@ColumnInfo(name="board_name")
private String boardName;
@ColumnInfo(name="board_description")
private String boardDescription;
@ColumnInfo(name="board_image_list")
@TypeConverters(ImageListTypeConverter.class)
private List<ImageItem> boardImageList;
public BoardItem(String boardName, String boardDescription){
this.boardName = boardName;
this.boardDescription = boardDescription;
this.boardImageList = new ArrayList<>();
}
public int getBoardId() { return boardId; }
public String getBoardName() {
return boardName;
}
public String getBoardDescription() {
return boardDescription;
}
public String getPhotosCount() {
return String.valueOf(this.boardImageList.size());
}
public void setBoardId(int boardId) {
this.boardId = boardId;
}
public List<ImageItem> getBoardImageList() {
return boardImageList;
}
public void setBoardImageList(List<ImageItem> list) { this.boardImageList = list; }
@Dao
public interface BoardItemDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE)
void addBoardItem(BoardItem boardItem);
@Transaction
@Query("SELECT * from board ORDER BY board_id DESC")
LiveData<List<BoardItem>> getBoardItems();
@Transaction
@Query("SELECT * from board ORDER BY board_id DESC")
List<BoardItem> getBoardItemsNow();
}
@Database(entities = {BoardItem.class}, version = 1)
public abstract class BoardItemDatabase extends RoomDatabase {
public abstract BoardItemDAO boardItemDAO();
//Singleton
private static volatile BoardItemDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
static final ExecutorService databaseWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
static BoardItemDatabase getDatabase(final Context context){
if (INSTANCE == null) {
synchronized (BoardItemDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), BoardItemDatabase.class, "board_database")
.allowMainThreadQueries()
.build();
}
}
}
return INSTANCE;
}
}
//Repository
public class BoardItemRepository {
private BoardItemDAO boardItemDAO;
private LiveData<List<BoardItem>> boardItemList;
private List<BoardItem> boardItemListNow;
public BoardItemRepository (Application application) {
BoardItemDatabase db = BoardItemDatabase.getDatabase(application);
boardItemDAO = db.boardItemDAO();
boardItemList = boardItemDAO.getBoardItems();
boardItemListNow = boardItemDAO.getBoardItemsNow();
}
//Room executes all queries on a separate thread
//Observed LiveData will notify the observer when data has changed
public LiveData<List<BoardItem>> getBoardItemList() { return boardItemList; }
//this method is called on a non-UI thread or the app will throw an exception. Room ensures
//that there are no long running operations on the main thread, blocking the UI.
public void addBoardItem(final BoardItem boardItem) {
BoardItemDatabase.databaseWriteExecutor.execute(() -> boardItemDAO.addBoardItem(boardItem));
}
public List<BoardItem> getBoardItemListNow() {return boardItemListNow; }
}
Note that BoardItem and ImageItem are classes I made myself: a board is supposed to contain multiple ImageItems.
My boardItem has different fields, one of which is a list of ImageItems.
Now, in a specific fragment I try to update this list of ImageItems in a board that already exists in my database, which is the board with id = 0 (the very first board in the db). I try to retrieve the list from the Database and replace it with a new one.
I have used LiveData in certain cases to update the view of my app when the item change, but I have non LiveData methods for this specific piece of my code I need to change my database as soon as I click the button that contains this code:
List<BoardItem> boardItems = boardListViewModel.getBoardItemsNow();
newList = boardItems.get(0).getBoardImageList();
newList.add(newItem);
boardItems.get(0).setBoardImageList(newList);
Когда я нажимаю кнопку, код выполняется без ошибок, но база данных не обновляется; она содержит список, как и раньше, без нового элемента.
Заранее спасибо, еще раз извините, если это звучит запутанно!
РЕДАКТИРОВАТЬ: вот моя модель просмотра:
public class BoardListViewModel extends AndroidViewModel {
private final MutableLiveData<BoardItem> boardSelected = new MutableLiveData<>();
private LiveData<List<BoardItem>> boardItems;
private List<BoardItem> boardItemsNow;
public BoardListViewModel(@NonNull Application application) {
super(application);
BoardItemRepository repo = new BoardItemRepository(application);
boardItems = repo.getBoardItemList();
boardItemsNow = repo.getBoardItemListNow();
}
public void select(BoardItem boardItem) {
boardSelected.setValue(boardItem);
}
public LiveData<BoardItem> getSelected() {
return boardSelected;
}
public LiveData<List<BoardItem>> getBoardItems() {
return boardItems;
}
public BoardItem getBoardItem(int position) {
return boardItems.getValue() == null ? null : boardItems.getValue().get(position);
}
public List<BoardItem> getBoardItemsNow() { return boardItemsNow; }
}
Комментарии:
1. как вы используете живые данные?
2. @iamanbansal Я использую живые данные в другом фрагменте. У меня есть представление рециркулятора, и каждый раз, когда данные меняются, я обновляю представление рециркулятора:
boardListViewModel = new ViewModelProvider((ViewModelStoreOwner)activity) .get(BoardListViewModel.class); boardListViewModel.getBoardItems().observe((LifecycleOwner) activity, boardItems -> adapter.setData(boardItems));
3. что делает
setData
функция?4. Использовать метод вставки для сохранения данных?
5. Можете ли вы показать свои
view model
?
Ответ №1:
Я полагаю, что ваша проблема заключается в том, что вы не обновляете, а пытаетесь добавить (вставить) новый элемент (строку) с помощью :-
@Insert(onConflict = OnConflictStrategy.IGNORE)
void addBoardItem(BoardItem boardItem);
Поскольку boardid уже существует и является первичным ключом, который неявно уникален, возникает конфликт, и этот конфликт игнорируется, поэтому новая строка не добавляется, а база данных остается неизменной.
Что вам следует сделать, так это обновить существующую строку, поэтому вам нужен dao @Update.
Поэтому добавьте, а затем используйте следующий dao :-
@Update(onConflict = OnConflictStrategy.IGNORE)
int updateBoardItem(BoardItem boardItem);
int
возвращаемое значение-это количество строк, которые были обновлены (вы ожидали бы 1, 0, если бы конфликт привел к игнорированию обновления).
Комментарии:
1. Я думаю, что я бы реализовал метод updateBoardItem в своем репозитории, написав что-то вроде
BoardItemDatabase.databaseWriteExecutor.execute(() -> boardItemDAO.updateBoardItem(boardItem));
. Однако я не уверен, как я должен проверить, правильно ли обновлен элемент, и, следовательно, вернуть правильный int.2. Я применил ваше предложение, и теперь оно отлично работает! Я не думал, что это будет так же просто, как сделать запрос на обновление.