I was trying to return multiple values from the table with such a query: `
@Query("SELECT *, AVG(engineCapacity) as avgCapacity FROM Car WHERE bodyType=:bodyType")
fun getByBodyType(bodyType: BodyType): ParameterQueryResult
The return type code:
data class ParameterQueryResult(
val cars: List<Car>,
val avgCapacity: Double
)
And got the error:
"Cannot figure out how to read this field from a cursor. private final java.util.List<com.example.project2.db.Car> cars = null;"
The same happened when I replaced the return type with Pair<List, Double>.
Code of the Entity class:
@Entity
data class Car(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val brand: String,
val bodyType: BodyType,
val color: String,
val engineCapacity: Double,
val price: Double,
)
Did anyone run into the same problem? Is it possible to return such combination of fields without using Cursor directly? Thanks a lot in advance
This is initially because Room doesn't know how to handle the var Car ParameterQueryResult as the class isn't actually a know class. So it needs to know what columns make up the car val. You can either include specific vals for each of the required vals from the car table OR more simply you can embed the Car class using @Embedded annotation, so
data class ParameterQueryResult(
@Embedded
val cars: Car,
val avgCapacity: Double
)
Then, as the avg function is an aggregate function it returns a single value for all the rows of a group.
In the absence of a GROUP BY clause, then the entire selection is the group (i.e. a single group aka 1 row for all the underlying rows selected by the WHERE clause).
As such you might as well use:-
@Query("SELECT AVG(engineCapacity) as whatever_as_it_does_not_matter_being_sinlge_value FROM Car WHERE bodyType=:bodyType")
fun getByBodyType(bodyType: String): Double
AS whatever_as_it_does_not_matter_being_sinlge_value
) does not matter as Room will just get the single value (index 0 from the underlying Cursor)
SELECT AVG(engineCapacity) FROM Car WHERE bodyType=:bodyType
However, say, for example, you wanted the average engine capacity for each color; then you would need to return, for example, List
and you would then use:-
@Query("SELECT *, avg(engineCapacity) AS avgCapacity FROM Car WHERE bodyType=:bodyType GROUP BY color")
fun getByBodyType(bodyType: String): List<ParameterQueryResult>
avg(engineCapacity)
, although then you would perhaps have an issue determining what value is associated with what color. In which case you may to output a List based upon a class that is comprised of a String for the color and a double for the average.Demo
Using the following (as per the above):-
data class ParameterQueryResult(
@Embedded
val car: Car,
val avgCapacity: Double
)
@Dao
interface TheDAOs {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(car: Car): Long
@Query("SELECT avg(engineCapacity) FROM car WHERE bodyType=:bodyType")
fun getAECByBodyType(bodyType: BodyType): Double
@Query("SELECT *,avg(engineCapacity) AS avgCapacity FROM car WHERE bodyType=:bodyType")
fun getCarAndAECByBodyType(bodyType: BodyType): List<ParameterQueryResult>
@Query("SELECT *, avg(engineCapacity) AS avgCapacity FROM Car WHERE bodyType=:bodyType GROUP BY color")
fun getByBodyType(bodyType: BodyType): List<ParameterQueryResult>
}
And then in an activity (for convenience and brevity run on the main thread) :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: TheDAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getTheDAOs()
dao.insert(Car(999, "brand1", BodyType("sedan",), "Red", 1500.00, 21999.00))
dao.insert(Car(9, "brand2", BodyType("suv", 200), "Green", 1600.00, 32500.00))
dao.insert(Car(99, "brand3", BodyType("hatch"), "Black", 1200.00, 18700.00))
dao.insert(Car(9999, "brand4", bodyType = BodyType("sedan"), "White", 1300.00, 19999.99))
dao.insert(Car(88, "brand1", BodyType("sedan"), "Metallic Red", 1500.00, 22499.99))
for (c in dao.getCarAndAECByBodyType(BodyType("sedan"))) {
Log.d("DBINFO_Q1", "AEC=${c.avgCapacity} CarID is ${c.car.id} Brand is ${c.car.brand} Color is ${c.car.color} .... ")
}
Log.d("DBINFO_Q2","AEC is ${dao.getAECByBodyType(BodyType("sedan"))}")
for (c in dao.getByBodyType(BodyType("sedan"))) {
Log.d("DBINFO_Q3", "AEC=${c.avgCapacity} CarID is ${c.car.id} Brand is ${c.car.brand} Color is ${c.car.color} .... ")
}
}
}
The result is :-
2022-10-30 16:55:37.590 D/DBINFO_Q1: AEC=1433.3333333333333 CarID is 88 Brand is brand1 Color is Metallic Red ....
2022-10-30 16:55:37.592 D/DBINFO_Q2: AEC is 1433.3333333333333
2022-10-30 16:55:37.595 D/DBINFO_Q3: AEC=1500.0 CarID is 88 Brand is brand1 Color is Metallic Red ....
2022-10-30 16:55:37.595 D/DBINFO_Q3: AEC=1500.0 CarID is 999 Brand is brand1 Color is Red ....
2022-10-30 16:55:37.596 D/DBINFO_Q3: AEC=1300.0 CarID is 9999 Brand is brand4 Color is White ....