NOTE: I cannot use relation as we had performance issue which is not reproduced on direct join query.
Until I added target user and from group user and corresponding
LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_target_user
LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_from_group_user
It worked well. But after addition, I can not figure out how to make those prefixes map in the query.
class ReadMessageEntity(
#Embedded
var message: MessageEntity,
#Embedded
var block: BlockEntity?,
#Embedded
var user: ChatUserRelationEntity,
#Embedded(prefix = "target_user_")
var targetUser: ChatUserEntity?,
#Embedded(prefix = "from_group_user_")
var fromGroupUser: ChatUserEntity?
)
This is which I'm trying to query:
#Transaction
#Query("""
SELECT * FROM message_item
LEFT JOIN block_item ON block_item.block_message_id = message_item.message_local_id
LEFT JOIN chat_user_relation ON chat_user_relation.chat_user_id = message_item.message_user_id
LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_target_user
LEFT JOIN chat_user ON chat_user.chat_user_id = message_item.messages_from_group_user
WHERE message_item.message_chat_id = :chatId
ORDER BY message_created_at ASC
""")
fun getMessagesByChat(chatId: String): Single<List<ReadMessageEntity>>
The error:
e: error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (ambiguous column name: main.chat_user.chat_user_id)
Here is my join solution:
ChatsEntity
#Entity(tableName = "Chats",foreignKeys = [ForeignKey(entity = UserEntity::class,
parentColumns = ["id"], childColumns = ["userId"], onDelete = NO_ACTION),ForeignKey(entity = LastMessageEntity::class,
parentColumns = ["id"], childColumns = ["roomId"], onDelete = NO_ACTION)])
data class ChatsEntity(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
#ColumnInfo(name = "roomId") var roomId: String,
#ColumnInfo(name = "userId") var userId: String,
#ColumnInfo(name = "count") var count: Int
)
LastMessageEntity
#Entity(tableName = "LastMessages")
data class LastMessageEntity(
#PrimaryKey #ColumnInfo(name = "id") var id: String = "",
#ColumnInfo(name = "message") var message: String = "",
#ColumnInfo(name = "type") var type: String = ""
)
UserEntity
#Entity(tableName = "Users")
data class UserEntity(
#PrimaryKey #ColumnInfo(name = "id") var id: String = "",
#ColumnInfo(name = "username") var username: String = "",
#ColumnInfo(name = "token") var token: String = ""
)
1. using relation
class ChatUserMessage {
#Embedded
var chat : ChatsEntity? = null
#Relation(parentColumn = "userId", entityColumn = "id")
var user : UserEntity? = null
#Relation(parentColumn = "roomId", entityColumn = "id")
var lastMessage : LastMessageEntity? = null
}
SQL Query
#Query("SELECT * FROM Chats")
fun getAllChats(): List<ChatUserMessage?>?
2. without using relation
class ChatUserMessage
{
#Embedded
var chat: ChatsEntity? = null
#Embedded(prefix = "user_")
var user: UserEntity? = null
#Embedded(prefix = "message_")
var lastMessage: LastMessageEntity? = null
}
Query
#Query("SELECT Chats.*, LastMessages.id as message_id,LastMessages.message as message_message, LastMessages.type as message_type, Users.id as user_id, Users.username as user_username, Users.token as user_token FROM Chats INNER JOIN LastMessages ON LastMessages.id = Chats.roomId INNER JOIN Users ON Users.id = Chats.userId")
fun getAllChats(): List<ChatUserMessage?>?
In the query, you have to set an alias for the table chat_user as you are joining it twice in the same name which will confuse database engine so it tells you that the field is ambiguous.
EDIT:
I found something that may be similar to your issue: https://github.com/JetBrains/Exposed/issues/177
Also they referred to this code as an example to fix this issue:
object Users : Table() {
val id = varchar("id", 10).primaryKey()
val name = varchar("name", length = 50)
val residentialCityId = optReference("resid_city_id", Cities)
val bornCityId = optReference("born_city_id", Cities)
}
object Cities : IntIdTable() {
val name = varchar("name", 50) // Column
}
fun test() {
val userTable1 = Users.alias("u1")
val userTable2 = Users.alias("u2")
Cities
.innerJoin(userTable1, {Cities.id}, {userTable1[Users.residentialCityId]})
.innerJoin(userTable2, {Cities.id}, {userTable2[Users.bornCityId]})
.selectAll()
}
as mentioned Moayad .AlMoghrabi I should use table aliases. So I renamed LEFT JOIN chat_user to LEFT JOIN chat_user target_user. Note there is no AS between chat-user and target_user. (my mistake was that I tried to put it there). Behavior of prefixes is still a mystery to me as the prefix for chat_user works properly, but if I put the prefix to ChatUserRelationEntity then I receive unknown column error.
SELECT * FROM message_item
LEFT JOIN block_item
ON block_item.block_message_id = message_item.message_local_id
LEFT JOIN chat_user_relation
ON chat_user_relation.chat_user_relation_chat_user_id = message_item.message_user_id
AND chat_user_relation.chat_user_relation_chat_user_chat_id = :chatId
LEFT JOIN chat_user target_user
ON target_user.chat_user_id = message_item.messages_target_user
LEFT JOIN chat_user from_group_user
ON from_group_user.chat_user_id = message_item.messages_from_group_user
WHERE message_item.message_chat_id = :chatId
ORDER BY message_created_at ASC
Related
I have a many to many relationship Room database with three tables:
First one :
data class Name(
#PrimaryKey(autoGenerate = true)
var nameId : Long = 0L,
#ColumnInfo(name = "name")
var name : String = "",
#ColumnInfo(name = "notes")
var notes: String=""
)
Second:
#Entity(tableName = "tags_table")
data class Tag(
#PrimaryKey(autoGenerate = true)
var tagId : Long = 0L,
#ColumnInfo(name = "tag_name")
var tagName : String = ""
)
Third:
#Entity(
tableName = "tagInName_table",
primaryKeys = ["nameId", "tagId"],
foreignKeys = [
ForeignKey(
entity = Name::class,
parentColumns = ["nameId"],
childColumns = ["nameId"]
),
ForeignKey(
entity = Tag::class,
parentColumns = ["tagId"],
childColumns = ["tagId"]
)
]
)
data class TagInName(
#ColumnInfo(name = "nameId")
var nameId: Long = 0L,
#ColumnInfo(name = "tagId")
var tagId: Long = 0L
)
The data class I use for a return object in a Query:
data class NameWithTags(
#Embedded
val name: Name,
#Relation(
parentColumn = "nameId",
entityColumn = "tagId",
associateBy = Junction(value = TagInName::class)
)
val listOfTag : List<Tag>
)
This is how I query to get all NamesWithTags:
#Query("SELECT * FROM names_table")
#Transaction
fun getNamesWithTags() : LiveData<List<NameWithTags>>
So the thing I need to do is, I need to Query to return LiveData<List<NameWithTags>> where every NamesWithTags has a list which contains the Tag ID that I Query for.
From my interpretation of what you say you need to do, then :-
#Transaction
#Query("SELECT names_table.* FROM names_table JOIN tagInName_table ON names_table.nameId = tagInName_table.nameId JOIN tags_table ON tagInName_table.tagId = tags_table.tagId WHERE tags_table.tagId=:tagId ")
fun getNameWithTagsByTagId(tagId: Long): LiveData<List<NamesWithTags>>
Note the above is in-principle code and has not been compiled or tested, so it may contain some errors.
A NameWithTags will contain ALL related tags whcih should be fine according to (where every NamesWithTags has a list which contains the Tag ID ), if you wanted just certain Tags in the List of Tags then it's a little more complex, this is explained in a recent answer at Android Room query with condition for nested object
I looked in the android documentation for an answer to my question, but I couldn't find it. To create a recyclerview using the information contained in these classes, how can I get a list of this information in Room
#Entity(
foreignKeys = [
ForeignKey(
entity = City::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("cityfk"),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class Address(
#PrimaryKey
#ColumnInfo
var id: Long = 0
) : Serializable {
#ColumnInfo
var name: String = ""
#ColumnInfo(index = true)
var cityfk: Long = 0
}
#Entity(
foreignKeys = [
ForeignKey(
entity = State::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("statefk"),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class City(
#PrimaryKey
#ColumnInfo
var id: Long = 0
) : Serializable {
#ColumnInfo
var name: String = ""
#ColumnInfo(index = true)
var statefk: Long = 0
}
#Entity
data class State(
#PrimaryKey
#ColumnInfo
var id: Long = 0
) : Serializable {
#ColumnInfo
var name: String = ""
}
How can I get a list of addresses listing the classes?
How to get a result like this in ANSI SQL:
select ADDRESS.NAME ADDRESS
, CITY.NAME CITY
, STATE.NAME STATE
from ADDRESS
join CITY
on CITY.ID = ADDRES.CITYFK
join STATE
on STATE.ID = CITY.STATEFK
You would typically have a POJO to represent the combined data. You can then either have a field/variable for the extracted columns noting that values are matched to the liked named variable.
You can use #Embedded to include an entity in it's entirety so in theory embed Address City and State.
see variable/column name issues
You can use #Embedded along with #Relation for the child (children) BUT not for grandchildren (e.g. State). You would need an underlying City with State POJO where City is embedded and State is related by an #Relation.
variable/column names are not an issue when using #Relation as room builds underlying queries from the parent.
Variable/Column name issues
Room maps columns to variable according to variable names. So there will be issues with id's and name columns if using the simpler #Embedded for all three entities.
I would suggest always using unique names e.g. addressId, cityId, StateId, (at least for the column names e.g. #ColumnInfo(name = "addressId")) but simpler to just have var addressid.
An alternative is the use the #Embedded(prefix = "the_prefix") on some, this tells room to match the variable to column name with the prefix so you need to use AS in the SQL. Obviously the_prefix would be changed to suit.
The Dao's
if using #Embedded with #Relation then you simply need to get the parent so
#Query("SELECT * FROM address")
fun getAddressWithCityAndWithState(): List<AddressWithCityAndWithState>
where AddressWithCityAndWithState is the POJO that has the Address #Embedded and the CityWithState with #Relation.
You would also need the accompanying CityWithState POJO with City #Embedded and State with #Relation.
If Embedding Address, City and State with City having a prefix of "city_" and state having a prefix of "state_" then you would use something like :-
#Query("SELECT address.*, city.id AS city_id, city.name AS city_name, state.id AS state_id, state.name AS state_name FROM address JOIN city ON address.cityfk = city.it JOIN state ON city.statefk = state.id")
fun getAddressWithCityAndWithState(): List<AddressWithCityAndWithState>
where AddressWithCityAndWithState is the POJO that has Address, City and State #Embedded
Note the above is in-principle.
Working Example
The following is a working example based upon
a) renaming the columns to avoid ambiguity and
b) using #Embedded of all three classes in the POJO AddressWithCityWithState
First changes to the Address, City and State to rename the columns :-
Address :-
#Entity(
foreignKeys = [
ForeignKey(
entity = City::class,
parentColumns = arrayOf("city_id"), //<<<<<<<<<< CHANGED
childColumns = arrayOf("cityfk"),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class Address(
#PrimaryKey
#ColumnInfo(name ="address_id") //<<<<<<<<<< ADDED name
var id: Long = 0
) : Serializable {
#ColumnInfo(name = "address_name") //<<<<<<<<<< ADDDED name
var name: String = ""
#ColumnInfo(index = true)
var cityfk: Long = 0
}
City :-
#Entity(
foreignKeys = [
ForeignKey(
entity = State::class,
parentColumns = arrayOf("state_id"), //<<<<<<<<<< changed
childColumns = arrayOf("statefk"),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class City(
#PrimaryKey
#ColumnInfo(name = "city_id") // <<<<<<<<<< ADDED name
var id: Long = 0
) : Serializable {
#ColumnInfo(name = "city_name") //<<<<<<<<<< ADDED name
var name: String = ""
#ColumnInfo(index = true)
var statefk: Long = 0
}
State :-
#Entity
data class State(
#PrimaryKey
#ColumnInfo(name = "state_id") // ADDED name
var id: Long = 0
) : Serializable {
#ColumnInfo(name = "state_name") // ADDED name
var name: String = ""
}
Next the POJO AddressWithCityWithState :-
data class AddressWithCityWithState (
#Embedded
val address: Address,
#Embedded
val city: City,
#Embedded
val state: State
)
due to unique column names no prefix = ? required
A suitable DAO :-
#Query("SELECT * FROM address JOIN city on address.cityfk = city.city_id JOIN state ON city.statefk = state.state_id")
fun getAllAddressesWithCityAndWithState(): List<AddressWithCityWithState>
simplified due to column renaming so * instead AS clauses for ambiguous column names
Using the above :-
allDao = db.getAllDao()
var state = State()
state.name = "State1"
var stateid = allDao.insert(state)
var city = City()
city.name = "City1"
city.statefk = stateid
var cityid = allDao.insert(city)
var address = Address()
address.name = "Address1"
address.cityfk = cityid
allDao.insert(address)
for(awcws: AddressWithCityWithState in allDao.getAllAddressesWithCityAndWithState()) {
Log.d("DBINFO","${awcws.address.name}, ${awcws.city.name}, ${awcws.state.name}")
}
The result in the log being :-
2021-11-22 07:43:28.574 D/DBINFO: Address1, City1, State1
Other working examples (without changing column names)
Without any changes to the Entities (Address, city and state). Here are working examples of the other options.
1- Get full address as a single string, all that is required is the query such as :-
#Query("SELECT address.name||','||city.name||','||state.name AS fullAddress FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id ")
fun getAddressesAsStrings(): List<String>
of course not much use say for a drop down selector as you can't ascertain where in the database the rows came from.
2 - Basic POJO with unambiguous column names
The POJO :-
data class AddressWithCityWithState(
var address_id: Long,
var address_name: String,
var city_id: Long,
var city_name: String,
var state_id: Long,
var state_name: String
)
The query :-
/*
* Returns multiple columns renamed using AS clause to disambiguate
* requires POJO with matching column names
* */
#Query("SELECT " +
"address.id AS address_id, address.name AS address_name, " +
"city.id AS city_id, city.name AS city_name, " +
"state.id AS state_id, state.name AS state_name " +
"FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id")
fun getAddressesWithCityAndStateViaBasicPOJO(): List<AddressWithCityWithState>
3- POJO using EMBEDS
The POJO :-
data class AddressWithCityWithStateViaEmbeds(
#Embedded
var address: Address,
#Embedded(prefix = cityPrefix)
var city: City,
#Embedded(prefix = statePrefix)
var state: State
) {
companion object {
const val cityPrefix = "city_"
const val statePrefix = "state_"
}
}
The query :-
/*
* Returns multiple columns renamed according to the prefix=? coded in the
* #Embedded annotation
*
*/
#Query("SELECT address.*, " +
"city.id AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "id," +
"city.name AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "name," +
"city.statefk AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "statefk," +
"state.id AS " + AddressWithCityWithStateViaEmbeds.statePrefix + "id," +
"state.name AS " + AddressWithCityWithStateViaEmbeds.statePrefix + "name " +
"FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id")
fun getAddressesWithCityAndStateViaEmbedPOJO(): List<AddressWithCityWithStateViaEmbeds>
4- POJO's with parent EMBED and child RELATE
The POJO's :-
data class CityWithState(
#Embedded
var city: City,
#Relation(
entity = State::class,
parentColumn = "statefk",
entityColumn = "id"
)
var state: State
)
and :-
data class AddressWithCityWithStateViaRelations(
#Embedded
var address: Address,
#Relation(
entity = City::class, /* NOTE NOT CityWithState which isn't an Entity */
parentColumn = "cityfk",
entityColumn = "id"
)
var cityWithState: CityWithState
)
and the query :-
#Transaction
#Query("SELECT * FROM address")
fun getAddressesWithCityAndStateViaRelations(): List<AddressWithCityWithStateViaRelations>
note the use of #Tranaction so the underlying queries, built by Room, are all done within a single database transaction.
Putting the above into use
The following code in an activity uses all 4 to output the same results :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val TAG: String = "DBINFO"
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
var state = State(1)
state.name = "State1"
val state1Id = dao.insert(state)
state.id = 2
state.name = "State2"
val state2Id = dao.insert(state)
var city = City(10)
city.name = "City1"
city.statefk = state1Id
val city1Id = dao.insert(city)
city.id = 11
city.name = "City2"
city.statefk = state2Id
val city2Id = dao.insert(city)
city.id = 12
city.name = "City3"
city.statefk = state1Id
val city3Id = dao.insert(city)
var address = Address(100)
address.name = "Address1"
address.cityfk = city1Id
dao.insert(address)
address.id = address.id + 1
address.name = "Address2"
address.cityfk = city2Id
dao.insert(address)
address.id = address.id + 1
address.name = "Address3"
address.cityfk = city3Id
for (s: String in dao.getAddressesAsStrings()) {
Log.d(TAG + "STRG", s)
}
for (awcws: AddressWithCityWithState in dao.getAddressesWithCityAndStateViaBasicPOJO()) {
Log.d(TAG + "BASICPOJO", "${awcws.address_name}, ${awcws.city_name}, ${awcws.state_name}")
}
for (awcwsve: AddressWithCityWithStateViaEmbeds in dao.getAddressesWithCityAndStateViaEmbedPOJO()) {
Log.d(TAG + "EMBEDS","${awcwsve.address.name}, ${awcwsve.city.name}, ${awcwsve.state.name}")
}
for(awcwsvr: AddressWithCityWithStateViaRelations in dao.getAddressesWithCityAndStateViaRelations()) {
Log.d(TAG + "MIXED","${awcwsvr.address.name}, ${awcwsvr.cityWithState.city.name}, ${awcwsvr.cityWithState.state.name}")
}
}
}
The output to the log being :-
2021-11-22 12:33:54.322 D/DBINFOSTRG: Address1,City1,State1
2021-11-22 12:33:54.322 D/DBINFOSTRG: Address2,City2,State2
2021-11-22 12:33:54.324 D/DBINFOBASICPOJO: Address1, City1, State1
2021-11-22 12:33:54.324 D/DBINFOBASICPOJO: Address2, City2, State2
2021-11-22 12:33:54.326 D/DBINFOEMBEDS: Address1, City1, State1
2021-11-22 12:33:54.326 D/DBINFOEMBEDS: Address2, City2, State2
2021-11-22 12:33:54.332 D/DBINFOMIXED: Address1, City1, State1
2021-11-22 12:33:54.332 D/DBINFOMIXED: Address2, City2, State2
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 three tables and I have a many to many relationship here:
Contact table:
#Entity(tableName = "T_Contacts")
class Contact(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "_id") var id: Long,
#ColumnInfo(name = "phoneNumber")
var phoneNumber: String = "",
#ColumnInfo(name = "name")
var name: String = "",
#ColumnInfo(name = "active")
var active: Int = 1
#ColumnInfo(name = "contactId")
var contactId: Long = -1
#ColumnInfo(name = "phoneContactId")
var phoneContactId: String = ""
}
Email table:
#Entity(tableName = "T_EmailAddress")
class EmailAddress(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "_id")
var id : Long,
#ColumnInfo(name="emailAddress")
var emailAddress: String,
#ColumnInfo(name="active")
var active : Int) {
}
And the associative table:
#Entity(tableName = "T_EmailAddressContact",
foreignKeys = [ForeignKey(
entity = Contact::class,
parentColumns = arrayOf("contactId"),
childColumns = arrayOf("contactId"),
onDelete=ForeignKey.CASCADE),
ForeignKey(
entity = EmailAddress::class,
parentColumns = arrayOf("emailId"),
childColumns = arrayOf("emailId"),
onDelete=ForeignKey.CASCADE)]
)
class EmailAddressContactCrossRef(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id")
var id: Long,
#ColumnInfo(name="contactId")
var contactId: Long,
#ColumnInfo(name="emailId")
var emailId : Long,
#ColumnInfo(name="active")
var active : Int ) {
How can I get a list of all the email contacts using relations? I tried:
data class EmailAddressContact(
#Embedded val contact: EmailAddress,
#Relation(parentColumn = "contactId",
entityColumn = "contactId",
associateBy = Junction(EmailAddressContactCrossRef::class)
)
val listContacts: List<Contact>
)
but this only gets me a list of all contacts for a certain email address. I want something like this:
data class EmailAddressContact {
val id: Long,
val emailAddress: EmailAddress,
val contact: Contact,
val active: Boolean
}
by calling a query:
#Transaction
#Query("SELECT * FROM T_EmailAddressContact")
fun getAllEmailAddressAndContacts(): List<EmailAddressContact>
According to your EmailAddressContact structure it seems you don't need to use junction, just relations. Try this way:
data class EmailAddressContact(
#Embedded val emailAddressContactCrossRef: EmailAddressContactCrossRef, // <- there you'll get "id" and "active" fields
#Relation(
parentColumn = "contactId",
entityColumn = "contactId"
)
val contact: Contact,
#Relation(
parentColumn = "emailId",
entityColumn = "_id"
)
val emailAddress: EmailAddress
)
and query you mentioned in your question should fit
Given
#Entity(
tableName = "state",
foreignKeys = arrayOf(
ForeignKey(entity = Foo::class, parentColumns = arrayOf("id"), childColumns = arrayOf("foo_id")),
ForeignKey(entity = Bar::class, parentColumns = arrayOf("id"), childColumns = arrayOf("bar_id"))
)
)
data class State(
#PrimaryKey(autoGenerate = true) val id: Long = 1
) {
#ColumnInfo(name = "foo_id")
var fooId: Long? = null
#ColumnInfo(name = "bar_id")
var barId: Long? = null
}
#Entity(tableName = "foo")
open class Foo(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
open val id: Long,
#ColumnInfo(name = "foo")
val foo: String?,
)
#Entity(tableName = "bar")
open class Bar(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
open val id: Long,
#ColumnInfo(name = "bar")
val bar: String?,
)
I am trying to create a Join POJO to store the results of the query:
class FooBar(
#Embedded
val foo: Foo,
#Embedded
val bar: Bar
)
And my failed attempt at a query:
#Query("SELECT foo.*, bar.* FROM foo, bar JOIN state ON foo.id == state.foo_id JOIN bar ON bar.id == session.bar_id ")
fun getFooBar(): LiveData<FooBar>
However I get an error when compiling. Do I need to deconflict the id fields in foo and bar since they are named the same?
I have tried with a prefix, no luck:
class FooBar(
#Embedded(prefix = "foo_")
val foo: Foo,
#Embedded(prefix = "bar_")
val bar: Bar
)
Any ideas?
Here is an example that might help you.
#Query("SELECT RoomArticle.*, RoomBranch.id AS 'RoomBranch_id', RoomBranch.name AS 'RoomBranch_name' "
Data entity:
public class RoomArticleOfBranch {
#Embedded
public RoomArticle article;
#Embedded(prefix = "RoomBranch_")
public RoomBranch branch;
Notice I provide prefix both in query and in #Embedded.
As you're trying Embedded try to change the query as follows
#Dao
interface TestDao {
#Query("SELECT foo.id as foo_id,bar.id as bar_id,bar.bar as bar_bar FROM foo, bar JOIN state ON foo_id = state.foo_id and bar_id = state.bar_id")
fun getFooBar(): LiveData<FooBar>
}
There are two things need to be fixed.
You need to rename the column name during the select query.
fixing your joining query.