Android Room-通过自动生成获取新插入行的id
以下是我使用Room Persistence Library将数据插入数据库的方式: 实体:Android Room-通过自动生成获取新插入行的id,android,persistent-storage,android-room,Android,Persistent Storage,Android Room,以下是我使用Room Persistence Library将数据插入数据库的方式: 实体: @Entity class User { @PrimaryKey(autoGenerate = true) public int id; //... } 数据访问对象: @Dao public interface UserDao{ @Insert(onConflict = IGNORE) void insertUser(User user); //...
@Entity
class User {
@PrimaryKey(autoGenerate = true)
public int id;
//...
}
数据访问对象:
@Dao
public interface UserDao{
@Insert(onConflict = IGNORE)
void insertUser(User user);
//...
}
在上述方法中完成插入后,是否可以在不编写单独的选择查询的情况下返回用户id?
@Insert
函数可以返回void
、long
、long[]
或List
。请试试这个
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(User user);
// Insert multiple items
@Insert(onConflict = OnConflictStrategy.REPLACE)
long[] insert(User... user);
基于文档(代码段下方) 用
@Insert
注释注释的方法可以返回:
用于单次插入操作long
或long[]
或long[]
用于多个插入操作List
如果您不关心插入的idvoid
如果语句成功,则一条记录的插入返回值将为1 如果要插入对象列表,可以使用:
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] addAll(List<Object> list);
@Insert(onConflict=OnConflictStrategy.REPLACE)
公共长[]addAll(列表);
并使用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.
}
});
Observable.fromCallable(newcallable(){
@凌驾
公共对象调用()引发异常{
返回yourDao.addAll(列表);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(newconsumer()){
@凌驾
public void accept(@NonNull Object o)引发异常{
//o的长度为[]。大小=>插入的记录数。
}
});
在Dao中,insert查询返回Long
,即插入的rowId。 @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(recipes: CookingRecipes): Long
在模型(存储库)类中:(MVVM)fun addrecipes数据(cookingRecipes:cookingRecipes):单一?{
返回Single.fromCallable{recipesDao.insertManual(cookingRecipes)}
}
在ModelView类中:(MVVM)使用DisposableSingleObserver处理LiveData。工作源代码参考:通过以下按钮获取行ID。它在具有未来的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;
}
private-UserDao-UserDao;
私人遗嘱执行人服务;
公共长插拔状态(用户){
Callable insertCallable=()->userDao.insert(用户);
长rowId=0;
Future=executorService.submit(可插入);
试一试{
rowId=future.get();
}捕捉(中断异常e1){
e1.printStackTrace();
}捕获(执行例外){
e、 printStackTrace();
}
返回rowId;
}
参考:有关Callable的更多信息。经过一番努力,我终于解决了这个问题。以下是我使用MMVM体系结构的解决方案:
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
}
}
<?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>
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.3'
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
}
interface StudentDao {
@Insert
fun insertStudent(student: Student) : Long
}
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 }
}
}
}
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)
}
})
)
}
}
StudentDao.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
}
interface StudentDao {
@Insert
fun insertStudent(student: Student) : Long
}
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 }
}
}
}
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)
}
})
)
}
}
StudentRepository.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
}
interface StudentDao {
@Insert
fun insertStudent(student: Student) : Long
}
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 }
}
}
}
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)
}
})
)
}
}
我使用了数据绑定。这是我的XML:
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
}
}
<?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>
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.3'
根据用@Insert注释的函数,可以返回rowId
如果@Insert方法只接收到1个参数,它可以返回一个long,即插入项的新rowId。如果参数是数组或集合,则应返回long[]或List
我遇到的问题是,它返回的是rowId而不是id,我仍然没有找到如何使用rowId获取id
编辑:我现在知道如何从rowId获取id。下面是SQL命令:
SELECT id FROM table_name WHERE rowid = :rowId
由于执行了
@Insert
操作,您是否尝试使用int
或long
而不是void
。我要试一试!我也添加了一个答案,因为我在文档中找到了参考,我很有信心它会起作用;)这不是用异步任务完成的吗?如何从存储库函数返回值?返回Single.fromCallable(()->dbService.YourDao().insert(mObject))代码>为什么在文档中它对id类型说int,但返回long?假设id永远不会大到足以成为一个长的?所以行id和自动生成id实际上是一样的?在SQLite中,您可以拥有的最大主键id是一个64位有符号整数,因此最大值是9223372036854775807(因为它是一个id,所以只有正值)。在java中,int是32位有符号数,is max正值为2147483647,因此不能表示所有ID。您需要使用最大值为9223372036854775807的Java long来表示所有ID。文档只是一个例子,但是api的设计考虑到了这一点(这就是为什么它返回long而不是int或double),所以它应该是一个long。但在大多数情况下,sqlite数据库中不会有90亿行,因此他们使用int作为userId的示例,因为它占用更少的内存(或者这是一个错误)。这就是我从中得到的。感谢您对它返回长时间的解释。您是对的,但是Room的API即使在最坏的情况下也应该工作,并且必须遵循SQlite的规范。对于这种特殊情况,在长时间内使用int实际上是一样的,额外的内存消耗是negligible@MatPag您的报告不再包括对这种行为的确认(遗憾的是,也没有)。经过一点搜索,我找到并更新了你答案中的链接。希望它能比上一条稍好一点,因为这是一个非常重要的信息。“如果您的语句成功“->则一条记录的插入返回值将为1根据本文档:如果@Insert方法只接收到1个参数,则它可以返回一个long,即插入项的新rowId。如果参数是数组或集合,则应返回long[]或List。“