![](/img/trans.png)
[英]How to customize Room Database Query results that we can achieve in normal SQLite Open helper class
[英]Can we pass nullable Argument in room query
这是我的查询
如果给定的参数是 null,我正在尝试通过给定的参数搜索属性,那么它应该采用所有属性并且不过滤
@Query("""SELECT * FROM Property
WHERE
(listedFor = :lookingTo OR :lookingTo IS NULL) AND
(propertyType IN (:propertyType) OR :propertyType IS NULL) AND
(areaId IN(:areasOfProperties) OR :areasOfProperties IS NULL) AND
cityId = :cityId
ORDER BY registrationDate DESC""")
fun getPropertyListBySearchQuery(
lookingTo: String?,
propertyType: ArrayList<String>?,
areasOfProperties: ArrayList<String>?,
cityId: String,
) : LiveData<List<Property>>
这让我崩溃如下
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: android.database.sqlite.SQLiteException: near ",": syntax error (code 1): , while compiling: SELECT * FROM Property
WHERE
(listedFor = ? OR ? IS NULL) AND
(propertyType IN (?,?) OR ?,? IS NULL) AND
(areaId IN() OR IS NULL) AND
cityId = ?
ORDER BY registrationDate DESC
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:965)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:576)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1353)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1328)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.query(FrameworkSQLiteDatabase.java:183)
at androidx.room.RoomDatabase.query(RoomDatabase.java:530)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
at com.townproperties.data.localdata.LocalDao_Impl$23.call(LocalDao_Impl.java:1515)
at com.townproperties.data.localdata.LocalDao_Impl$23.call(LocalDao_Impl.java:1512)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
我们可以在房间查询中传递可为空的参数吗?
是的,但它有点令人费解。
简要说明
您显示的问题不是参数之一是否为 NULL 的问题(在这种情况下,我相信您会遇到 NPE(空指针异常)),但您正在尝试将列表(CSV)检查为如果它是单个值,因此逗号会导致语法错误。
?
*'s indicate to SQLite that the value will be a bound value - see https://www.sqlite.org/c3ref/bind_blob.html 解决此问题的一种方法是构建 SQL 并使用@RawQuery
。 SQL 内置在带有主体的 function 中。 以下是一个工作演示:-
首先是@Dao
注解的接口:-
@Dao
interface PropertyDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(property: Property)
@RawQuery
fun rawQueryPropertyList(rawQuery: SimpleSQLiteQuery): List<Property>
fun getPropertiesBySearchCriteria(
lookingTo: String?,
propertyType: ArrayList<String>?,
areasOfProperties: ArrayList<String>?,
cityId: String
): List<Property> {
var afterFirstInList: Boolean = false
var whereAdded = false
val baseSQL = "SELECT * FROM Property"
val qry: StringBuilder = java.lang.StringBuilder().append(baseSQL)
/* Looking To */
if (lookingTo != null && lookingTo.length > 0) {
if (!whereAdded) {
qry.append(" WHERE ")
whereAdded = true
}
if (qry.length > baseSQL.length) qry.append(" AND ")
qry.append(" listedFor='${lookingTo}'")
}
/* Property Type */
if (propertyType!= null && propertyType.size > 0) {
afterFirstInList = false
if (qry.length > baseSQL.length) qry.append(" AND ")
if (!whereAdded) {
qry.append(" WHERE ")
whereAdded = true
}
qry.append(" propertyType IN (")
for (s in propertyType) {
if (afterFirstInList) qry.append(",")
qry.append("'${s}'")
afterFirstInList = true
}
qry.append(")")
}
if (areasOfProperties!=null && areasOfProperties?.size!! > 0) {
afterFirstInList = false
if (qry.length > baseSQL.length) qry.append(" AND ")
if (!whereAdded) {
qry.append(" WHERE ")
whereAdded = true
}
qry.append(" areaId IN (")
for (s in areasOfProperties) {
if (afterFirstInList) qry.append(",")
qry.append("'${s}'")
afterFirstInList = true
}
qry.append(")")
}
if (qry.length > baseSQL.length) qry.append(" AND ")
if (!whereAdded) {
qry.append(" WHERE ")
whereAdded = true
}
qry.append(" cityId='${cityId}' ")
qry.append(" ORDER BY registrationDate DESC")
Log.d("PRPRTYINFO_SQL",qry.toString()) /* ONLY FOR TESTING */
return rawQueryPropertyList(SimpleSQLiteQuery(qry.toString()))
}
}
'@Entity' 带注释的属性数据 class 用于测试(显然可能不是您拥有的,而是根据问题中可用的内容构建的)。 如果有的话,差异可能不会那么重要:
@Entity
data class Property(
@PrimaryKey
var propertyId: Long?=null,
var propertyType: String,
var areaId: Long,
var cityId: Long,
var listedFor: Long,
var registrationDate: Long
)
使用了一个非常标准的@Database
注释 class。 请注意,为了简洁和方便,使用了.allowMainThreadQueries
。
最后是用于执行有限测试的活动代码:-
class MainActivity : AppCompatActivity() {
lateinit var db: PropertyDatabase
lateinit var dao: PropertyDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = PropertyDatabase.getInstance(this)
dao = db.getPropertyDao()
dao.insert(Property(null,"House",100,1090,100,System.currentTimeMillis() ))
dao.insert(Property(null,"Flat",200,2090,200,System.currentTimeMillis()))
dao.insert(Property(null,"Villa",300,3090,300,System.currentTimeMillis()))
logProperties(dao.getPropertiesBySearchCriteria("", arrayListOf(""), arrayListOf(""),"1090"),"T1")
logProperties(
dao.getPropertiesBySearchCriteria(
lookingTo = "",
propertyType = arrayListOf("House","Flat","Villa","Apartment","Castle"),
areasOfProperties = arrayListOf("1","2","100","200","300","1000000000"),
cityId = "1090"
),
"T2"
)
logProperties(dao.getPropertiesBySearchCriteria(null, null, null,"1090"),"T3")
}
fun logProperties(plist: List<Property>, tagSuffix: String ) {
val TAG = "PRPRTYINFO_${tagSuffix}"
for (p in plist) {
Log.d(TAG,"Type=${p.propertyType} AreaId=${p.areaId} CityId=${p.cityId} ListedFor=${p.listedFor} RDate=${Date(p.registrationDate)}")
}
}
}
当运行全新安装(仅设计运行一次)时,日志包括:-
2022-09-26 12:35:46.722 D/PRPRTYINFO_SQL: SELECT * FROM Property WHERE propertyType IN ('') AND areaId IN ('') AND cityId='1090' ORDER BY registrationDate DESC
2022-09-26 12:35:46.728 D/PRPRTYINFO_SQL: SELECT * FROM Property WHERE propertyType IN ('House','Flat','Villa','Apartment','Castle') AND areaId IN ('1','2','100','200','300','1000000000') AND cityId='1090' ORDER BY registrationDate DESC
2022-09-26 12:35:46.741 D/PRPRTYINFO_T2: Type=House AreaId=100 CityId=1090 ListedFor=100 RDate=Mon Sep 26 12:35:46 GMT+10:00 2022
2022-09-26 12:35:46.741 D/PRPRTYINFO_SQL: SELECT * FROM Property WHERE cityId='1090' ORDER BY registrationDate DESC
2022-09-26 12:35:46.742 D/PRPRTYINFO_T3: Type=House AreaId=100 CityId=1090 ListedFor=100 RDate=Mon Sep 26 12:35:46 GMT+10:00 2022
不需要在运行时构建 SQL 并使用原始查询(在编译时未检查,尽管根据您的示例编译时检查不是 100%)的替代方法是利用 CTE(公用表表达式(查询中的临时表))。 然而,由于必须管理列表以及缺少列表,这可能比构建 SQL 更复杂。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.