Android room inserting with a query - android

I know inserting data into SQlite room library can be done through #Insert annotation, but I (out of curiosity) tried inserting values through SQL statement but Android studio showed me error stating column names can't be resolved. Is it a way of forcing developer to use #Insert annotation or if I am doing something wrong here? Thanks!
Please review following screenshot -

If you omit the column list that is being highlighted and thus provide values for all columns e.g. :-
#Query("INSERT INTO TestTable VALUES(null,:a,:b)")
List<TestTable> getall(String a, String b, String columna, String columnb);
Android Studio doesn't complain but then you get a compiler error of :-
error: INSERT query type is not supported yet. You can use:SELECT, DELETE, UPDATE
So perhaps better sooner than later.

#Entity
data class ModulesRoom(
#PrimaryKey(autoGenerate = false)
var id: Int = 0,
var nav: String = "",
var name: String = "",
var imageurl: String = ""
)
for List Object
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveEmployees(modules: List<ModulesRoom>)
for single obbjet
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveEmployees(modules: ModulesRoom)
Ussage
val mModules = Modules()
mModules.id = values
mModules.nav = values
saveEmployees(mModules)

What I am doing is calling getOpenHelper().getWritableDatabase() on the RoomDatabase.
Then you get the SupportSQLiteDatabase object that is essentially the non-Room way.
From that you can call execSQL() or use insert() with table name and ContentValues.

Related

Kotlin Room SUM operation

I need to perform column sum operation
But I don't understand what exactly I need to insert into fun getSumItem(): Flow<Cursor>
this is my Dao
#Dao
interface Dao {
#Insert
fun InsertItem(item: Income)
#Query("SELECT SUM(sum) AS value FROM income")
fun getSumItem(): Flow<Cursor>
}
this is my table
#Entity(tableName = "income")
data class Income(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
#ColumnInfo(name = "date")
var date: String,
#ColumnInfo(name = "type")
var type: String,
#ColumnInfo(name = "sum")
var sum: Float,
)
I did not find a similar example and any detailed information on the Internet.
when I use Cursor i get this error:
Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type). - android.database.Cursor
all other options that I found on the Internet also gave errors
When you say SELECT SUM(sum) AS value FROM income the output that Room handles will be a Cursor with a single column named value. Room does not know how to extract this value and build a Cursor object from that single value.
Room expects to extract data from the underlying Cursor that is generated by the SQLite API, which Room is a wrapper to/around, and deliver the data according to the object that the function returns (and thus must be able to ascertain how to build the resultant object).
As Room extracts a single value then you simply need to retrieve the value returned as a Float.
fun getSumItem(): Flow<Float>

Why would this query work in the Database Inspector section of the App Inspection in Android Studio, but not within a Room Query?

