Android Room - 通过自动生成获取新插入行的 id
Posted
技术标签:
【中文标题】Android Room - 通过自动生成获取新插入行的 id【英文标题】:Android Room - Get the id of new inserted row with auto-generate 【发布时间】: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,而无需编写单独的选择查询?
【问题讨论】:
您是否尝试过使用int
或long
而不是void
作为@Insert
操作的结果?
还没有。我会试一试!
我也添加了一个答案,因为我在文档中找到了参考资料,我非常有信心它会起作用;)
这不能用aSyncTask
来完成吗?您如何从存储库函数中返回值?
【参考方案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
函数可以返回void
、long
、long[]
或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通过以下代码片段获取行 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_
android:layout_
android:fillViewport="true"
tools:context="com.kgandroid.studentsubject.view.RegistrationFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constarintLayout"
android:layout_
android:layout_
android:isScrollContainer="true">
<TextView
android:id="@+id/tvRoll"
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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'
【讨论】:
以上是关于Android Room - 通过自动生成获取新插入行的 id的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Android Room 从数据库中获取一行? [复制]