How to upload an array to the mysql database in kotlin - android

How to make an INSERT into a MySQL database in Kotlin(android) using the library "mysql:mysql-connector-java:5.1.25"
At the moment, I was able to organize only this type of requests, but they are processed by the application for a very long time
File with data about the structure of the array and its declaration:
data class StudentsData(
val id: Int,
val group: String,
val fullName: String,
)
val studData: MutableList<StudentsData> = mutableListOf()
My query:
for (index in studData.indices) {
val value = studData[index]
thread {
try {
Class.forName("com.mysql.jdbc.Driver")
val connection = DriverManager.getConnection(url, user, pass)
val insert ="INSERT INTO students (id, uid, studGroup, fullName) VALUES " +
"(NULL, '${uid}','${value.group}','${value.fullName}');"
val queryInsertAccount = connection.prepareStatement(insert)
queryInsertAccount.execute()
}
catch (e: Exception) {
Log.e("error", "${e.message}")
}
}.join()
}

Related

Extract Data from firebase

Unable to extract information from the datasnapshot received from firebase.
Currently, I am able to get the dataSnapshot from firebase, but I am having problems extracting the information from it.
In the example below I have a lobby with the code "81MUB" and inside I have a list of players (only using one player in the example). Data from FireBase
{
"81MUB": [
{
"name": "Alejandro",
"points": 0
}
]
}
Data Class
data class Player(
val name: String,
val points: Int
)
Listener
fun getCode(): String {
val index = ('A'..'Z') + ('1'..'9')
var code = ""
for (i in 0..4){
code += index[Random().nextInt(index.size)]
}
return code
}
class MviewModel : ViewModel() {
private val _Players: MutableLiveData<MutableList<Player>> =
MutableLiveData(mutableListOf<Player>(Player("Alejandro", 0)))
private var _LobbyCode: String = ""
private val dataBase = FirebaseDatabase.getInstance()
fun getPlayer(): MutableLiveData<MutableList<Player>> = _Players
fun createLobby() {
_LobbyCode = getCode()
}
fun listener() {
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Post failed, log a message
}
}
dataBase.reference.child(_LobbyCode).addValueEventListener(postListener)
}
}
Any tips?
Each time you call getCode() you are generating a new random code. When reading data, you always use the exact same code that exists in the database. So in code, it should look like this:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val name = snapshot.child("name").getValue(String::class.java)
val points = snapshot.child("points").getValue(Long::class.java)
Log.d("TAG", "$name/$points")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
The result in the logcat will be:
Alejandro/0
If you however want to map the 81MUB node into an object of type Player, then your data class should look like this:
data class Player(
val name: String? = null,
val points: Int? = null
)
And in code:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val player = snapshot.getValue(Player::class.java)
Log.d("TAG", "${player.name}/${player.points}")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
Which will produce the exact same output as above.
You might also take into consideration, using the DatabaseReference#push() method which:
Create a reference to an auto-generated child location. The child key is generated client-side and incorporates an estimate of the server's time for sorting purposes.
Instead of using your codes.

Values are not added in an array instead it keeps updating value at 0th index in Firebase Firestore

