Android Room - Get the Id of New Inserted Row with Auto-Generate

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 operation
  • long[] or Long[] or List<Long> for multiple insert operations
  • void 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



Leave a reply



Submit