Android Room - รับรหัสของแถวที่แทรกใหม่พร้อมการสร้างอัตโนมัติ


138

นี่คือวิธีที่ฉันแทรกข้อมูลลงในฐานข้อมูลโดยใช้ Room Persistence Library:

Entity:

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

วัตถุการเข้าถึงข้อมูล:

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

เป็นไปได้ไหมที่จะส่งคืน id ของผู้ใช้เมื่อการแทรกเสร็จสมบูรณ์ในวิธีการข้างต้นโดยไม่ต้องเขียนข้อความค้นหาแยกต่างหาก


1
คุณได้ลองใช้intหรือlongแทนที่จะvoidเป็นผลจากการ@Insertดำเนินการ?
MatPag

ยัง. ฉันจะยิง!
SpiralDev

ฉันได้เพิ่มคำตอบด้วยเพราะฉันพบข้อมูลอ้างอิงในเอกสารและฉันค่อนข้างมั่นใจว่ามันจะใช้งานได้;)
MatPag

3
จะไม่ทำกับaSyncTask? คุณคืนค่าจากฟังก์ชันที่เก็บของคุณอย่างไร
Nimitz14

คำตอบ:


192

ขึ้นอยู่กับเอกสาร ที่นี่ (ด้านล่างข้อมูลโค้ด)

วิธีการที่มี@Insertคำอธิบายประกอบสามารถส่งคืน:

  • long สำหรับการใช้งานเม็ดมีดเดียว
  • long[]หรือLong[]หรือList<Long>สำหรับการใช้งานหลายเม็ด
  • void หากคุณไม่สนใจเกี่ยวกับรหัสที่แทรก

5
ทำไมในเอกสารถึงบอกว่า int สำหรับประเภท id แต่กลับยาว? สมมติว่า id จะไม่ใหญ่พอที่จะยาว? ดังนั้น id แถวและ id สร้างอัตโนมัติจึงเป็นสิ่งเดียวกันอย่างแท้จริง?
Michael Vescovo

11
ใน SQLite รหัสหลักที่ใหญ่ที่สุดที่คุณสามารถมีได้คือจำนวนเต็ม 64 บิตที่มีการลงชื่อดังนั้นค่าสูงสุดคือ 9,223,372,036,854,775,807 (เฉพาะค่าบวกเนื่องจากเป็นรหัส) ใน java int คือหมายเลขที่ลงนาม 32 บิตและค่าบวกสูงสุดคือ 2,147,483,647 ดังนั้นจึงไม่สามารถแสดงรหัสทั้งหมดได้ คุณต้องใช้ Java แบบยาวซึ่งค่าสูงสุดคือ 9,223,372,036,854,775,807 เพื่อแสดงรหัสทั้งหมด เอกสารประกอบเป็นตัวอย่างเท่านั้น แต่ api ได้รับการออกแบบโดยคำนึงถึงสิ่งนี้ (นั่นคือเหตุผลว่าทำไมมันถึงกลับมายาวและไม่ใช่ int หรือ double)
MatPag

2
ตกลงมันควรจะยาวจริงๆ แต่ในกรณีส่วนใหญ่จะไม่มี 9 พันล้านแถวใน sqlite db ดังนั้นจึงใช้ int เป็นตัวอย่างสำหรับ userId เนื่องจากใช้หน่วยความจำน้อยลง (หรือเป็นข้อผิดพลาด) นั่นคือสิ่งที่ฉันได้รับจากสิ่งนี้ ขอบคุณสำหรับคำอธิบายว่าทำไมถึงส่งคืนนาน
Michael Vescovo

3
คุณพูดถูก แต่ API ของห้องควรใช้งานได้แม้ในสถานการณ์ที่เลวร้ายที่สุดและต้องเป็นไปตามข้อกำหนดของ SQlite การใช้ int เป็นเวลานานสำหรับกรณีเฉพาะนี้เป็นสิ่งเดียวกันในทางปฏิบัติการใช้หน่วยความจำเพิ่มเติมนั้นเล็กน้อยมาก
MatPag

