Android Odası - Otomatik oluşturmayla yeni eklenen satırın kimliğini al


138

Room Persistence Library'yi kullanarak veri tabanına şu şekilde veri ekliyorum:

Varlık:

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

Veri erişim nesnesi:

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

Yukarıdaki yöntemin kendisinde ekleme işlemi tamamlandıktan sonra, ayrı bir seçme sorgusu yazmadan Kullanıcının kimliğini döndürmek mümkün müdür?


1
Operasyonun sonucu olarak intveya longyerine kullanmayı denediniz mi? void@Insert
MatPag

Henüz değil. Bir şans vereceğim!
SpiralDev

Ben de bir cevap ekledim çünkü dokümantasyonda referansı buldum ve çalışacağından oldukça eminim;)
MatPag

3
bu bir ile yapılmayacak aSyncTaskmı? Depo işlevinizden değeri nasıl döndürürsünüz?
Nimitz14

Yanıtlar:


192

Buradaki belgelere göre (kod pasajının altında)

@InsertEk açıklama ile not eklenmiş bir yöntem şunu döndürebilir:

  • long tek ekleme işlemi için
  • long[]veya Long[]veya List<Long>birden fazla ekleme işlemi için
  • void eklenen kimlikleri umursamıyorsanız

5
neden belgelerde kimlik türü için int diyor ama uzun döndürüyor? id'nin asla uzun olmayacak kadar büyük olmayacağını varsaymak mı? yani satır kimliği ve otomatik oluşturma kimliği tam anlamıyla aynı şey mi?
Michael Vescovo

11
SQLite'de sahip olabileceğiniz en büyük birincil anahtar kimliği 64 bitlik işaretli bir tamsayıdır, bu nedenle maksimum değer 9,223,372,036,854,775,807'dir (yalnızca bir kimlik olduğu için pozitiftir). Java'da bir int 32 bit işaretli sayıdır ve maksimum pozitif değer 2,147,483,647'dir, bu nedenle tüm kimlikleri temsil edemez. Tüm kimlikleri temsil etmek için maksimum değeri 9,223,372,036,854,775,807 olan uzun bir Java kullanmanız gerekir. Dokümantasyon yalnızca örnektir, ancak api bu akılda tutularak tasarlanmıştır (bu nedenle uzun döndürür, int veya double değil)
MatPag

2
tamam yani gerçekten uzun olmalı. ama belki çoğu durumda bir sqlite db'de 9 milyar satır olmayacaktır, bu nedenle daha az bellek aldığı için (veya bu bir hata) userId için örnek olarak int kullanırlar. Ben bundan alıyorum. Neden uzun döndüğüne dair açıklama için teşekkürler.
Michael Vescovo

3
Haklısınız, ancak Oda API'leri en kötü senaryoda bile çalışmalı ve SQlite teknik özelliklerine uymalıdır. Bu özel durum için uzun bir int kullanmak neredeyse aynı şeydir, ek bellek tüketimi ihmal edilebilir
MatPag

1
@MatPag Orijinal bağlantınız artık bu davranışın bir onayını içermiyor (ve ne yazık ki, odanın Ekle ek açıklaması için API referansı da yok ). Arama biraz sonra buldum bu ve cevap bağlantıyı güncellendi. Umarım bu oldukça önemli bir bilgi parçası olduğu için öncekinden biraz daha iyi devam eder .
CodeClown42

28

@Insertişlev dönebilir void, long, long[]ya da List<Long>. Lütfen bunu deneyin.

 @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));
murt

8

Ekstreniz başarılı olursa, bir kayıt için eklemenin dönüş değeri 1 olacaktır.

Nesne listesi eklemek istemeniz durumunda, şu şekilde gidebilirsiniz:

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

Ve bunu Rx2 ile çalıştırın:

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
" İfadenizin başarılı olması durumunda, eklemenin dönüş değeri 1 olacaktır" -> Bu dokümantasyona göre: developer.android.com/training/data-storage/room/accessing-data "@Insert yöntemi yalnızca alırsa 1 parametresinde, eklenen öğe için yeni satır kimliği olan uzun bir değeri döndürebilir . Parametre bir dizi veya koleksiyonsa bunun yerine long [] veya List <Long> döndürmelidir . "
CodeClown42

4

Aşağıdaki snippet ile satır kimliğini alın. Bir ExecutorService with Future üzerinde çağrılabilir kullanır.

 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: Çağrılabilir hakkında daha fazla bilgi için Java Yürütme Hizmeti Eğitimi .


3

LongDao'nuzda , ekleme sorgusu, yani eklenen satır kimliği döndürülür.

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

Model (Depo) sınıfınızda: (MVVM)

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

ModelView sınıfınızda: (MVVM) LiveData'yı DisposableSingleObserver ile işleyin.
Kaynak çalışan referansı: https://github.com/SupriyaNaveen/CookingRecipes


2

Çok uğraştıktan sonra bunu çözmeyi başardım. MMVM mimarisini kullanan çözümüm:

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

                })
        )
    }
}

Parçada:

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

DataBinding'i kullandım.İşte XML'im:

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

Bunu asynctask ile başarmak için çok uğraştım, çünkü oda ekleme ve silme işlemi ayrı bir iş parçacığında yapılmalıdır. Sonunda bunu RxJava'da gözlemlenebilir Tek tip ile yapabildim.

Rxjava için Gradle bağımlılıkları:

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

0

@Insert ile açıklamalı dokümantasyon işlevlerine göre rowId döndürebilir.

@Insert yöntemi yalnızca 1 parametre alırsa, eklenen öğe için yeni satır kimliği olan long döndürebilir. Parametre bir dizi veya koleksiyonsa, bunun yerine long [] veya List <Long> döndürmelidir.

Bununla ilgili sorunum, id'yi değil, rowId'yi döndürmesi ve hala rowId'yi kullanarak kimliği nasıl alacağımı bulamadım.

Ne yazık ki henüz yorum yapamıyorum çünkü 50 itibarım yok, bu yüzden bunun yerine bunu bir cevap olarak gönderiyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.