Requery & creating database from sql dump: how to? - android

I'm trying to use requery https://github.com/requery/requery library with Kotlin and SQLite backend. I have a sql dump, which I want to write to sqlite database in the first launch of an application, and then I want to map data classes to database entities with requery.
Here is data source initialization with table creation:
if (!(DataStorage.isDbInitialized(context))) {
val db = writableDatabase
val inputStream = context?.resources?.openRawResource(R.raw.dump)
val reader = BufferedReader(InputStreamReader(inputStream))
val builder = StringBuilder()
var line : String?
var end = false
while (!end) {
line = reader.readLine()
if(line == null) {
end = true
} else {
builder.append(line)
}
}
db.execSQL(builder.toString())
onCreate(db)
DataStorage.setDbInitialized(context)
}
I have to derive this class from both SqlitexDatabaseSource and CommonDataSource to use with Kotlin. SQL query execuled successfully, but when I trying to select all objects from database, this request returns zero sized list:
val result : Result<Feat> = (application as MainApp).dataStore.select(Feat::class).get()
result.each {
Log.d("TAG", it.name)
}
DTO created as described in documentation:
https://github.com/Syjgin/PathfinderFeats/blob/master/app/src/main/java/com/syjgin/pathfinderfeats/model/Feat.kt
Is it possible to initialize requery data with sql dump, or I have to create DTO for each row and submit it via insert method?

Related

Insert list of class's object to Room by parsing API data using MVVM in Kotlin

I built an application in Kotlin using MVVM. I fetched the API response from the server successfully. Now I want to insert API's parsing data into RoomDB.
API response included both JSON Object & Array. I want to insert specific data from API to DB. Please help me with how I can make an entity class and set parsing data to the class data members with/without loops and insert it into RoomDB by making a single list of the whole class.
Please provide tutorial links or any kind of material links instead of the Android Developers Guide. Thanks a lot!
In API Response we have many data but actually, we don't need that all that so basically we to create one data class that is only constant the specific that actually, we need. and that all operation is performed in a repository and we manage it.
entity class that only contains essential data
#Entity(tableName = "wallpaper")
data class WallpaperDataClass (
#PrimaryKey(autoGenerate = true)
val note_id:Int=0,
val photoId: Int,
val photos_url: String,
val photographer_name: String,
val photographer_url: String,
val src:String
)
Fill the data in model
if (NetworkUtils.isOnline(applicationContext)) {
/**
* Online
* if Your net is online then call api
*/
try {
val result: Response<PhotoModel> =
wallpaperInterface.getWallpaper(authorization, page, per_page)
if (result.body() != null) {
val photos = mutableListOf<WallpaperDataClass>()
result.body()!!.photos.forEach {
// in blows line we set data in modal
val wallpaperDataClass = WallpaperDataClass(
photoId = it.id,
photos_url = it.url,
photographer_name = it.photographer,
photographer_url = it.photographerUrl,
src = it.src.portrait
)
photos.add(wallpaperDataClass)
if (!offlineDatabase.getDao().exists(it.id)){
offlineDatabase.getDao().insertWallpaper(wallpaperDataClass)
}
mutableLiveData.postValue(ErrorHandling.Success(photos))
}
} else {
Log.d("WallpaperResponse", "getWallpaper: ${result.message()}")
}
} catch (e: Exception) {
mutableLiveData.postValue(ErrorHandling.Faild(e.localizedMessage!!.toString()))
}
} else {
/**
* Offline
*if Your net is offline then fetch from db
*/
try {
val wallpaper = offlineDatabase.getDao().getOfflineWallpaper()
mutableLiveData.postValue(ErrorHandling.Success(wallpaper))
} catch (e: Exception) {
mutableLiveData.postValue(ErrorHandling.Faild(e.localizedMessage!!.toString()))
}
}
}
}
Video Tutorial

How to export Room Database to .CSV