I have a table within my local SQLite database, the Class for it is as follows:
#Entity(tableName = "table_bp_reading")
class BPReading(
var systolicValue: Int = 120,
var diastolicValue: Int = 80,
var pulseValue: Int = 72,
var timeStamp: String = getDateTimeStamp(),
#PrimaryKey(autoGenerate = true) var pId: Int = 0
) {
...
The relevant part of the Dao looks as such:
...
#Query("SELECT * FROM table_bp_reading WHERE timeStamp BETWEEN :startDate AND :endDate")
fun getReadingsByDateRange(startDate: String, endDate: String): Flow<List<BPReading>>
...
The database inspector displays a table with
the following entries.
Running the following query within the Database Inspector:
SELECT * FROM table_bp_reading WHERE timeStamp BETWEEN '2021-09-18 00:00:00' AND '2021-09-18 23:59:59'
Gives the following result.
However, when I try to observe the data using the following:
bpReadingViewModel.bpReadingsByDate("2021-09-18 00:00:00", "2021-09-18 23:59:59").observe(viewLifecycleOwner, {
bpReading ->
bpReading.let {
bpReadingsByDate = it
if(it.isNotEmpty()) bindDBDataToScatterChart()
}
})
No data is displayed on my end, the query results in an empty list.
Note, however, that if instead of using the full timestamp with time, I just use a date string such as "2021-09-18":
bpReadingViewModel.bpReadingsByDate("2021-09-18", "2021-09-18").observe(viewLifecycleOwner, {
bpReading ->
bpReading.let {
bpReadingsByDate = it
if(it.isNotEmpty()) bindDBDataToScatterChart()
}
})
And I also change the query from the Dao given previously to the following:
#Query("SELECT * FROM table_bp_reading WHERE DATE(timeStamp) BETWEEN :startDate AND :endDate")
fun getReadingsByDateRange(startDate: String, endDate: String): Flow<List<BPReading>>
It works and it returns the 3 entries for that day as it is meant to. However, once I change the date range in the above, to filter between 2021-09-18 and 2021-09-19, it stops working again.
Please help, I am so confused by what is going on here.
EDIT:
Here's the relevant call from Repository:
fun readingsByDateRange(s: String, e: String): Flow<List<BPReading>> =
bpReadingDao.getReadingsByDateRange(e, s)
And the relevant call from the ViewModel for the class:
fun bpReadingsByDate(s: String, e: String): LiveData<List<BPReading>> =
repository.readingsByDateRange(s, e).asLiveData()
Which is why it is indeed bpReadingsByDate, and not getReadingsByDateRane. Sorry, this is my mistake for not being consistent with naming the function across files.
The query is fine (tested), from your comments and edited question, you have swapped the start and end timestamps which will normally result in nothing being selected.
So you should change bpReadingDao.getReadingsByDateRange(e, s)to be bpReadingDao.getReadingsByDateRange(s, e).

how to query a room database by row android kotlin

I am new to Room and it's throwing me through a loop. I am trying to take a specific row of a specific data class in my Room database and compile into a list and turn this list into a String so I can do stuff with it. I can sort of do this by printing the contents of the Room database table to the console, but that's it.
I'll show you my code that I have
this is my data class.
code.kt
#Parcelize
#Entity(tableName = "code_table")
data class code(
#PrimaryKey(autoGenerate = true)
val id: Int,
val code: String //this is the item I want to isolate
): Parcelable
a snippet of my ViewModel.kt that I use.
val readAllData: LiveData<List<code>>
private val repository: respository
init {
val Dao = database.getDatabase(application).dao()
repository = respository(Dao)
readAllData = repository.readAllData
}
a snippet of my repository.kt that I use.
val readAllData: LiveData<List<code>> = dao.readAllData()
a snippet of my Dao.kt that I use.
#Query("SELECT * FROM code_table ORDER BY id ASC")
fun readAllData(): LiveData<List<code>>
How I read the Room database table
private fun dbread(){
mUserViewModel = ViewModelProvider(this).get(ViewModel::class.java)
mUserViewModel.readAllData.observe(viewLifecycleOwner, Observer { user ->
var abc = user
println(abc)
})
}
this is what it outputs
I/System.out: [code(id=2, code=textA), code(id=3, code=textB), code(id=4, code=textC)]
I am not hell-bent on only querying the code row but I need to aggregate all the data in the code row into a string or JSON array. so, in this case, that would be the textA, textB and textC (I'm worried I haven't made myself clear)
I have also tried doing the following in the Dao
#Query("SELECT code FROM code_table")
fun readdb(): LiveData<List<code>>//I've tried this with lot different types within the Parentheses
this makes the build fail: it says the following
:app:kaptDebugKotlin 1 error
java.lang.reflect.InvocationTargetException (no error message)
I would guesstimate that this error is because the SQL syntax is off but I don't think it is.
I have also tried messing around with the ViewModel and repository to see if I can get them to only output just the code row but to no avail. I'm surprised I'm the first to post about this.
thank you for your time.
You can obtain you desidered result just by using SQL.
In Android (and with Room) you're using a SQLite database, so the most important thing is writing some SQL that is compliant with SQLite.
Then you can select the concatenation of all the values in your table, just by asking to the database to do it. You can achieve it with this:
#Query("SELECT GROUP_CONCAT(code) FROM code_table")
fun readConcatenatedCode(): LiveData<String>
The returned value will be a LiveData of String, and the String will contain all the values, concatenated.

Room automatically sorts on the basis of primary key in Android

I have a data class like this
#Entity
data class Question(
#field:SerializedName("question")
var question: String? = null,
#field:SerializedName("answers")
var answers: ArrayList<String?>? = null,
#field:SerializedName("id")
#PrimaryKey
var id: Int? = null
)
Then in DAO I have saving and getting methods like this
#Dao
interface QnADao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveQuestion(questions:Question)
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveAllQuestions(questions: List<Question?>?)
#Query("SELECT * from Question")
fun getAllQnA():List<Question>
}
I am saving a list of Questions and then later on retrieving them. So whenever I retrieve them I get the list sorted according to the id which is the primary key.
So if I am saving questions with id:254, id:23, id:45 and id:92 then I am getting it like this id:23, id:45, id:92 and id:254
But I don't need a sorted list like that, I need to get the data as it was saved in the database. Any help would be appreciated. Thanks.
Try to use autoGenerate = true for primary key so it will create PK number in sequence
See below lines
#PrimaryKey(autoGenerate = true)
So that now your insert and retrive order will be same
You can add a Date field to your Question entity
#field:SerializedName("date")
var date: Date? = null,
and order your entities by date
#Query("SELECT * FROM Question ORDER BY date DESC")

