Android Room-자동 생성 된 새로운 삽입 된 행의 ID를 가져옵니다.


138

이것이 Room Persistence Library를 사용하여 데이터베이스에 데이터를 삽입하는 방법입니다.

실재:

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

데이터 액세스 객체 :

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

별도의 선택 쿼리를 작성하지 않고 위의 메소드 자체에서 삽입이 완료되면 User의 ID를 반환 할 수 있습니까?


1
작업 결과로 int또는 long대신에 사용해 보셨습니까 ? void@Insert
MatPag

아직. 나는 기회를 줄 것이다!
SpiralDev

나는 문서에서 참조를 찾았 기 때문에 답변을 추가했으며 그것이 효과가 있다고 확신합니다.)
MatPag

3
이 작업을 수행하지 aSyncTask않습니까? 리포지토리 함수에서 값을 어떻게 반환합니까?
Nimitz14

답변:


191

여기 의 문서를 기반으로합니다 (코드 스 니펫 아래)

주석으로 주석이 달린 메소드 @Insert는 다음을 리턴 할 수 있습니다.

  • long 단일 인서트 작업용
  • long[]Long[]또는 List<Long>여러 삽입 작업을위한
  • void 삽입 된 ID를 신경 쓰지 않으면

4
왜 문서에서 ID 유형에 대해 int라고 말하지만 오래 반환합니까? ID가 길어질 정도로 충분히 크지 않을 것이라고 가정합니까? 그래서 행 ID와 자동 생성 ID는 문자 그대로 동일한 것입니까?
Michael Vescovo

11
SQLite에서 가장 큰 기본 키 ID는 64 비트 부호있는 정수이므로 최대 값은 9,223,372,036,854,775,807입니다 (ID이므로 양수 만). Java에서 int는 32 비트 부호있는 숫자이며 최대 양수 값은 2,147,483,647이므로 모든 ID를 나타낼 수는 없습니다. 모든 id를 나타내려면 최대 값이 9,223,372,036,854,775,807 인 Java long을 사용해야합니다. 문서는 단지 예일 뿐이지 만, API는이를 염두에두고 설계되었습니다. (따라서 int 또는 double이 아닌 오래 반환되는 이유입니다)
MatPag

2
그래야 정말 길어야합니다. 그러나 대부분의 경우 sqlite db에 90 억 개의 행이 없으므로 ind를 사용하여 메모리를 덜 사용하거나 실수로 인해 userId의 예제로 사용합니다. 그것이 내가 이것에서 취하는 것입니다. 왜 오래 돌아 오는지 설명해 주셔서 감사합니다.
Michael Vescovo

3
맞습니다. 그러나 룸의 API는 최악의 시나리오에서도 작동해야하며 SQlite의 사양을 따라야합니다. 이 특정 사건에 대한 오랜 동안 int를 사용하는 것은 실질적으로 같은 일이며, 추가 메모리 소비는 무시할
MatPag

1
@MatPag 원래 링크 에는 더 이상이 동작에 대한 확인이 포함되어 있지 않습니다 (슬프게도 Room의 Insert 주석에 대한 API 참조 도 아님 ). 검색 조금 후에 나는 발견 당신의 대답에있는 링크를 업데이트했습니다. 바라건대 이것은 정보 보다 상당히 중요하기 때문에 마지막 것보다 조금 더 잘 유지 되기를 바랍니다 .
CodeClown42

27

@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 매개 변수 는 삽입 된 항목의 새 rowId 인 long을 리턴 할 수 있습니다 . 매개 변수가 배열 또는 콜렉션 인 경우 대신 long [] 또는 List <Long>을 리턴 해야 합니다. "
CodeClown42

4

다음 코드 조각으로 행 ID를 가져옵니다. Future와 함께 ExecutorService에서 호출 가능을 사용합니다.

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

참조 : Callable에 대한 자세한 내용은 Java Executor Service Tutorial 을 참조하십시오.


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) DisposableSingleObserver로 LiveData를 처리하십시오.
작업 소스 참조 : https://github.com/SupriyaNaveen/CookingRecipes


1

많은 투쟁 후, 나는 이것을 해결했습니다. 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 로이 작업을 수행하기 위해 많은 노력을 기울였습니다. 마지막으로 RxJava에서 Observable Single 유형 으로이를 수행 할 수 있습니다.

다음은 rxjava에 대한 Gradle 종속성입니다.

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

0

@Insert로 주석이 달린 설명서 함수 에 따르면 rowId를 반환 할 수 있습니다.

@Insert 메소드가 1 개의 매개 변수 만 수신하면 삽입 된 항목의 새 rowId 인 long을 리턴 할 수 있습니다. 매개 변수가 배열 또는 콜렉션 인 경우 대신 long [] 또는 List <Long>을 리턴해야합니다.

내가 가진 문제는 id가 아닌 rowId를 반환하지만 rowId를 사용하여 id를 얻는 방법을 찾지 못했다는 것입니다.

슬프게도 아직 50 개의 평판이 없기 때문에 아직 댓글을 달 수 없으므로 대신 답변으로 게시하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.