How can I export my Room Database to a .CSV file. I would like it to be saved to device storage. I searched everything and no answer was suitable. I hope there is a way for this.
You cannot just save a database as a CSV. However the database, if fully checkpointed, is just a file. If not fully checkpointed then it (unless write-ahead logging as been disabled) would be three files.
The database itself consists of various parts, a header (first 100 bytes of the file) and then blocks of data for the various components. Most of these dependant upon the schema (the tables), there are also system tables
sqlite_master is a table that holds the schema
if autogenerate = true is used for a integer type primary key then there is also the sqlite_sequence table
room itself has the room_master_table in which room stores a hash, this being compared against a compiled hash based upon the Room's expected schema.
To save all that data as a CSV, would be complex (and needless as you can just copy the database file(s)).
If what you want is a CSV of the app's data, then that would depend upon the tables. If you a single table then extracting the data as a CSV would be relatively simple but could be complicated if the data includes commas.
If there are multiple tables, then you would have to distinguish the data for the tables.
Again the simplest way, if just securing the data is to copy the file.
However as an example based upon :-
A database that has 3 tables (apart from the system tables)
PostDataLocal (see below for columns)
GroupDataLocal
AdminDataLocal
an existing answer has been adapted for the example
Then:-
The following in an #Dao annotated interface (namely AllDao) :-
#Query("SELECT postId||','||content FROM postDataLocal")
fun getPostDataLocalCSV(): List<String>
#Query("SELECT groupPostIdMap||','||groupId||','||groupName FROM groupDataLocal")
fun getGroupDataLocalCSV(): List<String>
#Query("SELECT adminGroupIdMap||','||userId||','||adminName||','||avatar FROM adminDataLocal")
fun getAdminDataLocalCSV(): List<String>
And the following function where dao is an AllDao instance previously instantiated :-
private fun createCSV() {
val sb = StringBuilder()
var afterFirst = false
sb.append("{POSTDATALOCAL}")
for (s in dao.getPostDataLocalCSV()) {
if(afterFirst) sb.append(",")
afterFirst = true
sb.append(s)
}
afterFirst = false
sb.append("{GROUPDATALOCAL}")
for (s in dao.getGroupDataLocalCSV()) {
if (afterFirst) sb.append(",")
afterFirst = true
sb.append(s)
}
afterFirst = false
sb.append("{ADMINDATALOCAL}")
for (s in dao.getAdminDataLocalCSV()) {
if ((afterFirst)) sb.append(",")
afterFirst = true
sb.append(s)
}
Log.d("CSV_DATA","CSV is :-\n\t$sb")
}
And then in an activity (where dao has been instantiated) the following:-
createCSV()
Then, when the database contains the following data (extracted via App Inspection) :-
PostDataLocal
GroupDataLocal
AdminDataLocal
The result written to the log (as could be written to a file rather than the log) is :-
D/CSV_DATA: CSV is :-
{POSTDATALOCAL}1,Post001,2,Post002,3,Post003{GROUPDATALOCAL}1,1,Group001 (Post001),1,2,Group002 (Post001),1,3,Group003 (Post001),2,4,Group004 (Post002),2,5,Group005 (Post002),3,6,Group006 (Post003){ADMINDATALOCAL}1,1,Admin001,admin001.gif,1,2,Admin002,admin002.gif,1,3,Admin003,admin003.gif,2,4,Admin004,admin004.gif,2,5,Admin005,admin005.gif,3,6,Admin006,admin006.gif,4,7,Admin007,admin007.gif,5,8,Admin008,admin008.gif,6,9,Admin009,admin009.gif,6,10,Admin010,admin010.gif
Note how headers have been included to distinguish between the tables
of course no consideration has been given to the inclusion of commas in the data (the above is intended to just show that in-principle you can generate a CSV representation of the data relatively easily)
Additional
Here's a more automated version in which you don't need to create the #Query annotated functions, rather it interrogates sqlite_master to extract the tables and the uses the table_info pragma to ascertain the columns, building the respective SQL.
As such it should cater for any Room database.
It also allows for the replacement of commas in the data with an indicator of a comma that could then be replaced when processing the CSV.
The supportive (secondary/invoked by the primary) function being :-
private fun getTableColumnNames(tableName: String, suppDB: SupportSQLiteDatabase): List<String> {
val rv = arrayListOf<String>()
val csr = suppDB.query("SELECT name FROM pragma_table_info('${tableName}')",null)
while (csr.moveToNext()) {
rv.add(csr.getString(0))
}
csr.close()
return rv.toList()
}
And the Primary function :-
private fun AutoCreateCSV(): String {
val replaceCommaInData = "{COMMA}" /* commas in the data will be replaced by this */
val rv = StringBuilder()
val sql = StringBuilder()
var afterFirstTable = false
var afterFirstColumn = false
var afterFirstRow = false
val suppDb = db.getOpenHelper().writableDatabase
var currentTableName: String = ""
val csr = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE('sqlite_%') AND name NOT LIKE('room_%') AND name NOT LIKE('android_%')", null)
while (csr.moveToNext()) {
sql.clear()
sql.append("SELECT ")
currentTableName = csr.getString(0)
if (afterFirstTable) rv.append(",")
afterFirstTable = true
afterFirstColumn = false
rv.append("{$currentTableName},")
for (columnName in getTableColumnNames(currentTableName,suppDb)) {
if (afterFirstColumn) sql.append("||','||")
afterFirstColumn = true
sql.append("replace(`$columnName`,',','$replaceCommaInData')")
}
sql.append(" FROM `${currentTableName}`")
val csr2 = db.query(sql.toString(),null)
afterFirstRow = false
while (csr2.moveToNext()) {
if (afterFirstRow) rv.append(",")
afterFirstRow = true
rv.append(csr2.getString(0))
}
csr2.close()
}
csr.close()
return rv.toString()
}
Using the same data and as the primary function returns a String the following code Log.d("CSV_DATA2",AutoCreateCSV()) results in :-
D/CSV_DATA2: {PostDataLocal},1,Post001,2,Post002,3,Post003,{GroupDataLocal},1,1,Group001 (Post001),1,2,Group002 (Post001),1,3,Group003 (Post001),2,4,Group004 (Post002),2,5,Group005 (Post002),3,6,Group006 (Post003),{AdminDataLocal},1,1,Admin001,admin001.gif,1,2,Admin002,admin002.gif,1,3,Admin003,admin003.gif,2,4,Admin004,admin004.gif,2,5,Admin005,admin005.gif,3,6,Admin006,admin006.gif,4,7,Admin007,admin007.gif,5,8,Admin008,admin008.gif,6,9,Admin009,admin009.gif,6,10,Admin010,admin010.gif
and if the data includes a comma e.g. Post001 is changed to be the value Post001, <<note the comma in the data>>
Then :-
D/CSV_DATA2: {PostDataLocal},1,Post001{COMMA} <<note the comma in the data>>,2,Post002,3 ....
this additional solution also fixes a little bug in the first where some separating commas were omitted between the header and the data.
Get all your data as a list from room and use this library
https://github.com/doyaaaaaken/kotlin-csv
It works well, here is my usage
private fun exportDatabaseToCSVFile(context: Context, list: List<AppModel>) {
val csvFile = generateFile(context, getFileName())
if (csvFile != null) {
exportDirectorsToCSVFile(csvFile, list)
} else {
//
}
}
private fun generateFile(context: Context, fileName: String): File? {
val csvFile = File(context.filesDir, fileName)
csvFile.createNewFile()
return if (csvFile.exists()) {
csvFile
} else {
null
}
}
private fun getFileName(): String = "temp.csv"
fun exportDirectorsToCSVFile(csvFile: File, list: List<AppModel>) {
csvWriter().open(csvFile, append = false) {
// Header
writeRow(listOf("row1", "row2", "row3"))
list.forEachIndexed { index, appModel ->
writeRow(listOf(getRow1, getRow2, getRow3))
}
shareCsvFile(csvFile)
}
}
private fun shareCsvFile(csvFile: File) {
// share your file, don't forget adding provider in your Manifest
}

