android.database.sqlite.SQLiteException:靠近“?”:语法错误(代码 1):

android.database.sqlite.SQLiteException: near "?": syntax error (code 1):

我正在使用 Room 库(MVVM 模式)和 Dao 函数之一 returns 此错误消息:

android.database.sqlite.SQLiteException: near "?": syntax error (code 1): , while compiling: UPDATE parcel_table SET possibleDeliveryPersonsList = ?,? WHERE id = ?

这是道代码:

@Dao
public interface ParcelDao {
    @Insert
    void insert(Parcel parcel);

    @Delete
    void delete(Parcel parcel);

    @Query("UPDATE parcel_table SET shippingDate=:shippingDate WHERE id = :id")
    void updateShippingDate(String shippingDate, int id);

    @Query("UPDATE parcel_table SET parcelStatus=:status WHERE id = :id")
    void updatePackageStatus(Enums.ParcelStatus status, int id);

    @Query("UPDATE parcel_table SET deliveryPersonName=:deliveryPersonName WHERE id = :id")
    void updateDeliveryPersonName(String deliveryPersonName, int id);

    @Query("UPDATE parcel_table SET possibleDeliveryPersonsList = :possibleList WHERE id = :tid")
    void updatePossibleDeliveryPersonsList(List<String> possibleList, int tid);

    @Query("DELETE FROM parcel_table")
    void deleteAllParcels();

    @Query("SELECT * from parcel_table")
    LiveData<List<Parcel>> getParcels();
}

这是包裹的一部分 class:

@Entity(tableName = "parcel_table")
public class Parcel {
    private Enums.ParcelType parcelType;
    private boolean isFragile;
    private Enums.ParcelWeight parcelWeight;
    private LatLng warehouseLocation;
    private String recipientName;
    private LatLng recipientAddress;
    private String recipientEmail;
    private String recipientPhone;
    private String dateReceived;
    private String shippingDate;
    private Enums.ParcelStatus parcelStatus;
    private String deliveryPersonName;
    private String fireBasePushId;
    private List<String> possibleDeliveryPersonsList;
@PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;
        //and more...
}

List<String>类型转换器:

@TypeConverter
    public String listToString(List<String> list) {
        String joined = TextUtils.join(", ", list);
        return joined;
    }

    @TypeConverter
    public List<String> stringToList(String string) {
        List<String> myList = new ArrayList<String>(Arrays.asList(string.split(",")));
        return myList;
    }

我不知道该怎么做,因为 SQLite 代码应该是由 Dao 自动生成的,我对它没有影响...

Query 注释的文档解释了 Room 参数绑定的一个特性:

As an extension over SQLite bind arguments, Room supports binding a list of parameters to the query. At runtime, Room will build the correct query to have matching number of bind arguments depending on the number of items in the method parameter.

此功能可能打算用在 where 子句中(如文档示例中所示),但似乎在查询语句中无处不在。

在您的情况下,期望的行为是让 Room 应用您的类型转换器,但 Room 忽略类型转换器并生成特殊列表绑定。

我认为您将不得不解决当前 Room 实施的这一限制。您可能想写一个 Room bug-report 来确认这里提供的解释是正确的。

将使用类型转换器的一个 option for workaround 是定义此 class:

public class DeliveryPersonsUpdate {
    public int id;
    public List<String> deliveryPersons;

    public DeliveryPersonsUpdate(int id, List<String> deliveryPersons) {
        this.id = id;
        this.deliveryPersons = deliveryPersons;
    }
}

然后将此方法添加到您的 Dao 中:

@Update(entity = Parcel.class)
void update(DeliveryPersonsUpdate update);

调用示例:

db.ParcelDao().update(new DeliveryPersonsUpdate(id, personsList); 

除了 Bob Snyder 的回答之外还有两个解决方法(但它们需要经过全面测试):

  1. 自己给"imitate" TypeConverter(从List到String)(这是一个棘手的事情,我没有在实践中尝试过!):

在 DAO 中,将 possibleList 的类型更改为 String:

@Query("UPDATE parcel_table SET possibleDeliveryPersonsList = :possibleList WHERE id = :tid")
    void updatePossibleDeliveryPersonsList(String possibleList, int tid);

添加转换辅助方法(也可以放在DAO中):

void updatePossibleDeliveryPersonsList(List<String> possibleList, int tid) {
   String listToString = TextUtils.join(", ", possibleList); 
   // copied from your converter, it could be put in some common function to follow DRY
   updatePossibleDeliveryPersonsList(listToString, tid); 
}

并从 Repository/ViewModel:

调用它
db.ParcelDao().updatePossibleDeliveryPersonsList(possibleList, tid);
  1. 用单个更新替换 DAO 中的多个 updateXXX 方法(您的 table 中有很多字段,可能最好尝试一些通用的方法来更新它们的任意组合?) :
@Update
void update(Parcel parcel);

添加到您的 DAO 方法以按 ID 搜索包裹:

@Query("SELECT * from parcel_table where id = :id")
Parcel getParcel(int id);

然后在您的 Repository/ViewModel 中首先获取 Parcel,然后更改它(状态、名称)然后更新数据库:

Parcel parcel = db.ParcelDao().getParcel(id); // let's say it can't be null
parcel.shippingDate = yourShippingDate; // or change here any of your other fields, including list
db.ParcelDao().update(parcel);