On clicking like button , it is not either increasing like count in UI nor adding a userid in likes array. It is not adding a userid into an array instead it is updating value at 0th index. I am attaching photos of logic to add values in array in firestore.
I have also added the project on github. Please take a look at it for more clearification.
https://github.com/Anshi10/Social-Media-App
Post data class which has array of user ids which liked a post.
data class Post(
val text : String = "" ,
val createdBy : user = user() ,
val createdAt : Long = 0L ,
val Likes : ArrayList<String> = ArrayList()
)
Post Dao for adding post into firestore and updating likes
class PostDao {
val db = FirebaseFirestore.getInstance()
val PostCollection = db.collection("Post")
val auth = Firebase.auth
fun addPost(text : String){
//!! is to ensure that post is created only when user is logged in otherwise it will count as illegal exception
val currentUserId = auth.currentUser!!.uid
val userdao = userDao()
GlobalScope.launch{
//getuserbyid return task which will turn into object of user data class
val Postuser = userdao.getuserbyId(currentUserId).await().toObject(user::class.java)!!
//this will give the time when the post is created
val currentTime = System.currentTimeMillis()
val post = Post(text,Postuser,currentTime)
PostCollection.document().set(post)
}
}
fun getPostById(postid : String) : Task<DocumentSnapshot>{
return PostCollection.document(postid).get()
}
fun updateLikes(postid: String) {
GlobalScope.launch {
val currentUserid = auth.currentUser!!.uid
val post = getPostById(postid).await().toObject(Post::class.java)!!
val isliked = post.Likes.contains(currentUserid)
if (isliked) {
post.Likes.remove(currentUserid)
} else {
post.Likes.add(currentUserid)
}
PostCollection.document(postid).set(post)
}
Log.d("msg","updateLikes called")
}
}
onBindViewHolder function
override fun onBindViewHolder(holder: PostViewHolder, position: Int, model: Post) {
holder.userName.text = model.createdBy.name
holder.userText.text = model.text
//with(context) load(url) into(view)
Glide.with(holder.userImage.context).load(model.createdBy.imageUrl).circleCrop().into(holder.userImage)
holder.Likecount.text = model.Likes.size.toString()
holder.userTime.text = Utils.getTimeAgo(model.createdAt)
val auth = FirebaseAuth.getInstance()
val currentuserId = auth.currentUser!!.uid
val isliked = model.Likes.contains(currentuserId)
if(isliked){
holder.likeButton.setImageDrawable(ContextCompat.getDrawable(holder.likeButton.context,R.drawable.ic_baseline_favorite_24))
}
else{
holder.likeButton.setImageDrawable(ContextCompat.getDrawable(holder.likeButton.context,R.drawable.unliked))
}
}
}
Firestore structure
first collection named post which contains field createdAt , createdBy,likesCount,text of the post.
second collection named users which contains field id , imageUrl , name

how to get position of deleted Firestore Data that have been deleted from console (outside source) to update Or notify the adapter

I have this app that someone can delete his document at any time so if he deletes the document I want every other user to get updated the document has been removed and remove the document from the listView, it's more like a Food Ordering app so the user order could be taking by another Driver so the document will no longer be available for other users, I want to update the recyclerView Whenever a user deletes a document, so how to get the position without clicking how to detect that change to get the position, Sorry if I couldn't explain much I'm Using Groupie Adapter
val ref = firestore.collection("orders").whereNotEqualTo("userUid", uid)
val adapter = GroupAdapter<GroupieViewHolder>()
ref.addSnapshotListener { value, error ->
if (error != null){
return#addSnapshotListener
}
value?.documentChanges?.forEach {
if (it.type == DocumentChange.Type.ADDED) {
val userUid = it.document.data.get("userUid") as String
val userPhone = it.document.data.get("phone") as String
val userName = it.document.data.get("name") as String
val time = it.document.data.get("time") as String
val marketName = it.document.data.get("marketName") as String
val amount = it.document.data.get("amount") as String
val storeimgUrl = it.document.data.get("storeImg") as String
val order = it.document.data.get("order") as String
val userImg = it.document.data.get("userImg") as String
adapter.add(DriverOrders(userUid, userPhone, userName, time, marketName, amount, storeimgUrl, order, userImg))
adapter.notifyDataSetChanged()
}
if(it.type == DocumentChange.Type.REMOVED){
// here Wher I'm trying to get the position of deleted Or removed data
if (it.document.id == DriverOrders.docId){
adapter.notifyItemRemoved(it.oldIndex)
}
}
}
recyclerview_driverorders.adapter = adapter
}
class DriverOrders(val userUid: String, val userPhone: String,
val userName: String, val time: String,
val marketName: String, val amount: String, val storeimgUrl: String, val order: String,
val userImg: String):Item<GroupieViewHolder>() {
override fun getLayout(): Int {
return R.layout.driver_row
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.itemView.driverorders_sName.text = marketName
viewHolder.itemView.driverorders_order.text = order
viewHolder.itemView.driverorders_time.text = Date
Picasso.get().load(storeimgUrl).fit().into(viewHolder.itemView.driverorders_sImg)
}
}

Android Room Type Converter for Cursor -> List