1
@MatPag ของคุณเชื่อมโยงเดิมไม่ได้รวมถึงการยืนยันของพฤติกรรมนี้ (และเศร้าไม่ไม่อ้างอิง API สำหรับห้องใส่คำอธิบายประกอบ ) หลังจากการค้นหาเล็กน้อยฉันพบสิ่งนี้และอัปเดตลิงก์ในคำตอบของคุณ หวังว่ามันจะยังคงดีกว่าครั้งล่าสุดเล็กน้อยเนื่องจากนี่เป็นข้อมูลที่ค่อนข้างสำคัญ
CodeClown42

28

@Insertฟังก์ชั่นสามารถกลับvoid, long, หรือlong[] List<Long>โปรดลองทำตามนี้

 @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

ค่าส่งคืนของการแทรกสำหรับหนึ่งระเบียนจะเป็น 1 หากคำสั่งของคุณสำเร็จ

ในกรณีที่คุณต้องการแทรกรายการวัตถุคุณสามารถไปที่:

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

และดำเนินการด้วย 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.

        }
    });

1
"ค่าส่งคืนของการแทรกสำหรับหนึ่งระเบียนจะเป็น 1 หากคำสั่งของคุณสำเร็จ" -> ตามเอกสารนี้: developer.android.com/training/data-storage/room/accessing-data "หากวิธีการ @Insert ได้รับเพียง 1 พารามิเตอร์สามารถส่งคืน long ซึ่งเป็น rowId ใหม่สำหรับรายการที่แทรกหากพารามิเตอร์เป็นอาร์เรย์หรือคอลเล็กชันพารามิเตอร์ควรส่งคืน long [] หรือ List <Long>แทน "
CodeClown42

4

รับ ID แถวตาม sniplet ต่อไปนี้ ใช้ callable บน ExecutorService พร้อม 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สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Callable


3

ใน Dao ของคุณแบบสอบถามแทรกจะส่งกลับLongเช่น rowId แทรก

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

ในคลาส Model (Repository) ของคุณ: (MVVM)

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

ในคลาส ModelView ของคุณ: (MVVM) จัดการ LiveData ด้วย DisposableSingleObserver
แหล่งอ้างอิงการทำงาน: https://github.com/SupriyaNaveen/CookingRecipes


2

หลังจากต่อสู้กันมามากฉันก็สามารถแก้ปัญหานี้ได้ นี่คือวิธีแก้ปัญหาของฉันโดยใช้สถาปัตยกรรม 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)
                    }

                })
        )
    }
}

ในส่วนย่อย:

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 แล้วนี่คือ 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>

ฉันพยายามอย่างมากที่จะทำสิ่งนี้ให้สำเร็จด้วย asynctask เนื่องจากการแทรกห้องและการลบต้องทำในเธรดแยกต่างหาก ในที่สุดก็สามารถทำได้ด้วยSingle type ที่สังเกตได้ใน RxJava

นี่คือการพึ่งพา Gradle สำหรับ rxjava:

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

0

ตามฟังก์ชันเอกสารที่มี @Insert สามารถส่งคืน rowId

หากเมธอด @Insert ได้รับพารามิเตอร์เพียง 1 พารามิเตอร์ก็สามารถส่งคืน long ซึ่งเป็น rowId ใหม่สำหรับรายการที่แทรก หากพารามิเตอร์เป็นอาร์เรย์หรือคอลเล็กชันพารามิเตอร์ควรส่งคืน long [] หรือ List <Long> แทน

ปัญหาที่ฉันมีคือมันส่งคืน rowId ไม่ใช่ id และฉันยังไม่พบวิธีรับ id โดยใช้ rowId

น่าเศร้าที่ฉันยังแสดงความคิดเห็นไม่ได้เพราะฉันไม่มีชื่อเสียง 50 คนฉันจึงโพสต์ข้อความนี้เป็นคำตอบแทน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.