【问题标题】:Android Room - Get the id of new inserted row with auto-generateAndroid Room - 通过自动生成获取新插入行的 id
【发布时间】:2017-11-05 22:47:12
【问题描述】:

这就是我使用 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,而无需编写单独的选择查询?

【问题讨论】:

  • 您是否尝试过使用intlong 而不是void 作为@Insert 操作的结果?
  • 还没有。我会试一试!
  • 我也添加了一个答案,因为我在文档中找到了参考资料,我非常有信心它会起作用;)
  • 这不能用aSyncTask 来完成吗?您如何从存储库函数中返回值?

标签: android persistent-storage android-room


【解决方案1】:

基于文档here(代码sn-p下方)

带有@Insert注解的方法可以返回:

  • long 用于单次插入操作
  • long[]Long[]List<Long> 用于多个插入操作
  • void 如果你不关心插入的 id(s)

【讨论】:

  • 为什么在文档中说 id 类型为 int 但返回 long?假设 id 永远不会长到足够长?所以行 id 和自动生成的 id 是一回事吗?
  • 在 SQLite 中,您可以拥有的最大主键 id 是 64 位有符号整数,因此最大值为 9,223,372,036,854,775,807(只有正数,因为它是一个 id)。在 java 中,int 是 32 位有符号数,最大正值为 2,147,483,647,因此无法表示所有 id。您需要使用最大值为 9,223,372,036,854,775,807 的 Java long 来表示所有 id。该文档仅作为示例,但 api 的设计考虑到了这一点(这就是它返回 long 而不是 int 或 double 的原因)
  • 好的,所以它真的应该很长。但在大多数情况下,sqlite 数据库中可能不会有 90 亿行,因此他们使用 int 作为 userId 的示例,因为它占用的内存更少(或者这是一个错误)。这就是我从中得到的。感谢您解释为什么它会返回 long。
  • 你是对的,但是 Room 的 API 即使在最坏的情况下也应该工作,并且必须遵循 SQlite 的规范。对于这种特定情况,在 long 上使用 int 实际上是一回事,额外的内存消耗可以忽略不计
  • @MatPag 您的original link 不再包含对此行为的确认(遗憾的是,the API reference for room's Insert annotation 也没有)。经过一番搜索,我找到了this 并更新了您答案中的链接。希望它持续比上一个好一点,因为这是一个非常重要的信息。
【解决方案2】:

@Insert 函数可以返回voidlonglong[]List<Long>。请试试这个。

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

 // Insert multiple items
 @Insert(onConflict = OnConflictStrategy.REPLACE)
  long[] insert(User... user);

【讨论】:

  • return Single.fromCallable(() -> dbService.YourDao().insert(mObject));
【解决方案3】:

如果你的语句成功,插入一条记录的返回值为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" -> 根据此文档:developer.android.com/training/data-storage/room/accessing-data"如果@Insert 方法收到只有1个参数,它可以返回一个long,这是插入项的新rowId。如果参数是数组或集合,它应该返回long[]或List 而不是。”
【解决方案4】:

通过以下代码片段获取行 ID。它在带有 Future 的 ExecutorService 上使用 callable。

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

参考:Java Executor Service Tutorial 了解有关 Callable 的更多信息。

【讨论】:

    【解决方案5】:

    根据documentation@Insert注解的函数可以返回rowId。

    如果@Insert 方法只接收1 个参数,它可以返回一个long,即插入项的新rowId。如果参数是数组或集合,则应返回 long[] 或 List

    我遇到的问题是它返回的是rowId而不是id,我还没有找到如何使用rowId获取id。

    编辑:我现在知道如何从 rowId 中获取 id。下面是 SQL 命令:

    SELECT id FROM table_name WHERE rowid = :rowId
    

    【讨论】:

      【解决方案6】:

      在您的 Dao 中,插入查询返回 Long 即插入的 rowId。

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

      在您的模型(存储库)类中:(MVVM)

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

      在您的 ModelView 类中:(MVVM) 使用 DisposableSingleObserver 处理 LiveData。
      工作来源参考:https://github.com/SupriyaNaveen/CookingRecipes

      【讨论】:

        【解决方案7】:

        经过一番挣扎,我设法解决了这个问题。这是我使用 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做到这一点 在 RxJava 中输入 observable。

        这是 rxjava 的 Gradle 依赖项:

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

        【讨论】:

          猜你喜欢
          • 2020-12-18
          • 2011-04-03
          • 2011-11-18
          • 2019-06-07
          • 2013-10-06
          • 1970-01-01
          • 2019-12-10
          • 1970-01-01
          • 2018-07-30
          相关资源
          最近更新 更多