W ten sposób wstawiam dane do bazy danych za pomocą biblioteki trwałości pomieszczeń:
Jednostka:
@Entity
class User {
@PrimaryKey(autoGenerate = true)
public int id;
//...
}
Obiekt dostępu do danych:
@Dao
public interface UserDao{
@Insert(onConflict = IGNORE)
void insertUser(User user);
//...
}
Czy można zwrócić identyfikator użytkownika po zakończeniu wstawiania w samej powyższej metodzie bez pisania oddzielnego zapytania wybierającego?
android
persistent-storage
android-room
SpiralDev
źródło
źródło
int
lublong
zamiastvoid
w wyniku@Insert
operacji?aSyncTask
? w jaki sposób zwracasz wartość z funkcji repozytorium?Odpowiedzi:
Na podstawie dokumentacji tutaj (poniżej fragmentu kodu)
Metoda z
@Insert
adnotacją może zwrócić:źródło
@Insert
Funkcja zwracavoid
,long
,long[]
iList<Long>
. Spróbuj tego.@Insert(onConflict = OnConflictStrategy.REPLACE) long insert(User user); // Insert multiple items @Insert(onConflict = OnConflictStrategy.REPLACE) long[] insert(User... user);
źródło
return Single.fromCallable(() -> dbService.YourDao().insert(mObject));
Zwracana wartość wstawienia dla jednego rekordu będzie wynosić 1, jeśli wyciąg zakończy się pomyślnie.
Jeśli chcesz wstawić listę obiektów, możesz skorzystać z:
@Insert(onConflict = OnConflictStrategy.REPLACE) public long[] addAll(List<Object> list);
I wykonaj to za pomocą Rx2:
Observable.fromCallable(new Callable<Object>() { @Override public Object call() throws Exception { return yourDao.addAll(list<Object>); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Object>() { @Override public void accept(@NonNull Object o) throws Exception { // the o will be Long[].size => numbers of inserted records. } });
źródło
Uzyskaj identyfikator wiersza za pomocą następującego snipletu. Używa wywoływalnego na ExecutorService z przyszłością.
private UserDao userDao; private ExecutorService executorService; public long insertUploadStatus(User user) { Callable<Long> insertCallable = () -> userDao.insert(user); long rowId = 0; Future<Long> future = executorService.submit(insertCallable); try { rowId = future.get(); } catch (InterruptedException e1) { e1.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return rowId; }
Ref: Samouczek dotyczący usługi Java Executor, aby uzyskać więcej informacji na temat wywoływanych.
źródło
W twoim Dao, zapytanie wstawiające zwraca
Long
np. Wstawiony rowId.@Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(recipes: CookingRecipes): Long
W klasie Twojego modelu (repozytorium): (MVVM)
fun addRecipesData(cookingRecipes: CookingRecipes): Single<Long>? { return Single.fromCallable<Long> { recipesDao.insertManual(cookingRecipes) } }
W klasie ModelView: (MVVM) Obsługa LiveData za pomocą DisposableSingleObserver.
Odniesienie do źródła pracy: https://github.com/SupriyaNaveen/CookingRecipes
źródło
Po wielu zmaganiach udało mi się to rozwiązać. Oto moje rozwiązanie wykorzystujące architekturę MMVM:
Student.kt
@Entity(tableName = "students") data class Student( @NotNull var name: String, @NotNull var password: String, var subject: String, var email: String ) { @PrimaryKey(autoGenerate = true) var roll: Int = 0 }
Student replace.kt
interface StudentDao { @Insert fun insertStudent(student: Student) : Long }
StudentRepository.kt
class StudentRepository private constructor(private val studentDao: StudentDao) { fun getStudents() = studentDao.getStudents() fun insertStudent(student: Student): Single<Long>? { return Single.fromCallable( Callable<Long> { studentDao.insertStudent(student) } ) } companion object { // For Singleton instantiation @Volatile private var instance: StudentRepository? = null fun getInstance(studentDao: StudentDao) = instance ?: synchronized(this) { instance ?: StudentRepository(studentDao).also { instance = it } } } }
StudentViewModel.kt
class StudentViewModel (application: Application) : AndroidViewModel(application) { var status = MutableLiveData<Boolean?>() private var repository: StudentRepository = StudentRepository.getInstance( AppDatabase.getInstance(application).studentDao()) private val disposable = CompositeDisposable() fun insertStudent(student: Student) { disposable.add( repository.insertStudent(student) ?.subscribeOn(Schedulers.newThread()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribeWith(object : DisposableSingleObserver<Long>() { override fun onSuccess(newReturnId: Long?) { Log.d("ViewModel Insert", newReturnId.toString()) status.postValue(true) } override fun onError(e: Throwable?) { status.postValue(false) } }) ) } }
We fragmencie:
class RegistrationFragment : Fragment() { private lateinit var dataBinding : FragmentRegistrationBinding private val viewModel: StudentViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initialiseStudent() viewModel.status.observe(viewLifecycleOwner, Observer { status -> status?.let { if(it){ Toast.makeText(context , "Data Inserted Sucessfully" , Toast.LENGTH_LONG).show() val action = RegistrationFragmentDirections.actionRegistrationFragmentToLoginFragment() Navigation.findNavController(view).navigate(action) } else Toast.makeText(context , "Something went wrong" , Toast.LENGTH_LONG).show() //Reset status value at first to prevent multitriggering //and to be available to trigger action again viewModel.status.value = null //Display Toast or snackbar } }) } fun initialiseStudent() { var student = Student(name =dataBinding.edName.text.toString(), password= dataBinding.edPassword.text.toString(), subject = "", email = dataBinding.edEmail.text.toString()) dataBinding.viewmodel = viewModel dataBinding.student = student } }
Użyłem DataBinding.Oto mój XML:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="student" type="com.kgandroid.studentsubject.data.Student" /> <variable name="listener" type="com.kgandroid.studentsubject.view.RegistrationClickListener" /> <variable name="viewmodel" type="com.kgandroid.studentsubject.viewmodel.StudentViewModel" /> </data> <androidx.core.widget.NestedScrollView android:id="@+id/nestedScrollview" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" tools:context="com.kgandroid.studentsubject.view.RegistrationFragment"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constarintLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:isScrollContainer="true"> <TextView android:id="@+id/tvRoll" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:gravity="center_horizontal" android:text="Roll : 1" android:textColor="@color/colorPrimary" android:textSize="18sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/edName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:layout_marginEnd="16dp" android:ems="10" android:inputType="textPersonName" android:text="Name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/tvRoll" /> <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:text="Name:" android:textColor="@color/colorPrimary" android:textSize="18sp" app:layout_constraintBaseline_toBaselineOf="@+id/edName" app:layout_constraintEnd_toStartOf="@+id/edName" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/tvEmail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Email" android:textColor="@color/colorPrimary" android:textSize="18sp" app:layout_constraintBaseline_toBaselineOf="@+id/edEmail" app:layout_constraintEnd_toStartOf="@+id/edEmail" app:layout_constraintStart_toStartOf="parent" /> <EditText android:id="@+id/edEmail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:layout_marginEnd="16dp" android:ems="10" android:inputType="textPersonName" android:text="Name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/edName" /> <TextView android:id="@+id/textView6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Password" android:textColor="@color/colorPrimary" android:textSize="18sp" app:layout_constraintBaseline_toBaselineOf="@+id/edPassword" app:layout_constraintEnd_toStartOf="@+id/edPassword" app:layout_constraintStart_toStartOf="parent" /> <EditText android:id="@+id/edPassword" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:layout_marginEnd="16dp" android:ems="10" android:inputType="textPersonName" android:text="Name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/edEmail" /> <Button android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginTop="24dp" android:layout_marginEnd="32dp" android:background="@color/colorPrimary" android:text="REGISTER" android:onClick="@{() -> viewmodel.insertStudent(student)}" android:textColor="@android:color/background_light" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/edPassword" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> </layout>
Dużo walczyłem, aby to osiągnąć z asynctask, ponieważ operacje wstawiania i usuwania pokoju muszą być wykonywane w osobnym wątku. Wreszcie można to zrobić z typem pojedynczym obserwowanym w RxJava.
Oto zależności Gradle dla rxjava:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' implementation 'io.reactivex.rxjava2:rxjava:2.0.3'
źródło
Zgodnie z dokumentacją funkcje z adnotacją @Insert mogą zwracać rowId.
Problem, który mam z tym polega na tym, że zwraca on rowId, a nie identyfikator, i nadal nie dowiedziałem się, jak uzyskać identyfikator za pomocą rowId.
Niestety nie mogę jeszcze komentować, ponieważ nie mam 50 punktów reputacji, więc zamiast tego zamieszczam to jako odpowiedź.
Edycja: teraz wiem, jak uzyskać identyfikator z rowId. Oto polecenie SQL:
SELECT id FROM table_name WHERE rowid = :rowId
źródło