Phòng Android - Nhận id của hàng được chèn mới với tính năng tự động tạo


138

Đây là cách tôi chèn dữ liệu vào cơ sở dữ liệu bằng Thư viện liên tục phòng:

Thực thể:

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

Đối tượng truy cập dữ liệu:

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

Có thể trả về id của Người dùng sau khi quá trình chèn được hoàn thành trong chính phương thức trên mà không cần viết một truy vấn chọn riêng không?


1
Bạn đã thử sử dụng inthoặc longthay vì voidkết quả của @Inserthoạt động?
MatPag

Chưa. Tôi sẽ cung cấp cho một shot!
Xoắn ốc

Tôi cũng đã thêm một câu trả lời vì tôi đã tìm thấy tài liệu tham khảo trong tài liệu và tôi khá tự tin rằng nó sẽ hoạt động;)
MatPag

3
Điều này sẽ không được thực hiện với một aSyncTask? Làm thế nào bạn trả lại giá trị từ chức năng kho lưu trữ của bạn?
Nimitz14

Câu trả lời:


191

Dựa trên các tài liệu ở đây (bên dưới đoạn mã)

Một phương thức được chú thích với @Insertchú thích có thể trả về:

  • long cho hoạt động chèn đơn
  • long[]hoặc Long[]hoặcList<Long> cho nhiều hoạt động chèn
  • void nếu bạn không quan tâm đến (các) id được chèn

4
Tại sao trong tài liệu lại nói int cho loại id nhưng trả về lâu? giả sử id sẽ không bao giờ đủ lớn để tồn tại lâu? Vì vậy, id hàng và id tự động tạo ra có giống nhau không?
Michael Vescovo

11
Trong SQLite, id khóa chính lớn nhất bạn có thể có là số nguyên có chữ ký 64 bit, vì vậy giá trị tối đa là 9,223,372,036,854,775,807 (chỉ dương vì nó là id). Trong java, số int được ký là 32 bit và giá trị dương tối đa là 2.147.483.647, do đó không thể đại diện cho tất cả các id. Bạn cần sử dụng một Java dài mà giá trị tối đa của nó là 9.223.372.036.854.775.807 để thể hiện tất cả các id. Tài liệu chỉ là ví dụ, nhưng api được thiết kế với ý tưởng này (đó là lý do tại sao nó sẽ trở lại lâu và không phải là int hoặc double)
MatPag

2
ok vậy nó thực sự nên dài nhưng có lẽ trong hầu hết các trường hợp, sẽ không có 9 tỷ hàng trong db sqlite để họ sử dụng int làm ví dụ cho userId vì nó chiếm ít bộ nhớ hơn (hoặc đó là một lỗi). Đó là những gì tôi lấy từ cái này. Cảm ơn đã giải thích về lý do tại sao nó trở lại lâu dài.
Michael Vescovo

3
Bạn đã đúng, nhưng các API của Phòng sẽ hoạt động ngay cả trong trường hợp xấu nhất và phải tuân theo các thông số kỹ thuật của SQlite. Sử dụng một int trong một thời gian dài cho trường hợp cụ thể này thực tế là điều tương tự, mức tiêu thụ bộ nhớ bổ sung là không đáng kể
MatPag

1
@MatPag Liên kết ban đầu của bạn không còn bao gồm xác nhận về hành vi này (và thật đáng buồn, cả tham chiếu API cho chú thích Chèn của phòng ). Sau một chút tìm kiếm tôi đã tìm thấy điều này và cập nhật liên kết trong câu trả lời của bạn. Hy vọng rằng nó vẫn tồn tại tốt hơn một chút so với cái cuối cùng vì đây là một thông tin khá quan trọng.
CodeClown42

27

@Insertchức năng có thể trở lại void, long, long[]hoặc List<Long>. Hãy thử điều này.

 @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

Giá trị trả về của phần chèn cho một bản ghi sẽ là 1 nếu câu lệnh của bạn thành công.

Trong trường hợp bạn muốn chèn danh sách các đối tượng, bạn có thể đi với:

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

Và thực hiện nó với 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
"Giá trị trả về của việc chèn cho một bản ghi sẽ là 1 nếu câu lệnh của bạn thành công" -> Theo tài liệu này: developer.android.com/training/data-st Storage / room / access-data "Nếu phương thức @Insert chỉ nhận được 1 tham số, nó có thể trả về một hàng dài, đó là rowId mới cho mục được chèn. Nếu tham số là một mảng hoặc tập hợp, nó sẽ trả về dài [] hoặc List <Long> . "
CodeClown42

4

Lấy ID hàng bằng sniplet sau. Nó sử dụng có thể gọi được trên một ExecutorService với Tương lai.

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

Tham khảo: Hướng dẫn dịch vụ thực thi Java để biết thêm thông tin về Callable.


3

Trong Dao của bạn, truy vấn chèn trả về Longtức là rowId được chèn.

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

Trong lớp Model (Kho lưu trữ) của bạn: (MVVM)

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

Trong lớp ModelView của bạn: (MVVM) Xử lý LiveData với máy chủ dùng một lần.
Tài liệu tham khảo làm việc: https://github.com/SupriyaNaveen/CookingRecipes


1

Sau rất nhiều cuộc đấu tranh, tôi đã giải quyết được điều này. Đây là giải pháp của tôi khi sử dụng kiến trúc MMVM:

Sinh viên.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
}

Sinh viênDao.kt

interface StudentDao {
    @Insert
    fun insertStudent(student: Student) : Long
}

StudentRep repository.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 }
                }
    }
}

Sinh viênViewModel.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)
                    }

                })
        )
    }
}

Trong đoạn:

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

Tôi đã sử dụng DataBinding. Đây là XML của tôi:

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

Tôi đã phải vật lộn rất nhiều để thực hiện điều này với asynctask vì thao tác chèn và xóa phòng phải được thực hiện trong một luồng riêng biệt. Cuối cùng có thể làm điều này với loại đơn có thể quan sát được trong RxJava.

Đây là phụ thuộc Gradle cho rxjava:

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

0

Theo các chức năng tài liệu được chú thích bằng @Insert có thể trả về rowId.

Nếu phương thức @Insert chỉ nhận được 1 tham số, nó có thể trả về một giá trị dài, đó là rowId mới cho mục được chèn. Nếu tham số là một mảng hoặc một tập hợp, thay vào đó, nó sẽ trả về [] hoặc Danh sách <Long>.

Vấn đề tôi gặp phải là nó trả về rowId chứ không phải id và tôi vẫn chưa tìm ra cách lấy id bằng rowId.

Đáng buồn là tôi chưa thể bình luận, vì tôi không có 50 danh tiếng, vì vậy tôi sẽ đăng bài này như một câu trả lời.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.