Android Room - Get the id of new inserted row with auto-generate
Based on the documentation here (below the code snippet)
A method annotated with the @Insert
annotation can return:
long
for single insert operationlong[]
orLong[]
orList<Long>
for multiple insert operationsvoid
if you don't care about the inserted id(s)
How to get the id of inserted row in android room databse in Kotlin?
As per the documentation here
If the @Insert method receives a single parameter, it can return a
long value, which is the new rowId for the inserted item. If the
parameter is an array or a collection, then the method should return
an array or a collection of long values instead, with each value as
the rowId for one of the inserted items. To learn more about returning
rowId values, see the reference documentation for the @Insert
annotation, as well as the SQLite documentation for rowid tables
So you can use it like
@Insert(onConflict = OnConflictStrategy.REPLACE)
long addUserWithLong(user: User)
or if you are inserting a list
@Insert(onConflict = OnConflictStrategy.REPLACE)
long[] addUserWithLong(user: List<User>)
Edit-1
After checking answers from this post.
No, you can't. I wrote an answer to the issue. The reason is, that
LiveData is used to notify for changes. Insert, Update, Delete won't
trigger a change.
I just created a test project and successfully received Id of last inserted item in activity. Here is my implementation.
Dao
@Insert
suspend fun addUser(user: Users): Long
Repo
suspend fun insertUser(context: Context, users: Users): Long {
val db = AppDatabase.getInstance(context)
val dao = db.userDao()
return dao.addUser(users)
}
ViewModel
fun addUser(context: Context, users: Users) = liveData {
//you can also emit your customized object here.
emit("Inserting...")
try {
val userRepo = UsersRepo()
val response = userRepo.insertUser(context, users)
emit(response)
} catch (e: Exception) {
e.printStackTrace()
emit(e.message)
}
}
Activity
viewModel.addUser(applicationContext, user).observe(this, Observer { userId ->
Log.d("MainActivity", "Inserted User Id is $userId")
})
Check test application here.
How to return newly inserted item(row) id using android room (Kotlin)?
Your insert
method in the viewmodel is launching a new coroutine, it returns before it has executed the insert, so you get the initial value of 0. If you wait for the job to complete you will get the correct id.
Change it like this to see correct value:
var insertedId = 0L
fun insertItem(item: SingleItem) = viewModelScope.launch {
insertedId = myRepository.insertItem(item)
Log.i("INSERT_ID", "Inserted ID is: $insertedId")
}
If you want to get this value in your fragment you have to wait for the job to complete. The best way is to make your viewmodel insert
method a suspend
function instead of a fire and forget.
fun insertItem(item: SingleItem) =
myRepository.insertItem(item)
and in the fragment
lifecycleScope.launch {
val id = viewModel.insertItem(item)
Log.i("INSERT_ID", "Inserted ID is: $id")
}
but note that this is the wrong architecture, all business logic should be in the viewmodel, not the fragment.
Room API - How to retrieve recently inserted generated id of the entity?
Have your @Insert
method return a long
:
@Insert
long insertStudent(Student s);
The return value will be the rowId
of the row, and that should be the auto-generated primary key value.
Android Room - Get the id of new inserted row with auto-generate - MVVM Version
Okay so what I ended up doing and what seems to be working pretty well is this:
Basically used the structure from this answer:
https://stackoverflow.com/a/12575319/1972372
And implemented the AsynTask
callback-interface in my ViewModel
with a MutableLiveData
object for the Activity to call once in the beginning and hang on to until it is updated once:
public class ChallengeViewModel extends AndroidViewModel implements ChallengesRepository.UpdateChallengeInfosAsyncTask.AsyncResponse {
private MutableLiveData<Integer> insertedChlgInfoID;
....
....
public void insertChallengeInfos(ChallengeInfos challengeInfos) {
mRepository.insertChallengeInfos(challengeInfos, this);
}
public LiveData<Integer> getInsertedChlgInfoID() {
return insertedChlgInfoID;
}
@Override
public void processFinish(Long result) {
insertedChlgInfoID.setValue(Ints.checkedCast(result));
}
}
I understand the reasons why AsyncTask
may be called bad in certain scenarios, but I think in my case it really works fine and doesn't really have any potential risks of memory leaks or similar, since it's only very basic write operations. And rewriting the whole logic doesn't seem to be of any benefit for my case.
Android Room - Get the id of new inserted row form @Insert
You can do that using a listener interface that has a callback that accepts a long value of the inserted row id in the database.
Listener Interface
public interface NewIdListener {
void onInsert(long id);
}
Dao
@Dao
public interface UserDao {
@Insert
long insert(UserEntity userEntity); // return id of the inserted userEntity
}
Repository
public class UserRepository {
private Executor mExecutor = Executors.newSingleThreadExecutor();
private UserDao userDao;
...
public void insertUserEntity(final UserEntity entity, final NewIdListener listener) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
listener.onInsert(userDao.insert(entity));
}
});
}
ViewModel
public void insertUserEntity(UserEntity entity, NewIdListener listener) {
userRepository.insertUserEntity(entity, listener);
}
Activity
userViewModel.insertUserEntity(new UserEntity("User Name", "12345678"), new NewIdListener() {
@Override
public void onInsert(final long id) {
requireActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(requireActivity(), "Id: " + id, Toast.LENGTH_SHORT).show();
}
});
}
});
Note: For background thread, I've used Executor
instead of AsyncTask
as AsyncTask
is deprecated now.
Kotlin - return new inserted id from Room database using Persistence Room:runtime lib
If you're inserting multiple entities, you can only get their IDs back in an array or list, for example, like this:
@Insert
fun insertUsers(vararg userRegistrationEntities: UserRegistrationEntity): List<Long>
If you insert one entity at a time, you can get its ID back as a Long
:
@Insert
fun insertUser(userRegistrationEntity: UserRegistrationEntity): Long
Using Room, how to get the last inserted auto-generated ID , including when the table didn't get anything inserted into it?
And the answer:
SpecialDao.kt
@Suppress("AndroidUnresolvedRoomSqlReference")
@Dao
abstract class SpecialDao {
@Query("SELECT seq FROM sqlite_sequence WHERE name = :tableName")
abstract fun getSequenceNumber(tableName: String): Long?
}
Sample to show it works:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DBProvider.init(this)
AsyncTask.execute {
val dao = DBProvider.DB.dao()
var sequenceNumber = dao.getSequenceNumber("favorite_speed_dial")
Log.d("AppLog", "id:$sequenceNumber")
val favoriteSpeedDialDao = DBProvider.DB.favoriteSpeedDialDao()
var favoritesList = favoriteSpeedDialDao.getFavoritesList()
Log.d("AppLog", "favoritesList:${favoritesList}")
Log.d("AppLog", "deleting all and inserting a new item...")
favoriteSpeedDialDao.deleteAll()
favoriteSpeedDialDao.insert(FavoriteSpeedDialDTO(0L, "123"))
favoritesList = favoriteSpeedDialDao.getFavoritesList()
Log.d("AppLog", "favoritesList:${favoritesList}")
sequenceNumber = dao.getSequenceNumber("favorite_speed_dial")
Log.d("AppLog", "id:$sequenceNumber")
}
}
companion object {
fun toString(collection: Collection<*>?): String {
if (collection == null)
return "null"
val sb = StringBuilder("{")
var isFirst = true
for (`object` in collection) {
if (!isFirst)
sb.append(',')
else
isFirst = false
sb.append(`object`)
}
sb.append('}')
return sb.toString()
}
}
}
Related Topics
How to Reference an Asset in a Library Project
How to Check If "Android.Permission.Package_Usage_Stats" Permission Is Given
Android Saf (Storage Access Framework): Get Particular File Uri from Treeuri
How to Get Profile Like Gender from Google Signin in Android
Android: Failed to Allocate Memory
How to Save Image in Shared Preference in Android | Shared Preference Issue in Android with Image
Using the Limit Statement in a SQLite Query
Android Webview Click Open Within Webview Not a Default Browser
Bring Application to Front After User Clicks on Home Button
Continually Running Background Service
Android Searchview Filter Listview
How to Set the Airplane_Mode_On to "True" or On
Room Persistence: Error:Entities and Pojos Must Have a Usable Public Constructor
Android Action Bar Searchview as Autocomplete
Google Maps API V2 'Failed to Load Map. Could Not Contact Google Servers'
How to Implement an Android:Background That Doesn't Stretch
How to Change Proxy Settings in Android (Especially in Chrome)