Android Room - Get the id of new inserted row with auto-generate

前端 未结 7 1232
野性不改
野性不改 2020-11-29 00:03

This is how I am inserting data into database using Room Persistence Library:

Entity:

@Entity
class User {
    @PrimaryKey(autoGenerate = true)
    p         


        
相关标签:
7条回答
  • 2020-11-29 00:24

    In your Dao, the insert query returns Long i.e. the rowId inserted.

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

    In your Model(Repository) class : (MVVM)

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

    In your ModelView class: (MVVM) Handle LiveData with DisposableSingleObserver.
    Working sourcer reference : https://github.com/SupriyaNaveen/CookingRecipes

    0 讨论(0)
  • 2020-11-29 00:24

    After a lot of struggle, I managed to solve this. Here is my solution using MMVM architecture:

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

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

    I have used DataBinding.Here is my 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>
    

    I have struggled a lot to accomplish this with asynctask as room insert and delete operation must be done in a separate thread. Finally able to do this with Single type observable in RxJava.

    Here is Gradle dependencies for rxjava:

    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.0.3' 
    
    0 讨论(0)
  • 2020-11-29 00:30

    The return value of the insertion for one record will be 1 if your statement successfully.

    In case you want to insert list of objects, you can go with:

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

    And execute it with 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.
    
            }
        });
    
    0 讨论(0)
  • 2020-11-29 00:36

    According to the documentation functions annoted with @Insert can return the rowId.

    If the @Insert method receives only 1 parameter, it can return a long, which is the new rowId for the inserted item. If the parameter is an array or a collection, it should return long[] or List<Long> instead.

    The problem I have with this is that it returns the rowId and not the id and I still haven't found out how to get the id using the rowId.

    Sadly I can't comment yet, because I don't have 50 reputation, so I am posting this as an answer instead.

    Edit: I now know how to get the id from the rowId. Here is the SQL command:

    SELECT id FROM table_name WHERE rowid = :rowId
    
    0 讨论(0)
  • 2020-11-29 00:41

    Get the row ID by the following sniplet. It uses callable on an ExecutorService with 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 for more information on Callable.

    0 讨论(0)
  • 2020-11-29 00:45

    @Insert function can return void, long, long[] or List<Long>. Please try this.

     @Insert(onConflict = OnConflictStrategy.REPLACE)
      long insert(User user);
    
     // Insert multiple items
     @Insert(onConflict = OnConflictStrategy.REPLACE)
      long[] insert(User... user);
    
    0 讨论(0)
提交回复
热议问题