I'm trying to write a type converter for a List<Pair<Uri,Uri?>>:
Query("SELECT $uri, $imageUrl FROM $data WHERE $color IS NULL")
abstract fun findWithMissingColor(): List<Pair<Uri, Uri?>>
#TypeConverter
fun toUriPair(cursor: Cursor?): List<Pair<Uri, Uri?>> {
return cursor?.let{ generateSequence { if (it.moveToNext()) it else null }.map {
Pair(
Uri.parse(it.getString(0)),
try { Uri.parse(it.getString(1)) } catch (e: Exception) {null}
)
}.toList() } ?: emptyList()
}
which is loosely based on information from here.
But Room doesn't recognize this as a valid type converter for this query:
error: Not sure how to convert a Cursor to this method's return type (java.util.List<kotlin.Pair<android.net.Uri,android.net.Uri>>).
public abstract java.util.List<kotlin.Pair<android.net.Uri, android.net.Uri>> findWithMissingColor();
The core principal of the answer you used as the basis is that if more than 1 column is extracted that the column names match (duplicate column names are ambiguous):-
Room will automatically bind the parameters of the method into the bind arguments. This is done by matching the name of the parameters to the name of the bind arguments.
Query
Additionally a TypeConverter can only convert a single value :-
Each converter method should receive 1 parameter and have non-void return type
TypeConverter
EXAMPLE
Without going into the possibility that you may have serialised URI's and URL's, here's an example that may assist with you extracting the data, assuming that the URI's and URL's are plain Strings. i.e. no need for TypeConverters. Would obviously be different if using serialised URI's and URL's and BLOBS. Then you need a TypeConverters for each
The example is based upon what has been included in your question but as explained may well differ.
MyDataPair (an inner class of MyDataEntity) is the equivalent of the tuple/Pair with member names of first and second these being important as the column names when extract have to match these names (first point made above).
First the Entity which I've called MyDataEntity (this will probably differ but is basically the structure bar String V Blob) :-
#Entity(tableName = MyDataEntity.MYTABLE_TABLE_NAME)
class MyDataEntity{
/*
CONSTANTS for COLUMN AND TABLE NAMES
*/
companion object {
const val ID_COLUMN_NAME: String = "_id"
const val URI_COLUMN_NAME: String = "uri"
const val IMAGEURL_COLUMN_NAME: String = "urlimage"
const val COLOR_COLUMN_NAME: String = "color"
const val MYTABLE_TABLE_NAME: String = "mydata"
}
#PrimaryKey
#ColumnInfo(name = ID_COLUMN_NAME)
var id: Long? = null
#ColumnInfo(name = URI_COLUMN_NAME)
var uri: String? = null
#ColumnInfo(name = IMAGEURL_COLUMN_NAME)
var imageUrl: String? = null
#ColumnInfo(name = COLOR_COLUMN_NAME)
var color: Long? = null
constructor()
#Ignore
constructor(id: Long?,uri: String?,imageurl: String,color: Long?) {
this.id = id
this.uri = uri
this.imageUrl = imageurl
this.color = color
}
class MyDataPair(first: String?, second: String?) {
var first: String? = first
var second: String? = second
#Ignore
/* NOT USED/COMPLETE */
fun getFirstAsUri(): URI {
/* code here to build URI */
return URI(first)
}
#Ignore
/* NOT USED/COMPLETE */
fun getSecondAsUrl(): URL {
/* code here to build URL */
return URL(second)
}
}
}
The Dao MyDataDao (including your query AND specific output column names to suit the inner MyDataPair class (first and Second) in the MyDataEntity) :-
#Dao
interface MyDataDao {
/* For inserting test data */
#Insert
fun insertMyDataRow(myDataEntity: MyDataEntity): Long
/* Efectively your query BUT WITH specific output/result column names to suit the MyDataPair class */
#Query("SELECT ${MyDataEntity.URI_COLUMN_NAME} AS first,${MyDataEntity.IMAGEURL_COLUMN_NAME} AS second FROM ${MyDataEntity.MYTABLE_TABLE_NAME} WHERE ${MyDataEntity.COLOR_COLUMN_NAME} IS NULL")
fun findWithMissingColor(): List<MyDataEntity.MyDataPair>
/* Clear the table for renability */
#Query("DELETE FROM ${MyDataEntity.MYTABLE_TABLE_NAME}")
fun clearMyDataEntityTable(): Int
}
The Database MyDatabase
#Database(entities = [MyDataEntity::class],version = 1)
abstract class MyDatabase: RoomDatabase() {
abstract fun getMyDataDao(): MyDataDao
companion object {
const val DATABASE_NAME = "mydatabase"
}
}
Finally an Activity MainActivity putting it all together :-
class MainActivity : AppCompatActivity() {
lateinit var db: MyDatabase
lateinit var dao: MyDataDao
val TAG = "MYDBINFO"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,MyDatabase::class.java,MyDatabase.DATABASE_NAME)
.allowMainThreadQueries()
.build()
dao = db.getMyDataDao()
var deleted_rows = dao.clearMyDataEntityTable()
/* CLEAR ANY EXIST DATALOAD AND LOAD SOME DATA */
Log.d(TAG, "$deleted_rows were deleted from the table ${MyDataEntity.MYTABLE_TABLE_NAME}")
dao.insertMyDataRow(MyDataEntity(null,"uri1","imageurl1", 0x00ffffff))
dao.insertMyDataRow(MyDataEntity(null,"uri2","imageurl2",null))
dao.insertMyDataRow(MyDataEntity(null,"uri3","imageurl3",0xAAAAAA))
dao.insertMyDataRow(MyDataEntity(null,"uri4","imageurl4",null))
/* EXTRACT ROWS WHERE COLOR IS NULL */
var results = dao.findWithMissingColor()
/* WRITE SOME INFO ABOUT THE EXTRACTED DATA TO THE LOG */
for(mdp: MyDataEntity.MyDataPair in results) {
Log.d(TAG,"STRING_URI = ${mdp.first} STRING_URL = ${mdp.second}")
}
}
}
runs of the main thread for convenience and brevity
inserts 4 rows 2 with color as NULL, 2 with a color
RESULTS from a run
The database after running is (as per Database Inspector) :-
The Log includes :-
2021-04-12 12:29:18.085 D/MYDBINFO: 4 were deleted from the table mydata
2021-04-12 12:29:18.099 D/MYDBINFO: STRING_URI = uri2 STRING_URL = imageurl2
2021-04-12 12:29:18.099 D/MYDBINFO: STRING_URI = uri4 STRING_URL = imageurl4

