Ruang Android - Dapatkan id dari baris baru yang disisipkan dengan penghasil otomatis

138

Ini adalah bagaimana saya memasukkan data ke dalam database menggunakan Room Persistence Library:

Kesatuan:

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

Objek akses data:

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

Apakah mungkin untuk mengembalikan id Pengguna setelah penyisipan selesai dalam metode di atas sendiri tanpa menulis kueri pemilihan yang terpisah?

SpiralDev
sumber
1
Sudahkah Anda mencoba menggunakan intatau longbukannya voidsebagai hasil @Insertoperasi?
MatPag
Belum. Saya akan mencoba!
SpiralDev
Saya telah menambahkan jawaban juga karena saya telah menemukan referensi dalam dokumentasi dan saya cukup yakin itu akan berhasil;)
MatPag
3
tidakkah ini akan dilakukan dengan aSyncTask? bagaimana Anda mengembalikan nilai dari fungsi repositori Anda?
Nimitz14

Jawaban:

191

Berdasarkan dokumentasi di sini (di bawah cuplikan kode)

Metode yang dijelaskan dengan @Insertanotasi dapat mengembalikan:

  • long untuk operasi penyisipan tunggal
  • long[]atau Long[]atau List<Long>untuk beberapa operasi insert
  • void jika Anda tidak peduli dengan id yang dimasukkan
MatPag
sumber
4
mengapa dalam dokumentasi itu dikatakan int untuk tipe id tetapi kembali lama? apakah asumsi id tidak akan pernah cukup besar untuk menjadi panjang? jadi id baris dan id penghasil otomatis secara harfiah adalah hal yang sama?
Michael Vescovo
11
Dalam SQLite id kunci utama terbesar yang dapat Anda miliki adalah integer bertanda 64 bit, sehingga nilai maksnya adalah 9.223.372.036.854.775.807 (hanya positif karena merupakan id). Di java, int adalah nomor bertanda 32 bit dan nilai maks positifnya adalah 2.147.483.647, sehingga tidak dapat mewakili semua id. Anda perlu menggunakan panjang Java yang nilainya maksimum adalah 9.223.372.036.854.775.807 untuk mewakili semua id. Dokumentasinya hanya sebagai contoh saja, tetapi api dirancang dengan pemikiran ini (itulah sebabnya ia kembali panjang dan tidak int atau dobel)
MatPag
2
ok jadi itu benar-benar harus lama. tetapi mungkin untuk sebagian besar kasus tidak akan ada 9 miliar baris dalam sqlite db sehingga mereka menggunakan int sebagai contoh untuk userId karena membutuhkan lebih sedikit memori (atau itu kesalahan). Itulah yang saya ambil dari ini. Terima kasih atas penjelasan tentang mengapa pengembaliannya lama.
Michael Vescovo
3
Anda benar, tetapi API Kamar harus bekerja bahkan dalam skenario terburuk dan harus mengikuti spesifikasi SQlite. Menggunakan int lebih lama untuk kasus spesifik ini praktis adalah hal yang sama, konsumsi memori tambahan dapat diabaikan
MatPag
1
@MatPag Anda link asli tidak lagi termasuk konfirmasi dari perilaku ini (dan sedih, begitu juga referensi API untuk ruangan Insert penjelasan ). Setelah sedikit mencari saya menemukan ini dan memperbarui tautan dalam jawaban Anda. Semoga ini bertahan sedikit lebih baik daripada yang terakhir karena ini adalah info yang cukup signifikan.
CodeClown42
27

@Insertfungsi dapat kembali void, long, long[]atau List<Long>. Silakan coba ini.

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

 // Insert multiple items
 @Insert(onConflict = OnConflictStrategy.REPLACE)
  long[] insert(User... user);
Quang Nguyen
sumber
5
return Single.fromCallable(() -> dbService.YourDao().insert(mObject));
murt
8

Nilai balik dari penyisipan untuk satu catatan akan menjadi 1 jika pernyataan Anda berhasil.

Jika Anda ingin menyisipkan daftar objek, Anda dapat pergi dengan:

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

Dan jalankan dengan 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.

        }
    });
Cuong Vo
sumber
1
"Nilai pengembalian penyisipan untuk satu catatan akan menjadi 1 jika pernyataan Anda berhasil" -> Menurut dokumentasi ini: developer.android.com/training/data-storage/room/accessing-data "Jika metode @Insert hanya menerima 1 parameter, itu dapat mengembalikan panjang, yang merupakan rowId baru untuk item yang dimasukkan. Jika parameter adalah array atau koleksi, itu harus mengembalikan panjang [] atau sebagai gantinya <Long> .
CodeClown42
4

Dapatkan ID baris dengan sniplet berikut. Ini menggunakan callable pada 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: Tutorial Java Executor Service untuk informasi lebih lanjut tentang Callable.

Hardian
sumber
3

Di Dao Anda, kueri insert mengembalikan Longyaitu rowId yang disisipkan.

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

Di kelas Model (Repositori) Anda: (MVVM)

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

Di kelas ModelView Anda: (MVVM) Pegang LiveData dengan DisposableSingleObserver.
Referensi sumber kerja yang berfungsi: https://github.com/SupriyaNaveen/CookingRecipes

Supriya Naveen
sumber
1

Setelah banyak perjuangan, saya berhasil menyelesaikan ini. Ini solusi saya menggunakan arsitektur 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
}

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

                })
        )
    }
}

Dalam Fragmen:

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

Saya telah menggunakan DataBinding. Berikut ini adalah XML saya:

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

Saya telah berjuang keras untuk mencapai hal ini dengan asynctask karena operasi memasukkan dan menghapus ruang harus dilakukan dalam utas terpisah. Akhirnya dapat melakukan ini dengan tipe tunggal yang dapat diamati di RxJava.

Inilah dependensi Gradle untuk rxjava:

implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.3' 
kgandroid
sumber
0

Menurut fungsi dokumentasi yang dijelaskan dengan @Insert dapat mengembalikan rowId.

Jika metode @Insert hanya menerima 1 parameter, ia bisa mengembalikan panjang, yang merupakan rowId baru untuk item yang dimasukkan. Jika parameternya adalah array atau koleksi, itu harus mengembalikan panjang [] atau sebagai gantinya <Long>.

Masalah yang saya miliki dengan ini adalah bahwa ia mengembalikan rowId dan bukan id dan saya masih belum menemukan cara untuk mendapatkan id menggunakan rowId.

Sayangnya saya belum bisa berkomentar, karena saya tidak punya 50 reputasi, jadi saya memposting ini sebagai jawaban.

AndroidKotlinNoob
sumber