Room:删除 ForeignKey 或将 ForeignKey 设置为默认值

Room: deleting a ForeignKey or setting the ForeignKey to a default value

我有以下实体:

@Entity(indices  = {@Index("address")},
    foreignKeys = @ForeignKey(
    entity = Address.class,
    parentColumns = "address",
    childColumns = "address",
    onUpdate = CASCADE,
    onDelete = SET_DEFAULT))
public class Human {
    @PrimaryKey(autoGenerate = true)
    public long id;

    @NonNull
    public String name;

    @NonNull
    public String address;
}

@Entity
public class Address {

    @PrimaryKey
    @NonNull
    public String address;

    public int someInt;
}

我用不同的地址添加 "humans",稍后我尝试使用以下方法删除或编辑:

@Insert(onConflict = REPLACE)
void add(Human human);

我看到的选项和它们的问题:

  1. 我有一个地址列表,其中一个是 "default"。当我删除一个地址时,我希望位于被删除地址的"Humans"到"move"到"default"。 我用过:

    @Query("delete from Address where address = :address")
    void deleteAddress(String address);
    

所以 onDelete = SET_DEFAULT 似乎并没有像我想象的那样工作,如果我在人类实体中设置默认值地址。

当前失败:NOT NULL 约束失败。

  1. 删除的替代方法是编辑地址并将其设置为 "default",从而与现有的 "default" 条目合并。 我试过了:

    @Query("update Address SET address = :address WHERE address = :addressToEdit")
    void editAddress(String addressToEdit, String newAddress);
    

这当前失败了:UNIQUE 约束失败。 因为已经有一个同名的地址,显然。

  1. 在两个实体中将地址设置为@Nullable。这样,"default"地址为空。这给我的解决方案增加了额外的复杂性(我不喜欢它)而且我似乎无法使用以下代码查询具有空地址的 Humans,因为它没有 return 任何东西。

    @Query("select * from Human where address = :address")
    LiveData<List<Human>> getHumans(String address);
    

如何将此设置设为默认的已删除地址(外键)?

我建议修改您的地址实体并将 id 字段添加为自动递增的整数。然后在人类实体中,您可以通过 id 字段引用地址。因此,在删除地址记录之前,您可以使用要删除的默认引用 ID 更新人类记录,例如:1 然后您可以通过其 ID 删除此地址记录。在这里你可以解决地址唯一约束问题。

到目前为止,我最好的解决方案是使用问题中的第三个选项:

    @Query("select * from Human where address is NULL")
    LiveData<List<Human>> getAllHumansWithNullAddress();

与默认使用 NULL 一样。

Address 保持不变,但人类变为:

@Entity(indices  = {@Index("address")},
    foreignKeys = @ForeignKey(
    entity = Address.class,
    parentColumns = "address",
    childColumns = "address",
    onUpdate = CASCADE,
    onDelete = SET_NULL))
public class Human {
    @PrimaryKey(autoGenerate = true)
    public long id;

    @NonNull
    public String name;

    @Nullable
    public String address;
}

当前 room 不支持 onDelete = SET_DEFAULT 操作,room 将其视为与 onDelete = SET_NULL 相同,所以我建议如下:-

@Entity(foreignKeys = @ForeignKey(
                entity = Address.class,
                parentColumns = "address",
                childColumns = "fk_address_id",
                onUpdate = CASCADE,
                onDelete = SET_DEFAULT))
public class Human {
    @PrimaryKey(autoGenerate = true)
    public long id;

    @NonNull
    public String name;


    @Nullable
    public String getFk_address_id() {
        return fk_address_id;
    }

    public void setFk_address_id(@Nullable String fk_address_id) {
        this.fk_address_id = fk_address_id;
    }

    //default address doesn't work on delete as room doesn't support
    // on delete set default and treat it like set null
    @Nullable
    public String fk_address_id = "default address";

    public Human(@NonNull String name,  @Nullable String fk_address_id) {
        this.name = name;
        this.fk_address_id = fk_address_id;
    }

}

这里是地址字段唯一的地址实体。

@Entity(indices = {@Index(value = {"address"},
        unique = true)})
public class Address {
    public void setAddress(@NonNull String address) {
        this.address = address;
    }

    @PrimaryKey(autoGenerate = true)
    @NonNull
    int id;


    public String address = "default address";


    public int someInt;

    public Address(@NonNull String address, int someInt) {
        this.address = address;
        this.someInt = someInt;
    }

}

并且在 HumanDao 中你可以获得空引用地址并将它们更新为默认地址。

人道:

@Query("SELECT * FROM Human where fk_address_id IS NULL")
Human[] getNullRef();

地址道:

@Update
void updateAddresses(Address... addresses);

异步任务class

protected Void doInBackground(Void... voids) {
            AppDatabase db = AppDatabase.getDatabase(getApplicationContext());
            Address address = db.addressDao().getAddress("address to delete");
            if(address != null) {
                db.addressDao().delete(address);
                Human [] humans = db.humanDao().getNullRef();
                for(int i =0; i < humans.length; i++) humans[i].setFk_address_id(DEFAULT_ADDRESS);
                db.humanDao().updateHumans(humans);
            }
            return null;
        }