Pergunta

Qualquer um tem um bom conselho sobre como implementar o mapeamento um para muitos SQLite usando ContentProvider? Se você olhar para Uri ContentProvider#insert(Uri, ContentValues) você pode ver que tem ContentValues param que contém dados para inserir. O problema é que em sua implementação atual ContentValues não suporta put(String, Object) O método e a classe são finais, então não posso estendê -lo. Por que é um problema? Aqui vem meu design:

Eu tenho 2 mesas que estão em um relacionamento um para muitos. Para representá -los no código, tenho 2 objetos de modelo. O 1º representa o registro principal e possui um campo que é uma lista de instâncias do segundo objeto. Agora eu tenho um método auxiliar no objeto Model #1, que retorna ContentValues gerado fora do objeto atual. É trivial preencher um campo primitivo com ContentValues#put Métodos sobrecarregados, mas estou sem sorte para a lista. Então, atualmente, já que minha segunda linha de tabela é apenas um valor de string, eu gero uma string delimitada de vírgula que então reparo em string [] dentro ContentProvider#insert. Isso parece nojento, então talvez alguém possa sugerir como isso pode ser feito de maneira mais limpa.

Aqui está algum código. Primeiro da classe modelo:

public ContentValues toContentValues() {
    ContentValues values = new ContentValues();
    values.put(ITEM_ID, itemId);
    values.put(NAME, name);
    values.put(TYPES, concat(types));
    return values;
}

private String concat(String[] values) { /* trivial */}

E aqui está a versão reduzida de ContentProvider#insert método

public Uri insert(Uri uri, ContentValues values) {
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        // populate types
        String[] types = ((String)values.get(Offer.TYPES)).split("|");
        // we no longer need it
        values.remove(Offer.TYPES);
        // first insert row into OFFERS
        final long rowId = db.insert("offers", Offer.NAME, values);
        if (rowId > 0 && types != null) {
            // now insert all types for the row
            for (String t : types) {
                ContentValues type = new ContentValues(8);
                type.put(Offer.OFFER_ID, rowId);
                type.put(Offer.TYPE, t);
                // insert values into second table
                db.insert("types", Offer.TYPE, type);
            }
        }
        db.setTransactionSuccessful();
        return ContentUris.withAppendedId(Offer.CONTENT_URI, rowId);
    } catch (Exception e) {
        Log.e(TAG, "Failed to insert record", e);
    } finally {
        db.endTransaction();
    }

}
Foi útil?

Solução

Eu acho que você está olhando para o fim errado do relacionamento um para muitos.

Dê uma olhada no ContactsContract Provedor de conteúdo, por exemplo. Os contatos podem ter muitos endereços de e -mail, muitos números de telefone, etc. A maneira como é realizada é fazer inserções/atualizações/exclusão no "muitos" lado. Para adicionar um novo número de telefone, você insere um novo número de telefone, fornecendo um ID do contato para quem o número de telefone pertence.

Você faria o mesmo se tivesse um banco de dados SQLite simples sem provedor de conteúdo. Relacionamentos individuais nos bancos de dados relacionais são alcançados por meio de inserções/atualizações/exclui em uma tabela para o lado "muitos", cada um com uma chave estrangeira de volta ao lado "One".

Agora, do ponto de vista do OO, isso não é o ideal. Você pode criar objetos Wrapper no estilo ORM (pense em Hibernate) que permitem manipular uma coleção de crianças do lado "One". Uma classe de coleta suficientemente inteligente pode mudar e sincronizar a tabela "muitos" para corresponder. No entanto, estes não são necessariamente triviais para implementar adequadamente.

Outras dicas

Você pode usar ContentProviderOperations por esta.

São basicamente operações em massa com a capacidade de referência de volta aos identificadores gerados para linhas parentais.

Quão ContentProviderOperations Pode ser usado para um design um para muitos é muito bem explicado nesta resposta: Quais são a semântica do WithValueBackReference?

Então, eu vou responder minha própria pergunta. Eu estava no caminho certo com duas tabelas e dois objetos modelo. O que estava faltando e o que me confundiu era que eu queria inserir diretamente dados complexos através ContentProvider#insert em uma única chamada. Isto está errado. ContentProvider deve criar e manter essas duas tabelas, mas a decisão sobre qual tabela usar deve ser ditada pelo parâmetro URI de ContentProvider#insert. É muito conveniente usar o contentResolver e adicionar métodos como "addfoo" ao objeto Model. Esse método levaria o parâmetro contentResolver e, no final, aqui estão a sequência para inserir um registro complexo:

  1. Insira o registro dos pais através ContentProvider#insert e obtenha ID de registro
  2. De acordo com cada criança, fornece ID de pai (chave anteriores) e use ContentProvider#insert Com diferentes URI para inserir registros infantis

Portanto, a única questão restante é como envelope o código acima na transação?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top