I have a query like this in my room Dao
#Query("SELECT * FROM my_data ORDER BY id ASC LIMIT 1")
suspend fun getFirstItem(): MyEntity?
and MyEntity is just a data class with an auto generated id.
#Entity(tableName = "my_data")
data class MyEntity(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
#ColumnInfo(name = "date_created")
var dateCreated: String? = null,
#ColumnInfo(name = "description")
var description: String? = null
)
When I run the query the entity object that is returned has always id=0.
How can I get the actual id of the row using a query in room?
EDIT: This is the function I'm calling the query
suspend fun getFirstItem(context: Context): MyEntity? {
val db = MyDatabase.getDb(context)
return db.MyDao().getFirstItem()
}
The returned MyEntity object is something like this.
MyEntity(id=0, dateCreated="2022-12-07 09:38:37", description="some description")
Where the id is always returned as 0, although it isn't actually 0
In My System, Your Query works Properly.
All Available Entries
Query Result
Do invalid catch/Restart and Try again
I have two related entities:
Station
#Entity(tableName = "stations")
data class Station(
#PrimaryKey
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "latitude")
val latitude: Double,
#ColumnInfo(name = "longitude")
val longitude: Double,
#ColumnInfo(name = "connectors")
val connectors: List<Connector>, // this field has a type converter
)
Connector
#Entity(
tableName = "connectors",
primaryKeys = ["station_id", "station_connector_id"]
)
class Connector(
#ColumnInfo(name = "station_connector_id")
val stationConnectorId: Int,
#ColumnInfo(name = "station_id")
val stationId: Long,
#ColumnInfo(name = "type")
val type: ConnectorType,
)
When I insert data, I fill up both entites and it seems kinda okay, but when I'm trying to receive stations with particular types of connectors it duplicates rows.
For example, I have an object
Station(
id = 100,
latitude = 56.565,
longitude = 34.565,
connectors = [
Connector(stationConnectorId=1, stationId=100, type=TYPE_2),
Connector(stationConnectorId=2, stationId=100, type=CHADEMO),
Connector(stationConnectorId=3, stationId=100, type=TYPE_1)
]
)
And if I want to filter stations only by one connector type I receive one row with this station(and it's right), but if I want to reset filters and look up for stations that can contain many connectors, I receive duplicates of this stations(in this example if I request for station with TYPE_1, TYPE_2 and CHADEMO connector types it will be three equal rows).
I'm using this query to request stations from my database:
SELECT * FROM simple_stations
INNER JOIN connectors ON simple_stations.id = connectors.station_id
WHERE connectors.type IN (:connectorTypesList)
I've tried to use DISTINCT in the query, ForeignKeys and Indexes in these Entities, but it was not working, so now I'm completely lost.
If you just want Stations then you have various options then your have various options.
is to use a GROUP BY clause such as GROUP BY simple_stations.id
However, the issue you may then encounter is that Station would be incomplete/unreliable as you have a List of Connectors and if you GROUP by Station then you will only get a single arbitrary Connector (there again that may depend upon you TypeConvertor).
to use DISTINCT you would have to only include the Station columns (similar problem as above).
I'd suggest that your schema is at fault by including the List of Connectors related to the Station you are duplicating data (aka it's not normalised).
rather if you removed
#ColumnInfo(name = "connectors")
val connectors: List<Connector>, // this field has a type converter
from the Station Entity the data itself would still be available for retrieval.
You may then wish to have a POJO that Embeds the Station and has a List of Connector's, perhaps one with and one without the Connector List have an #Relationship (with and you would get all connectors irrespective of the WHERE clause as that's how #Relationship works). Without and you could have the constructor get only the Connectors with the types you want.
Perhaps consider the following based upon your code:-
The Station Entity
#Entity(tableName = "stations")
data class Station(
#PrimaryKey
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "latitude")
val latitude: Double,
#ColumnInfo(name = "longitude")
val longitude: Double
/*
#ColumnInfo(name = "connectors")
val connectors: List<Connector> // this field has a type converter
NO NEED IMPLIED BY RELATIONSHIP
*/
)
The Connector Entity
#Entity(
tableName = "connectors",
primaryKeys = ["station_id", "station_connector_id"]
)
class Connector(
#ColumnInfo(name = "station_connector_id")
val stationConnectorId: Int,
#ColumnInfo(name = "station_id")
val stationId: Long,
#ColumnInfo(name = "type")
val type: String //<<<<< changed for convenience
)
The StationWithConnectors POJO NEW
class StationWithConnectors {
#Embedded
var station: Station? = null
var connectors: List<Connector> = emptyList()
constructor(allDao: AllDao, station: Station, connectorTypeList: List<String>) {
this.station = station
this.connectors = allDao.getConnectorsOfSpecifiedTypesByStationId(station.id,connectorTypeList)
}
}
note the embedded query to build the list of connectors of only the specfified types
The Dao used i.e. AllDao
#Dao
interface AllDao {
#Insert
fun insert(station: Station): Long
#Insert
fun insert(connector: Connector): Long
#Query("SELECT * FROM stations")
fun getAllStations(): List<Station>
#Query("SELECT * FROM stations WHERE stations.id = :stationId")
fun getStationById(stationId: Long): Station
// Gets the Connectors per Station of the requested Type (i.e. NOT ALL CONNECTORS necessarily)
#Query("SELECT * FROM connectors WHERE station_id = :stationId AND connectors.type IN( :connectorTypesList)")
fun getConnectorsOfSpecifiedTypesByStationId(stationId: Long, connectorTypesList: List<String>): List<Connector>
// Gets the Stations that have Connectors of the requested type DISTINCT used along with only the station columns
#Query("SELECT DISTINCT stations.id, stations.latitude, stations.longitude " +
"FROM stations INNER JOIN connectors ON connectors.station_id = stations.id " +
"WHERE connectors.type IN(:connectorTypesList)")
fun getStationsWithSpecificConnectorTypes(connectorTypesList: List<String>): List<Station>
}
The #Database TheDatabase
#Database(entities = [Connector::class,Station::class],version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
}
and finaly an Activity to test/demonstrate
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,TheDatabase::class.java,"thedb.db")
.allowMainThreadQueries()
.build()
dao = db.getAllDao()
// Define some types
var type1 = "TYPE_1"
var type2 = "TYEP_2"
var type3 = "CHADEMO"
var type4 = "ANOTHER"
// Define stations with Connectors
var station1 = Station(100,56.565,34.565)
dao.insert(station1)
dao.insert(Connector(10,station1.id,type1))
dao.insert(Connector(20,station1.id,type4))
dao.insert(Connector(30,station1.id,type3))
dao.insert(Connector(40,station1.id ,type2))
var station2 = Station(200,33.333,22.222)
dao.insert(station2)
dao.insert(Connector(100,station2.id,type2))
dao.insert(Connector(110,station2.id,type4))
dao.insert(Connector(120,station2.id,type3))
// Define the search types
var listOfTypes = listOf(type1,type2) // Types to search for
// prepare the StationWithConnectors list
var allswcList: ArrayList<StationWithConnectors> = ArrayList()
// Get the stations with connectors of the required types
var stationsWithCertainTYpes = dao.getStationsWithSpecificConnectorTypes(listOfTypes)
// Build the StationWithCertainTypes POJOs
for(s: Station in stationsWithCertainTYpes) {
allswcList!!.add(StationWithConnectors(dao,s, listOfTypes))
}
var count = stationsWithCertainTYpes.size //<<<< just so breakpoint can be added
}
}
When run in debug mode then:-
StationWithCertainTypes gets both Stations (station 1 has type1 and type2, station2 has type2) as per :-
allswcList has the 2 StationWithConnectors built from the 2 Stations as per :-
Hi I have a query where I order the list of products based on the product_end_date. I want to get only the list of products whose end date is greater than or equal to today, how can i do that in Room
here is my query
#Query("SELECT * FROM product_data_table ORDER BY product_end_date ASC")
fun getAllProductsOrderByEndDate(): LiveData<List<Product>>
I tried this but did not work
#Query("SELECT * FROM product_data_table WHERE product_end_date >= date('now') ORDER BY product_end_date ASC")
fun getAllProductsOrderByEndDate(): LiveData<List<Product>>
Entity
#Entity(tableName = "product_data_table")
data class Product(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "product_id")
var id: Int,
#ColumnInfo(name = "product_name")
var name: String,
#ColumnInfo(name = "product_catagory")
var catagory: String,
#ColumnInfo(name = "product_end_date")
var end_date: Date
)
I am not sure how to use todays date in where clause
please suggest how to fix this
your help is much appreciated
Thanks
R
Store the end_date in Entity class as Long in Unix time
#Entity(tableName = "product_data_table")
data class Product(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "product_id")
var id: Int,
#ColumnInfo(name = "product_name")
var name: String,
#ColumnInfo(name = "product_catagory")
var catagory: String,
#ColumnInfo(name = "product_end_date")
var end_date: Long // Changed the type from Date to Long
)
When inserting the product pass Date().time for end_date
Now when querying, get the Unix time of today and pass it as a parameter for the query function. Something like this:
#Query("SELECT * FROM product_data_table WHERE product_end_date >= :endDate")
fun getAllProductsOrderByEndDate(endDate: Long): List<Product>
I use Room framework in an Android Studio project.
At present, I need to get the records of MVoice and sort the query result by createdDate desc, createdDate asc, name desc or name asc.
So I write the following code, but it's too complex, I think maybe it can be simple, such as passing field as paramter, I don't know how to do, could you tell me?
Code
interface DBVoiceDao{
#Query("SELECT * FROM voice_table ORDER BY createdDate desc")
fun listVoiceDateDesc():LiveData<List<MVoice>>
#Query("SELECT * FROM voice_table ORDER BY createdDate asc")
fun listVoiceDateAsc():LiveData<List<MVoice>>
#Query("SELECT * FROM voice_table ORDER BY name desc")
fun listVoiceNameDesc():LiveData<List<MVoice>>
#Query("SELECT * FROM voice_table ORDER BY name asc")
fun listVoiceNameAsc():LiveData<List<MVoice>>
}
data class MVoice(
#PrimaryKey (autoGenerate = true) #ColumnInfo(name = "id") var id: Int = 0,
var name: String = "",
var path: String = "",
var createdDate: Calendar = Calendar.getInstance(),
var isStar: Boolean = false,
var description: String = "",
var translation: String = ""
): Parcelable {}
It works well to query record MVoice using fun listVoice():LiveData<List<MVoice>> with Room framework in Android Studio with Kotlin.
Now I hope to query record of part fields (Such as ID and name) of MVoice, how can I do?
interface DBVoiceDao{
#Query("SELECT * FROM voice_table ORDER BY createdDate desc")
fun listVoice():LiveData<List<MVoice>>
/* How can I do this?
#Query("SELECT id, name FROM voice_table ORDER BY createdDate desc")
fun listVoiceOfPartField():LiveData< ??? >
*/
}
#Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
#PrimaryKey (autoGenerate = true) #ColumnInfo(name = "id") var id: Int = 0,
var name: String = "Untitled",
var path: String = "My path",
var createdDate: Calendar = Calendar.getInstance(),
var isStar: Boolean = false,
var description: String="My description"
)
As "Florina Muntenescu" suggested that Read only what you need in this article 7 Pro-tips for Room
You can achieve it by making a new Model Class:
data class VoiceMinimal(#ColumnInfo(name = "id") val id: Int,
#ColumnInfo(name = "name") val name: String)
In the DAO class, we define the query and select the right columns from the voice table.
#Dao
interface DBVoiceDao {
#Query(“SELECT id, name FROM voice_table ORDER BY createdDate dESC)
fun getVoiceMinimal(): List<VoiceMinimal>
}