How to access value outside of foreach on Android Studio, Kotlin, Room Database

I am writing a code for the access room database. I want to use some values outside of foEeach. for example:
my DAO query is:
#Query("SELECT * FROM table_users WHERE id = :id ")
fun getSingleUserDetails(id: Int): List<UsersEntity>
and accessing on MainActivity:
val db = AppDB.getApplicationDatabase(this)
db.boxLocationsDao().getSingleUserDetails(rowId).forEach {
val rowId = it.id
val userName = it.name }
In this case I can use all values within the forEach {}, but now I want to use the "userName" (or any other values) outside of forEach's "{}". How can I do that?
It's quite simple you can create a variable rowId and userName before forEach{} and store value inside forEach{} so you can use stored value outside forEach.
I have attached the code below.
var rowId:Int = 0
var userName:String = ""
val db = AppDB.getApplicationDatabase(this)
db.boxLocationsDao().getSingleUserDetails(rowId).forEach {
rowId = it.id
userName = it.name
}
//use **rowId** and **userName** variables here(outside foreEach{})

Using a coroutine inside a Transform.map function

Well here is a tricky one.
I have a query from my Room DB. The result is a Relational POJO
class MessageWithMsgQueueAccount {
#Embedded
var message: MessageDto? = null
#Relation(parentColumn = "clientMessageId", entityColumn = "clientMessageId", entity = MessageQueueDto::class)
var messageQueueList: List<MsgQueueAccount> = ArrayList()
}
So when i get that i apply a Transformation to this object so i can create another one that has only the information that i want.
fun toContactMessageChatItem(item: MessageWithMsgQueueAccount?, accountDto: AccountDto): MessageChatItem {
item?.message?.let {
// Procedure for creating the fields that i want
val isQuoted = it.quotemsgid > 0L
if (isQuoted) {
// Fetch quoted message body
}
return MessageChatItem(.....)
} ?: run {
return MessageChatItem(..........)
}
}
Since this is a chat, one field that i want to setup is a quoted message body. What i have as "input" is the messageId of the message that is being quoted. So that means that i have to make a query to my Room DB again inside the transform function.
The way i did it is this
val isQuoted = it.quotemsgid > 0L
var quotedBody = ""
if (isQuoted) {
// Fetch quoted message body
viewModelScope.launch(Dispatchers.IO) {
val quotedMessage = messagesRepository.getMessageByMessageId(it.quotemsgid)
withContext(Dispatchers.Main) {
quotedBody = quotedMessage.body
}
}
}
There no specific question but is there any better way to do something like this, meaning querying the DB inside a Transformation function. Is there a way that this would create a synchronize problem to my elements or something?