TypeConverters not working for Collections in #Query

I've an entity called Events which is defined as follows:
#Entity(tableName = "Events")
data class Event(#PrimaryKey val id: Long, val name: String, val venues: Set<String>, val rating: Int)
I've a pair of #TypeConverter methods for handling Set<String>:
#TypeConverter
fun fromStringToStringSet(str: String): Set<String> = str.split("<|>")
#TypeConverter
fun fromStringSetToString(set: Set<String>): String = set.joinToString("<|>")
In my Dao, I've a method annotated with #Query as follows:
#Query("UPDATE Events SET name = :name, venues = :venues WHERE id = :id")
fun updateAndRetainRating(id: Long, name: String, venues: Set<String>)
When I try to update an event with 2 venues, I get a runtime error telling me that the SQL couldn't be compiled. The generated SQL is:
UPDATE Events SET name = ?, venues = ?,? WHERE id = ?
This is obviously wrong. Looking into the generated code, Room is getting the size of Set<String> and adding the same number of ?s.
Why isn't my TypeConverter being used? I don't face this issue in other queries(#Insert and #Query(for SELECT)). Other TypeConverters are also working fine.
EDIT: The same issue also occurs if I use List<String> + TypeConverters instead of Set<String>.
Looking into the Room documentation, it appears that whenever we use a Collection in #Query, it bypasses TypeConverters and is straight away flattened.
For example, this:
#Query("SELECT * FROM Customers WHERE city IN (:cities)")
fun getCustomersInCities(cities: List<String>): Flowable<List<Customer>>
results in SELECT * FROM Customers WHERE city IN ('london', 'paris') if cities contains "london" and "paris".
Unfortunately, the following is also converted in a similar way:
#Query("UPDATE Customers SET phoneNumbers = :phoneNumbers WHERE id = :id")
fun updateCustomerPhoneNumbers(id: Long, phoneNumbers: List<String>)
The result is UPDATE Customers SET phoneNumbers = 1234567890, 9876543210 WHERE id = 23 if id is 23 and the phoneNumbers contains "1234567890" and "9876543210".
While this does make sense, it is really inconvenient and should be documented more clearly.
This solution worked for me:
TypeConverter not working when updating List<Boolean> in Room Database
Basically, replace List<String> by ArrayList<String>.

Categories

Resources