Recently I've decided to start using LiveData in my Room database by adding it to ado methods and then observing them in ViewModel, then even though I updated DB version after making all the changes and made a migration, it throws this error on emulator launch
2022-07-03 15:35:17.723 4866-4891/com.example... E/SQLiteLog: (1) statement aborts at 1: [ROLLBACK;] cannot rollback - no transaction is active
2022-07-03 15:35:17.724 4866-4891/com.example... E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
Process: com.example..., PID: 4866
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
Caused by: android.database.sqlite.SQLiteException: cannot rollback - no transaction is active (code 1 SQLITE_ERROR)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:709)
at android.database.sqlite.SQLiteSession.endTransactionUnchecked(SQLiteSession.java:441)
at android.database.sqlite.SQLiteSession.endTransaction(SQLiteSession.java:403)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:589)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:422)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:151)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:112)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
at com.example...data.database.NoteDao_Impl$16.call(NoteDao_Impl.java:723)
at com.example...data.database.NoteDao_Impl$16.call(NoteDao_Impl.java:720)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
My dao code :
#Dao
interface NoteDao {
#Delete
fun deleteFolder(folder: Folder)
#Query("UPDATE Note SET isStarred = :state WHERE id = :id")
fun updateNoteChecked(id : Int, state : Boolean)
#Query("DELETE FROM Note WHERE id = :id")
fun deleteNoteById(id : Int)
#Query("SELECT * FROM Note WHERE id = :id")
fun getNoteById(id : Int) : Note
#Query("SELECT title FROM Folder WHERE id = :id")
fun getFolderTitleById(id : Int) : String
#Query("SELECT * FROM folder WHERE id = :id")
fun getFolderById(id : Int) : Folder
#Query("UPDATE Folder SET title = :title WHERE id = :id")
fun updateFolderTitle(id : Int, title : String)
#Query("SELECT * FROM Folder WHERE id = :id")
fun getFolderWithNotesByFolderId(id : Int) : FolderWithNotes
#Query("SELECT * FROM Note WHERE folderId = :id ORDER BY isStarred")
fun getNotesByFolderId(id : Int) : LiveData<List<Note>>
#Query("SELECT * FROM note WHERE content LIKE :query ORDER BY isStarred")
fun searchAllNotes(query : String?) : LiveData<List<Note>>
#Query("SELECT * FROM note WHERE folderId = :id AND content LIKE :query ORDER BY isStarred" )
fun searchNotesByFolderId(query : String?, id : Int) : LiveData<List<Note>>
#Query("SELECT * FROM Note ORDER BY isStarred")
fun getAllNotes() : LiveData<List<Note>>
#Query("SELECT * FROM Folder WHERE id = :id")
fun getFolderWithNotes(id : Int) : List<FolderWithNotes>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertFolder(folder : Folder)
#Query("SELECT * FROM Folder")
fun getAllFolders() : LiveData<List<Folder>>
#Query("UPDATE folder SET noteCount = noteCount + 1 WHERE id = :id")
fun updateOnInsertNote(id : Int)
#Query("UPDATE folder SET noteCount = noteCount - 1 WHERE id = :id")
fun updateOnDeleteNote(id : Int)
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertNote(note : Note)
#Delete
suspend fun deleteNote(note : Note)
}
Database class :
#Database(entities = [Note::class, Folder::class] , version = 11 )
abstract class NoteDatabase : RoomDatabase() {
abstract fun NoteDao() : NoteDao
}
Data base hilt provide method :
#Provides
#Singleton
fun provideNoteDatabase(app : Application) : NoteDatabase{
return Room.databaseBuilder(
app ,
NoteDatabase::class.java,
"notes",
).fallbackToDestructiveMigration()
.addCallback(DatabaseCallback())
.build()
}
As per my comment, I suspect that the issue is within the callback (DatabaseCallback) and probably in the overidden onOpen method.
The reasoning is that the trace shows:-
that you have issued a query as per at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
that this successfully opens the database as per at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
-and then appears to end a transaction (without committing the transaction). As Room is proven, then the most likely issue is that an attempt is being made to end a transaction when no transaction has been started and thus, due to the lack of the commit, there is nowhere/nothing to rollback to.
Related
#Dao
interface LectureDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(lecture: Lecture)
#Delete
fun delete(lecture: Lecture)
#Query("update lecture set attendanceCount = attendanceCount + 1")
fun incrementAttendance(lecture: Lecture)
#Query("select * from lecture")
fun getLectures(): LiveData<List<Lecture>>
}
Well, I have a problem when I write incrementAttendance function I got this error. What I'm trying to do is I want to increase the absentee status by 1 for a specific lesson. Here is my model:
#Entity(tableName = "lecture")
data class Lecture(
#PrimaryKey(autoGenerate = true)
val id: Int?,
val lectureName: String?,
val attendanceCount: Int?
)
Why do I need TypeConverter for this? And how can I solve?
And I already tried to change kotlin-room versions. And that didn't help.
I solved the problem by giving direct id instead of lecture class to parameter.
#Query("update lecture set absenceCount = absenceCount + 1 where id = :id")
suspend fun incrementAbsenceCount(id: Int)
I am complete new to kotlin, and trying to build a notes app using MVVM architecture. But somewhere I go wrong and end up with this error. My kotlin version is 1.5.31
NoteDao.java:11: error: Not sure how to handle insert method's return type.
public abstract java.lang.Object insert(#org.jetbrains.annotations.NotNull()
NoteDao.kt
#Dao
interface NoteDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(note:Note): Note
#Delete
suspend fun delete(note: Note): Note
#Query("SELECT * FROM notes_table ORDER BY primary_key ASC")
fun getAllNotes(): LiveData<List<Note>>
}
Note.kt
#Entity(tableName = "notes_table")
class Note(#ColumnInfo(name = "note_text", typeAffinity = TEXT) val text: String){
#PrimaryKey(autoGenerate = true) #ColumnInfo(name="primary_key", typeAffinity = INTEGER) var id = 0
}
I am not sure how to handle this.
Try doing this:
#Dao
abstract class BaseDao<T : BaseEntity>(private val tableName: String) {
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract suspend fun insert(entity: T): Long
#Update
abstract suspend fun update(entity: T)
#Delete
abstract suspend fun delete(entity: T)
}
abstract class BaseEntity {
abstract val id: Long
}
I want to get the last row id in room android using kotlin
here is my Entity
#Entity(tableName = "tb_customer")
data class Customer(
#PrimaryKey(autoGenerate = true)
var customerId: Long = 0L,
val firstName: String,
val lastName: String,
val emailAddress: String,
val address: String,
val profileImage: String?)
my Dao
#Dao
interface CustomerDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(customer: Customer): Long
#Insert
suspend fun insertPhones(customerPhones: ArrayList<CustomerPhones>)
#Delete
suspend fun delete(customer: Customer)
#Update
suspend fun update(customer: Customer)
#Query("SELECT * FROM tb_customer ORDER BY customerId ASC")
fun getAllCustomers(): LiveData<List<Customer>>
#Query("DELETE FROM tb_customer")
suspend fun clear()
}
**Please help me to get last inserted id **
I have found the answer you could try this code
Dao
#Query("SELECT customerId FROM tb_customer ORDER BY customerId DESC LIMIT 1")
fun getLastCustomer(): LiveData<Long>
a**nd this is the repository **
fun getLastCustomer(): LiveData<Long> {
return customerDao.getLastCustomer()
}
and this is the view model
fun getLastCustomer(): LiveData<Long> {
return repository.getLastCustomer()
}
and finally from your context your have to write
customerViewModel.getLastCustomer().observe(viewLifecycleOwner,{id->
Log.d("TAG ", id.toString())
})
I have list of custom object that i wanna save it in the database.
So I have to use TypeConverters to make this possible.
My problem that i get an error when I implement the functionality and I noticed
that a function annotated with TypeConverter never used
Here's the error:
A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
error: The columns returned by the query does not have the fields [avatar,email,first_name,id,last_name] in com.hraa.fakeusers.model.UserData even though they are annotated as non-null or primitive. Columns returned by the query: [data]
public abstract androidx.lifecycle.LiveData<java.util.List<com.hraa.fakeusers.model.UserData>> getUsers();
And here's the code:
#Entity(tableName = USER_DATA_TABLE)
data class DataModel(
val data: List<UserData>,
val page: Int,
val per_page: Int,
val total: Int,
val total_pages: Int
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = null
}
data class UserData(
val avatar: String,
val email: String,
val first_name: String,
val id: Int,
val last_name: String
)
class Converters {
#TypeConverter
fun toUsersData(value: String): List<UserData> {
val type = object : TypeToken<List<UserData>>() {}.type
return Gson().fromJson(value, type)
}
#TypeConverter
fun fromUsersData(usersData: List<UserData>): String {
return Gson().toJson(usersData)
}
}
#Database(entities = [DataModel::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {
abstract fun dataDao(): DataDao
}
#Dao
interface DataDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(data: DataModel)
#Delete
suspend fun deleteData(data: DataModel)
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
}
Note: toUsersData() function never used.. I don't know why.
Note: toUsersData() function never used
How can you be sure of that? My guess is that this function could work well, but you have two type's transformations here:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
Transformation #1 (row level). Input: String (saved in db). Output: data (List).
That should be processed well thanks to your toUsersData() method (may be not, I've not checked, but it seems it should do)
Transformation #2 (row level). Input: data (List). Output: UserData (According to your desired return type). For that Room doesn't know how to do this transformation, so you have en error.
To check if your toUsersData() really works you can test next query:
#Query("SELECT * FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<DataModel>>
If your build is successful, then there is no problem with this function. You can also find this function in Java-class, that was autogenerated by Room during build.
You can try to add another data class:
data class UserDataList(
val data: List<UserData>
)
and change your data method to:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserDataList>>
I have following interface where I have created the standard crud methods and annotated the methods with insert, update, and delete.
interface BaseDao<T> {
#Insert
fun insert(table: T): Single<Long>
#Insert
fun insert(vararg table: T): Single<List<Long>>
#Update
fun update(table: T): Single<Int>
#Delete
fun delete(table: T): Single<Int>
}
I then create a interface for the DAO
#Dao
interface WeatherDao : BaseDao<WeatherTable> {
override fun insert(table: WeatherTable): Single<Long>
override fun insert(vararg table: WeatherTable): Single<List<Long>>
override fun update(table: WeatherTable): Single<Int>
override fun delete(table: WeatherTable): Single<Int>
#Query("SELECT * FROM weatherTable")
fun getAllWeather(): Single<List<WeatherTable>>
#Query("SELECT * FROM weatherTable WHERE id = :id LIMIT 1")
fun getWeatherById(id: Long): Single<WeatherTable>
#Query("SELECT count(*) FROM weatherTable")
fun count(): Single<Int>
}
When I compile I get a lot of error like this following:
error: An abstract DAO method must be annotated with one and only one of the following annotations: Insert,Delete,Query,Update,RawQuery
public abstract io.reactivex.Single<java.lang.Long> delete(#org.jetbrains.annotations.NotNull()
Because when I inherited from the interface. I have to manually add the #Insert, #Update, and #Delete.
Just wondering why these annontations are added automatically in the my WeatherDao interface.
So I now have to manually add them like this:
#Dao
interface WeatherDao : BaseDao<WeatherTable> {
#Insert
override fun insert(table: WeatherTable): Single<Long>
#Insert
override fun insert(vararg table: WeatherTable): Single<List<Long>>
#Update
override fun update(table: WeatherTable): Single<Int>
#Delete
override fun delete(table: WeatherTable): Single<Int>
#Query("SELECT * FROM weatherTable")
fun getAllWeather(): Single<List<WeatherTable>>
#Query("SELECT * FROM weatherTable WHERE id = :id LIMIT 1")
fun getWeatherById(id: Long): Single<WeatherTable>
#Query("SELECT count(*) FROM weatherTable")
fun count(): Single<Int>
}
Just wondering if I am using this wrong:
Following this google repo, you are not doing the abstractation correctly. To sum up, you do not need to have the inserts/updates/deletes in your #Dao Interface and it should be abstract.
interface BaseDao<T> {
/**
* Insert an object in the database.
*
* #param obj the object to be inserted.
*/
#Insert
fun insert(obj: T)
/**
* Insert an array of objects in the database.
*
* #param obj the objects to be inserted.
*/
#Insert
fun insert(vararg obj: T)
/**
* Update an object from the database.
*
* #param obj the object to be updated
*/
#Update
fun update(obj: T)
/**
* Delete an object from the database
*
* #param obj the object to be deleted
*/
#Delete
fun delete(obj: T)
}
#Entity(tableName = "data")
data class Data(#PrimaryKey val id: String, val value: String)
#Dao
abstract class DataDao : BaseDao<Data>() {
/**
* Get all data from the Data table.
*/
#Query("SELECT * FROM Data")
abstract fun getData(): List<Data>
}