Android Room inserting a list fails if any object throws a SQL exception

I have a application that inserts around 20,000 objects that I get from an api and I've found it to be quicker to do batch inserts by creating a list of objects then inserting the list.
The application periodically gets a updated list from the api that I then insert into the database.
I just noticed that if I have a list of... say 20 objects to insert, if even 1 object throws a SQLiteConstraintException(due to unique constraint) NONE of the 20 objects get inserted.
Is there a way to have room insert the other objects that don't throw a SQLiteConstraintException exception? Or should I just individually insert every object?
Here is some example code to demonstrate: Sample Project Here
Entity:
#Entity(
indices = [
Index(
value = ["first_name", "last_name"],
unique = true
)
]
)
data class User(
#ColumnInfo(name = "first_name") val firstName: String?,
#ColumnInfo(name = "last_name") val lastName: String?
) {
#PrimaryKey(autoGenerate = true)
var uid: Int = 0
}
Dao:
#Dao
interface UserDao {
#Query("SELECT * FROM user")
fun getAll(): List<User>
#Insert
fun insertUser(vararg users: User)
#Insert
fun insertUserList(users: List<User>)
}
Database:
#Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
)
.allowMainThreadQueries()
.build()
val userDao = db.userDao()
// Create 3 users and insert them 1 by 1
for (i in 0..2) {
val user = User("first$i", "last$i")
// First run won't throw any exceptions, but subsequent runs will. Need to catch them.
try {
userDao.insertUser(user)
} catch (e: SQLiteConstraintException) {
Log.e("Room", "onCreate: ", e)
}
}
// There are now 3 User's in the database
Log.d("Room", "### First 3 User's")
for (u in userDao.getAll()) {
Log.d("Room", u.toString())
}
// Create 10 users(some of which have the same first and last names)
// then do a batch insert
val userList = mutableListOf<User>()
for (i in 0..9) {
userList.add(
User("first$i", "last$i")
)
}
// Need to catch SQLiteConstraintException
try {
userDao.insertUserList(userList)
} catch (e: SQLiteConstraintException) {
Log.e("Room", "onCreate: ", e)
}
Log.d("Room", "### Next 10 User's")
// There are still 3 User's in the database
for (u in userDao.getAll()) {
Log.d("Room", u.toString())
}
}
}
You can add an OnConflictStrategy property to your #Insert annotation to say what you want to have happen when there is a foreign key constraint violation. In your case, IGNORE will skip over any failures:
#Insert(onConflict = OnConflictStrategy.IGNORE)
If you care about which ones failed (e.g., logging purposes), I think you can have your function return List<Int>. That list should match the length of the list of items you were inserting. The values will either be the ROWID of the inserted entry or -1 to indicate ignored inserts. From the index values of the -1 entries, you would find out which items themselves failed. I have not tried this, so YMMV.

Categories

Resources