Given that I have 3 entities, Order contains list of LineItem, each LineItem will associates with one Product by productId.
The problem that when I get data from OrderDao, it returns null for the product field, but in the lineItem field, it has data. While I can data with ProductWithLineItem.
Already tried a lot of work arounds but it does not work.
Here is my code for entities and dao
Entities
#Entity(tableName = DataConstant.ORDER_TABLE)
data class Order(
#PrimaryKey
#ColumnInfo(name = "orderId")
val id: String,
#ColumnInfo(name = "status")
var status: String
)
#Entity(tableName = DataConstant.LINE_ITEM_TABLE)
data class LineItem(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "lineItemId")
val id: Long,
#ColumnInfo(name = "productId")
val productId: String,
#ColumnInfo(name = "orderId")
val orderId: String,
#ColumnInfo(name = "quantity")
var quantity: Int,
#ColumnInfo(name = "subtotal")
var subtotal: Double
)
#Entity(tableName = DataConstant.PRODUCT_TABLE)
data class Product(
#PrimaryKey
#NonNull
#ColumnInfo(name = "productId")
val id: String,
#ColumnInfo(name = "name")
var name: String?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "price")
var price: Double?,
#ColumnInfo(name = "image")
var image: String?,
)
Relations POJOs
data class ProductAndLineItem(
#Embedded val lineItem: LineItem?,
#Relation(
parentColumn = "productId",
entityColumn = "productId"
)
val product: Product?
)
data class OrderWithLineItems(
#Embedded var order: Order,
#Relation(
parentColumn = "orderId",
entityColumn = "orderId",
entity = LineItem::class
)
val lineItemList: List<ProductAndLineItem>
)
Dao
#Dao
interface OrderDao {
#Transaction
#Query("SELECT * FROM `${DataConstant.ORDER_TABLE}` WHERE orderId = :id")
fun getById(id: String): Flow<OrderWithLineItems>
}
Result after running with Dao
Result after running query
Here is my code for entities and dao
You code appears to be fine, with the exception of returning a Flow, testing, using your code, but on the main thread using List (and no WHERE clause) i.e the Dao being :-
#Query("SELECT * FROM ${DataConstant.ORDER_TABLE}")
#Transaction
abstract fun getOrderWithLineItemsAndWithProduct(): List<OrderWithLineItems>
Results in :-
The data being loaded/tested using :-
db = TheDatabase.getInstance(this)
orderDao = db.getOrderDao()
orderDao.clearAll()
orderDao.insert(Product("product1","P1","desc1",10.01,"image1"))
orderDao.insert(Product("product2","P2","desc2",10.02,"image2"))
orderDao.insert(Product("product3","P3","desc3",10.03,"image3"))
orderDao.insert(Product("product4","P4","desc4",10.04,"image4"))
orderDao.insert(Product("","","",0.0,""))
val o1 = orderDao.insert(Order("Order1","initiaited"))
val o2 = orderDao.insert(Order("Order2","finalised")) // Empty aka no List Items
val o1l1 = orderDao.insert(LineItem(10,"product3","Order1",1,10.01))
val o1l2 = orderDao.insert(LineItem(20,"product4","Order1",2,20.08))
val o1l3 = orderDao.insert(LineItem(30,"","Order1",3,30.09))
val o1l4 = orderDao.insert(LineItem(40,"","x",1,10.01))
//val o1l3 = orderDao.insert(LineItem(30,"no such product id","Order1",10,0.0))
// exception whilst trying to extract if not commented out at test = ....
val TAG = "ORDERINFO"
val test = orderDao.getOrderWithLineItemsAndWithProduct()
for(owl: OrderWithLineItems in orderDao.getOrderWithLineItemsAndWithProduct()) {
Log.d(TAG,"Order is ${owl.order.id} status is ${owl.order.status}")
for(pal: ProductAndLineItem in owl.lineItemList) {
Log.d(TAG,"\tLine Item is ${pal.lineItem.id} " +
"for Order ${pal.lineItem.orderId} " +
"for ProductID ${pal.lineItem.productId} " +
"Quantity=${pal.lineItem.quantity} " +
"Product description is ${pal.product.description} Product Image is ${pal.product.image} Price is ${pal.product.price}")
}
}
As such I believe the issue might be that for some reason the Flow is detecting when the first query has completed but prior to the underlying queries.
That is when using #Relation the core objects (Order's) are extracted via the query and the core objects created then the related objects are extracted by a another query and used to build ALL the related objects as a List (unless just the one when it doesn't have to be a list). So prior to this underlying query the core object will have a null or an empty list for the underlying objects. Of course with a hierarchy of #Relations then this is replicated along/down the hierarchy.
I would suggest temporarily adding .allowMainThreadQueires to the databaseBuilder and using a List<OrderWithLineItems> or just a sole OrderWithLineItems. If using this then you get the Product(s) then the issue is with the Flow (which is what I suspect).
I have a list of message objects that are being displayed in a recycler view as normal
#Entity(tableName = "messages")
data class Message(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "message_id")
var messageId: String,
#ColumnInfo(name = "body", defaultValue = "")
var body: String,
#ColumnInfo(name = "msg_type")
var msgType: Int = 0,
#ColumnInfo(name = "message_date", defaultValue = "")
var messageDate: String
)
How ever i want to implement a feature like whatsapp, that by scrolling between messages you can see a message date view like "02 May" that appears only if two messages have messageDate a different day. Should i use a customItemDecoration? But again i can't
How can i implement that?
Apologies, I am fairly new to Kotlin!
What I have:
#entity(tableName = "main_table")
data class Table(
#PrimaryKey(autoGenerate = true) var tableId: Int,
#ColumnInfo(name = "column_name") val columnName: String?,
...
)
What I would like:
val columnList = listOf(...list of strings...)
#entity(tableName = "main_table")
data class Table(
#PrimaryKey(autoGenerate = true) var tableId: Int,
for (column in columnList)
#ColumnInfo(name = column+"_name") val column+Name: String?,
...
)
Motivation: that list will be re-used in other parts of the code, as such, it would be great if it only existed within the code once.
Two unknowns here for me, can that loop be done somehow? And can concatenation be done during the variable declaration?
Thank you for reading!
I am having a relational data structure like the following
Entry.kt
#Entity(tableName = "entries")
data class Entry(
#ColumnInfo(name = "entry_id") #PrimaryKey(autoGenerate = true) val entryID: Long,
#ColumnInfo(name = "time_stamp") val timeStamp: Timestamp
)
Feeling.kt
#Entity(tableName = "feelings")
data class Feeling(
#ColumnInfo(name = "feeling_id") #PrimaryKey(autoGenerate = true) val feelingID: Int,
val feeling: String,
val color: Int
)
Subentry.kt
#Entity(tableName = "sub_entries",indices = [Index("feeling_id")])
data class SubEntry(
#ColumnInfo(name = "sub_entry_id") #PrimaryKey(autoGenerate = true) val subEntryID: Long,
#ColumnInfo(name = "entry_id") var entryID: Long,
#ColumnInfo(name = "feeling_id") val feelingID: Int,
val intensity: Int
)
as well as a DOJO linking the three
EntryWithSubEntriesAndFeelings.kt
data class EntryWithSubEntriesAndFeelings(
#Embedded val entry: Entry,
#Relation(
parentColumn = "entry_id",
entityColumn = "entry_id"
)
val subEntries: List<SubEntry>,
#Relation(
parentColumn = "entry_id",
entityColumn = "feeling_id",
associateBy = Junction(SubEntry::class)
)
val feelings: List<Feeling>
)
Now with a prepopulated feelings database, everything works fine. But I am currently adding the feature to insert custom feelings. Those and the corresponding subentries end up correctly in the subentry database (I copied the database files from the virtual device and checked them manually). But for some reason, the new feeling is not getting passed to the observer and the adapter and thus it is not being displayed together with the other subentries.
Any hints are appreciated! :)
Turns out I still had some hardcoded references to the initial list in my code which prevented the new feeling from being displayed.
I am trying to create a room database and I want each item inserted into it to have its own unique id without me having to provide it, The problem is when I try to insert new items into the database I get an error asking me to provide an id.
Here is my entity:
#Entity(tableName = "notes_table")
data class Note(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
#ColumnInfo(name = "description")
val description: String,
#ColumnInfo(name = "priority")
var priority: Int)
Is there a way to have the database create its own auto-generated auto-increasing id column without having me having to add it like this:
val item = Note(id, item, priority)
insert(item)
And instead do this:
val item = Note(item, priority)
insert(item)
Create a constructor that takes item and priority as arguments
#Entity(tableName = "notes_table")
data class Note (var item: String,
#ColumnInfo(name = "priority")
var priority: String) {
#PrimaryKey(autoGenerate = true)
var id: Long = 0,
//.....
}
You can just simply give the id a default value and put that at the end:
#Entity(tableName = "notes_table")
data class Note(
#ColumnInfo(name = "description")
val description: String,
#ColumnInfo(name = "priority")
var priority: Int)
#PrimaryKey(autoGenerate = true) //must be at the end
val id: Int = 0 //Long type recommend
)
Then you can:
val item = Note(item, priority)
insert(item)
Because your data class Note has three parameter.
So you you have to create Note by passing three parameter.
It is nothing to do with autogenerate or room.