custom OnConflictStrategy using Room DB - android

I'd like to set my own conflict columns not default primary key (just an auto increment integer in my case). So my table is:
#Entity(tableName = "users_tab")
data class User(
#PrimaryKey(autoGenerate = true)
var id: Int,
var str1: String,
var str2: String,
var str3: String
)
#Dao
interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(users: List<User>)
}
I'd like to REPLACE when User has same str1 and str2 how should I modify my Insert adnotation?

Related

Not sure how to convert a Cursor to this method's return type in Room Android?

I am getting the following error when I build my application
Not sure how to convert a Cursor to this method's return type (kotlinx.coroutines.flow.Flow<? extends java.util.List<com.notification.NotificationEntity>>)
This is my Entity class
#Entity
internal data class NotificationEntity(
#PrimaryKey #ColumnInfo(name = "notification_id") val notificationId: String,
val title: String,
val body: String?,
#ColumnInfo(name = "is_actionable") val isActionable: Boolean,
#ColumnInfo(name = "created_at") val createdAt: Instant,
#ColumnInfo(name = "is_read") val isRead: Boolean = false)
And this is my Dao Class
#Dao
internal interface NotificationDao {
#Query("SELECT * FROM NotificationEntity ORDER BY created_at ASC")
suspend fun getAllNotifications(): Flow<List<NotificationEntity>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveNotification(notifications: List<NotificationEntity>)
}
Can someone help what is the issue here?
When you declare Dao's function which returns Flow the function should not be suspendable. Please see docs.
Change your code to:
#Dao
internal interface NotificationDao {
#Query("SELECT * FROM NotificationEntity ORDER BY created_at ASC")
fun getAllNotifications(): Flow<List<NotificationEntity>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveNotification(notifications: List<NotificationEntity>)
}
And add type converter for Instant type which used in NotificationEntity if not yet added.

How to insert entities with a one to many relationship in Room

I am building a database using Room and I can't figure out how to insert the new elements that have a relationship (one to many in my case) into the database. No solution has ever talked about the insertion (they only talk about querying the data).
Here is the DAO:
#Dao
abstract class ShoppingListsDao {
#Insert
abstract suspend fun addNewShoppingList(newShoppingList: ShoppingList)
#Insert
abstract suspend fun addNewItem(newItem: Item)
// This is how I thought it would work but it didn't
#Insert
#Transaction
abstract suspend fun addNewShoppingListWithItems(newShoppingListWithItems: ShoppingListWithItems)
}
Here are my entities:
#Entity
class ShoppingList(
#PrimaryKey(autoGenerate = true)
val listID: Int,
val ListName: String
)
#Entity(foreignKeys = [ForeignKey(
entity = ShoppingList::class,
parentColumns = ["listID"],
childColumns = ["parentListID"]
)])
class Item(
#PrimaryKey(autoGenerate = true)
var itemID: Int,
val name: String,
val quantity: Int,
val parentListID: Int
)
There isn't a way that I am aware of that lets you directly insert a compound entity (like ShoppingListWithItems). You have to just insert the individual entities to their tables.
In your example, you would want to define an insert method for your ShoppingList entity which returns the generated primary key (so you can use it for your other items) and an insert method for your Item entities which can insert a whole list of them.
#Insert
suspend fun addNewShoppingList(newShoppingList: ShoppingList): Long
#Insert
suspend fun addNewItems(newItems: List<Item>)
Then you can run a transaction to insert them in a batch.
#Transaction
suspend fun addNewShoppingListWithItems(shoppingList: ShoppingList, items: List<Item>) {
val listId = addNewShoppingList(shoppingList)
items.forEach { it.parentListId = listId }
addNewItems(items)
}
Here's a good resource to understand one-to-many relationships-> https://developer.android.com/training/data-storage/room/relationships#one-to-many
You can create an embedded object for 'ShoppingListWithItems' as (more on embedded objects - https://developer.android.com/training/data-storage/room/relationships#nested-objects):
data class ShoppingListWithItems(
#Embedded val shoppingList: ShoppingList,
#Relation(parentColumn = "listID", entityColumn = "parentListID") val itemList: List<Item>
)
To store them in the database, you can simply use a transaction:
#Transaction
suspend fun createTransaction(shoppingList: ShoppingList, itemList: List<Item>) {
addNewShoppingList(shoppingList)
addNewItem(*itemList) // or create an alternate to accept the list itself.
}
To retrieve the 'ShoppingListWithItems' instance:
#Query("SELECT * from ShoppingList where listID =:id")
suspend fun getShoppingListWithItemsById(id: String): List<ShoppingListWithItems>

Android Room: Resolve foreign key in query

Given the following data model (i.e. one customer can have many orders and one order can have many shipments), how do I get a list of all orders with their associated customer and shipments for a certain order date?
In Kotlin, I'd like to retrieve a list of type List<OrderWithCustomerAndShipments> with OrderWithCustomerAndShipments being a POJO like this:
data class OrderWithCustomerAndShipments(
val order: Order,
val category: Category,
val shipments: List<Shipment>
)
Approach
So far, I've got a method that returns a List<OrderWithShipments>.
Note: For brevity, I omit #TypeConverter
, #ForeignKey, #ColumnInfo, etc.
#Dao
interface Dao {
#Transaction
#Query("SELECT * FROM orders WHERE orderDate = :date")
fun getOrdersWithShipments(date: Date): LiveData<List<OrderWithShipments>>
}
data class OrderWithShipments(
#Embedded
val order: Order
#Relation(parentColumn = "id", entityColumn = "orderId")
val shipments = List<Shipment>
)
#Entity
data class Customer(
#PrimaryKey val id: Int,
val customerName: String
)
#Entity
data class Order(
#PrimaryKey val id: Int,
val customerId: Int,
val orderDate: Date,
)
#Entity
data class Order(
#PrimaryKey val id: Int,
val orderId: Int,
val shipmentDate: Date,
)
One would assume that it is easier to resolve an order's foreign key to the parent customer than to get all child shipments. However, my attempts to get the parent customer haven't been successful so far.
Have you tried approach below? You could use several #Relation in Room
#Dao
interface Dao {
#Transaction
#Query("SELECT * FROM orders WHERE orderDate = :date")
fun getOrdersWithCustomerAndShipments(date: Date): LiveData<List<OrderWithCustomerAndShipments>>
}
data class OrderWithCustomerAndShipments(
#Embedded
val order: Order
#Relation(parentColumn = "customerId", entityColumn = "id")
val customer: Customer
#Relation(parentColumn = "id", entityColumn = "orderId")
val shipments: List<Shipment>
)

DAO: How to use the return value from Insert

According to the documentation an #Insert function can return a long, which is the new rowId for the inserted item. How can I use the return value?
#Dao
interface TodoDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(todo: TodoItem): Long
}
Just to note the id for my #Entity is autogenerated.
#PrimaryKey(autoGenerate = true)
Here is the whole TodoItem entity.
#Entity(tableName = "todos")
#Parcelize
data class TodoItem(val text: String, val priority: Priority) : Parcelable {
#PrimaryKey(autoGenerate = true)
var todoId: Long = 0
}
If id onTodoItemis avar, you could assign the return value toid`, so now your entity has its generated primary key, for use with future DAO operations.
If you are going to use #Parcelize, ensure that all essential properties are in the data class constructor. As it stands, your todoId property would not get put into the Parcel.
#Entity(tableName = "todos")
#Parcelize
data class TodoItem(
val text: String,
val priority: Priority,
#PrimaryKey(autoGenerate = true) var todoId: Long = 0
) : Parcelable
Then, given an entity named entity and a DAO named dao:
entity.todoId = dao.insert(entity)

How to store Strings that can be queried in Room?

I want to store Strings (uids) in my Room DB. I want these uids to be able to be:
Queried (check to see if a uid exists in my list of strings)
Added to (add a uid to the list of strings)
What is the most efficient way to achieve this?
Here is my skeleton to give you some context, though my understanding of Room is very basic so there will be mistakes:
#Entity(tableName = "user_data")
data class UserData(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "matched_users") var matchedUsers: Set<String>
)
#Dao
interface UserDataDao {
// Check to see if the uid is in matchedUsers Set/List
#Query("SELECT * FROM user_data WHERE :matchId IN matched_users")
fun matchedBefore(matchId: String): Boolean
// Add uid to Set/List
#Insert(onConflict = OnConflictStrategy.ABORT)
fun addMatchUid(uid: String)
}
Any suggestions appreciated.
According to your context, it seems you want to build one-to-many relationship, to do so you can follow the bellow codes. If not let me know what exactly you want.
#Entity(tableName = "user_data")
data class UserData(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "name") var name: String
)
#Entity(tableName = "user_matcher")
data class UserMatcher(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "userId") var userId: Int
)
#Dao
interface UserMatcherDao {
// Check to see if the uid is in matchedUsers Set/List
#Query("SELECT m.id as mid, u.id as uid, u.name FROM user_matcher m INNER JOIN user_data u ON m.userId = u.id WHERE m.id = :matchId")
fun getMatchedUsers(matchId: Int): List<UserData>
}

Categories

Resources