Android Room - Holen Sie sich die ID der neu eingefügten Zeile mit der automatischen Generierung


137

So füge ich Daten mithilfe der Room Persistence Library in die Datenbank ein:

Entität:

@Entity
class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    //...
}

Datenzugriffsobjekt:

@Dao
public interface UserDao{
    @Insert(onConflict = IGNORE)
    void insertUser(User user);
    //...
}

Ist es möglich, die ID des Benutzers zurückzugeben, sobald das Einfügen in der obigen Methode abgeschlossen ist, ohne eine separate Auswahlabfrage zu schreiben?


1
Haben Sie versucht, intoder longstatt voidals Ergebnis der @InsertOperation zu verwenden?
MatPag

Noch nicht. Ich werde einen Versuch geben!
DeveScie

Ich habe auch eine Antwort hinzugefügt, weil ich die Referenz in der Dokumentation gefunden habe und ich bin ziemlich sicher, dass es funktionieren wird;)
MatPag

3
wird das nicht mit einem gemacht aSyncTask? Wie geben Sie den Wert aus Ihrer Repository-Funktion zurück?
Nimitz14

Antworten:


190

Basierend auf der Dokumentation hier (unter dem Code-Snippet)

Eine mit der @InsertAnnotation versehene Methode kann Folgendes zurückgeben:

  • long für den Einzeleinsatz
  • long[]oder Long[]oder List<Long>für mehrere Einfügevorgänge
  • void wenn Sie sich nicht für die eingefügten IDs interessieren

4
Warum steht in der Dokumentation int für ID-Typ, gibt aber lange zurück? Nimmt man an, dass die ID niemals groß genug sein wird, um lang zu sein? Also sind die Zeilen-ID und die automatisch generierte ID buchstäblich dasselbe?
Michael Vescovo

11
In SQLite ist die größte Primärschlüssel-ID, die Sie haben können, eine 64-Bit-Ganzzahl mit Vorzeichen, sodass der Maximalwert 9.223.372.036.854.775.807 beträgt (nur positiv, da es sich um eine ID handelt). In Java ist ein int eine 32-Bit-Nummer mit Vorzeichen und ein maximaler positiver Wert von 2.147.483.647, sodass nicht alle IDs dargestellt werden können. Sie müssen ein Java-Long verwenden, dessen Maximalwert 9.223.372.036.854.775.807 beträgt, um alle IDs darzustellen. Die Dokumentation ist nur zum Beispiel, aber die API wurde unter diesem
Gesichtspunkt entwickelt

2
ok also es sollte wirklich lange dauern. Aber vielleicht gibt es in den meisten Fällen nicht 9 Milliarden Zeilen in einer SQLite-Datenbank, daher verwenden sie int als Beispiel für die Benutzer-ID, da sie weniger Speicher benötigt (oder ein Fehler ist). Das nehme ich daraus. Vielen Dank für die Erklärung, warum es lange zurückkehrt.
Michael Vescovo

3
Sie haben Recht, aber die APIs von Room sollten auch im schlimmsten Fall funktionieren und den Spezifikationen von SQlite entsprechen. Die Verwendung eines int über einen langen
Zeitraum

1
@MatPag Ihr ursprünglicher Link enthielt keine Bestätigung dieses Verhaltens mehr (und leider auch nicht die API-Referenz für die Insert-Annotation des Raums ). Nach einem wenig Suchen fand ich diese und den Link in Ihrer Antwort aktualisiert. Hoffentlich bleibt es ein bisschen besser als das letzte, da dies eine ziemlich wichtige Information ist.
CodeClown42

27

@InsertFunktion kann zurückkehren void, long, long[]oder List<Long>. Bitte versuchen Sie dies.

 @Insert(onConflict = OnConflictStrategy.REPLACE)
  long insert(User user);

 // Insert multiple items
 @Insert(onConflict = OnConflictStrategy.REPLACE)
  long[] insert(User... user);

5
return Single.fromCallable(() -> dbService.YourDao().insert(mObject));
verletzt

8

Der Rückgabewert der Einfügung für einen Datensatz ist 1, wenn Ihre Anweisung erfolgreich ist.

Wenn Sie eine Liste von Objekten einfügen möchten, können Sie Folgendes ausführen:

@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] addAll(List<Object> list);

Und führen Sie es mit Rx2 aus:

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.

        }
    });

1
"Der Rückgabewert der Einfügung für einen Datensatz ist 1, wenn Ihre Anweisung erfolgreich war" -> Gemäß dieser Dokumentation: developer.android.com/training/data-storage/room/accessing-data "Wenn die @ Insert-Methode nur empfängt 1 Parameter, es kann eine lange zurückgeben, die die neue rowId für das eingefügte Element ist. Wenn der Parameter ein Array oder eine Sammlung ist, sollte er stattdessen long [] oder List <Long> zurückgeben . "
CodeClown42

4

Holen Sie sich die Zeilen-ID mit dem folgenden Sniplet. Es verwendet callable auf einem ExecutorService mit Future.

 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: Java Executor Service - Tutorial für weitere Informationen über aufrufbare.


3

In Ihrem Dao gibt Longdie Einfügeabfrage dh die eingefügte Zeilen-ID zurück.

 @Insert(onConflict = OnConflictStrategy.REPLACE)
 fun insert(recipes: CookingRecipes): Long

In Ihrer Model (Repository) -Klasse: (MVVM)

fun addRecipesData(cookingRecipes: CookingRecipes): Single<Long>? {
        return Single.fromCallable<Long> { recipesDao.insertManual(cookingRecipes) }
}

In Ihrer ModelView-Klasse: (MVVM) Behandeln Sie LiveData mit DisposableSingleObserver.
Working Sourcer Referenz: https://github.com/SupriyaNaveen/CookingRecipes


1

Nach vielen Kämpfen gelang es mir, dies zu lösen. Hier ist meine Lösung mit MMVM-Architektur:

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
}

StudentDao.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)
                    }

                })
        )
    }
}

Im Fragment:

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
    }
}

Ich habe DataBinding verwendet. Hier ist mein 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>

Ich habe viel Mühe gehabt, dies mit asynctask zu erreichen, da der Vorgang zum Einfügen und Löschen von Räumen in einem separaten Thread ausgeführt werden muss. Endlich in der Lage, dies mit Single zu tun in RxJava Art beobachtet werden .

Hier sind Gradle-Abhängigkeiten für rxjava:

implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.3' 

0

Gemäß der mit @Insert annotierten Dokumentation können Funktionen die rowId zurückgeben.

Wenn die @ Insert-Methode nur 1 Parameter empfängt, kann sie eine lange zurückgeben, die die neue rowId für das eingefügte Element ist. Wenn der Parameter ein Array oder eine Sammlung ist, sollte er stattdessen long [] oder List <Long> zurückgeben.

Das Problem dabei ist, dass es die rowId und nicht die ID zurückgibt und ich immer noch nicht herausgefunden habe, wie ich die ID mithilfe der rowId erhalten kann.

Leider kann ich noch keinen Kommentar abgeben, da ich keinen Ruf von 50 habe. Deshalb poste ich dies stattdessen als Antwort.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.