室:删除外键或将外键设置为默认值

时间:2018-10-22 20:03:32

标签: android sql sqlite foreign-keys android-room

我有以下实体:

@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;
}

我添加了具有不同地址的“人类”,后来我尝试使用以下地址进行删除或编辑:

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

我看到的选项及其问题:

  1. 我有一个地址列表,其中一个是“默认”。删除地址时,我希望驻留在已删除地址上的“人类”“移动”到“默认”。 我用过:

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

所以onDelete = SET_DEFAULT似乎不像我想象的那样,如果我将默认值设置为人类实体中的地址。

此操作当前失败,并显示: NOT NULL约束失败。

  1. 删除操作的替代方法是编辑地址并将其设置为“默认”,然后与现有的“默认”条目合并。 我尝试过:

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

此操作当前失败,并显示:唯一约束失败。,因为显然已经有一个具有相同名称的地址。

  1. 在两个实体中都将地址设置为@Nullable。这样,“默认”地址为空。这给我的解决方案增加了额外的复杂性(不希望使用它),而且似乎无法使用以下代码使用空地址查询人类,因为它不返回任何内容。

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

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

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

    @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;
}

答案 2 :(得分:0)

当前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();

AddressDao:

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

AsyncTask类

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;
        }