Getting data using Retrofit and inserting to SQLite Database

I have just started working with Kotlin. I have successfully got data from an API using Retrofit. I have to insert that data in SQLite. But I am not able to get a particular data from the response.
Here is my code:
apiInterface.enqueue( object : Callback<List<Movie>> {
override fun onResponse(call: Call<List<Movie>>?, response: Response<List<Movie>>?) {
if(response?.body() != null)
recyclerAdapter.setMovieListItems(response.body()!!)
response?.let {
for (i:Int in response.body()!!) {
recyclerAdapter.setMovieListItems(response.body()!!)
val myMovieList = response.body()
val myMovie = myMovieList!!.get(i)
var movie = MovieDatabase(myMovie.title, myMovie.image)
db.insertMovieData(movie)
}
}
}
override fun onFailure(call: Call<List<Movie>>?, t: Throwable?) {
}
})
}
Here is my insert method:
fun insertMovieData(movie: MovieDatabase) {
val db = this.writableDatabase
var cv = ContentValues()
cv.put(COL_FIRST_NAME, movie.name)
cv.put(COL_LAST_NAME, movie.image)
var result = db.insert(TABLE_NAME_MOVIE, null, cv)
if (result == -1.toLong())
Toast.makeText(context, "Failed", Toast.LENGTH_SHORT).show()
else
Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
}
If you've successfully, got back List<Movie> from the response body, just put the response into a List<Movie> myList.
List<Movie> myMovieList = response.body();
Then loop over it to get the values you need.
Movie myMovie = myList.get(0); //To get first Movie in list and so on
Then as per your Movie class use the getter methods to fetch further details of the movie; For example:
String imgURL = myMovie.getImage();
String movieName = myMovie.getTitle();
Build a SQLite DB from Room Persistence Library (it's simpler and easier than directly using SQLite database) and add the movie information there. Read - Save data in a local database using Room. Or, continue with your SQLite database, and call the respective insert method and query you've built with the data you got in imgURL and movieName.
You can also have a special method in your database handler class which could take entire myMovieList in one go and iterate over it inserting the values into database one by one.
My code examples are in Java, but, you should be able to write your Kotlin equivalent ones.
Here's the official documentation on Room Persistence Library.

Categories

Resources