Mostrando postagens com marcador BD. Mostrar todas as postagens
Mostrando postagens com marcador BD. Mostrar todas as postagens

domingo, 30 de agosto de 2009

Utilizando Banco de Dados SQLite no Android

Olá povo,

Entre as muitas coisas legais que o Android trouxe, o suporte nativo ao banco de dados SQLite foi uma das mais importantes, comercialmente falando. Com ele podemos utilizar os bons e famosos comandos SQL (SELECT, INSERT, UPDATE, DELETE, etc) no mundo mobile. Quem programou em J2ME sabe o quão complicado é implementar um repositório "digno" que suporte uma boa quantidade de registros nessa plataforma (não me falem de RMS... :)

Nesse POST, vou colocar trechos de código que utilizam esses conceitos e tentarei explicá-los. A parte da interface gráfica, vou explicar apenas o que for relevante para o uso com banco de dados.

Para criarmos um banco de dados SQLite, podemos:
  • utilizar a ferramenta SQLite Expert Personal e colocar o arquivo do banco de dados na pasta /data/data/pacote_da_minha_aplicacao/databases;
  • ou via código, utilizando uma classe "helper" do SQLite.
Utilizaremos a segunda abordagem, pois na vida real não queremos colocar esse arquivo manualmente no aparelho. Pois, caso seja necessária uma atualização, teremos que atualizar N aparelhos.

Mãos à obra! Vamos criar uma classe que herda de android.database.sqlite.SQLiteOpenHelper. Ela será responsável por criar o banco quando a aplicação for instalada (método onCreate) e atualizá-lo para novas versões da aplicação (método onUpgrade).

public class SQLiteHelper extends SQLiteOpenHelper {
  private String scriptCreate;
  private String scriptDelete;

  public SQLiteHelper(Context ctx, String nomeBd, 
      int versaoBanco, String scriptCreate, 
      String scriptDelete) {

      super(ctx, nomeBd, null, versaoBanco);
      this.scriptCreate = scriptCreate;
      this.scriptDelete = scriptDelete;
  }

  public void onCreate(SQLiteDatabase db) {
      db.execSQL(scriptCreate);
  }

  public void onUpgrade(SQLiteDatabase db, 
      int oldVersion, int newVersion) {

      db.execSQL(scriptDelete);
      onCreate(db);
  }
}


Agora vamos criar a classe que realizará as operações de abrir, selecionar, inserir, atualizar e excluir registos no banco de dados:

public class RepositorioCarro {
  private SQLiteDatabase db;
  private SQLiteHelper dbHelper;
  private static final String SCRIPT_DB_DELETE = 
    "DROP TABLE IF EXISTS carros";
  private static final String SCRIPT_DB_CREATE =
    "create table carros (_id integer primary "+
    "key autoincrement, nome text not null, "+
    "placa text not null, ano text not null);";

  public RepositorioCarro (Context ctx){
    dbHelper = new SQLiteHelper(ctx, "curso", 1, 
      SCRIPT_DB_CREATE, SCRIPT_DB_DELETE);
  }

  private long inserir(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome())
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());
    db = dbHelper.getWritableDatabase();
    long id = db.insert("carros", null, cv);
    db.close();
    return id;
  }

  private long atualizar(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome());
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());

    db = dbHelper.getWritableDatabase();
    long rows = db.update("carros", cv, "_id = ?", 
       new String[]{ String.valueOf(c.getId())});
    db.close();
    return rows; // qtde. de linhas afetadas 
  } 

  public int excluir(int id){
    db = dbHelper.getWritableDatabase();
    int rows = db.delete("carros", "_id = ?", 
      new String[]{ String.valueOf(id) });
    return rows; // qtde. de linhas afetadas
  }

  public List<Carro> buscarCarroPorNome(String nome){
    List<Carro> lista = new ArrayList<Carro>();

    String[] columns = new String[]{
       "_id", "nome", "placa", "ano"};
    String[] args = new String[]{nome+"%"};

    db = dbHelper.getWritableDatabase();
    Cursor c = db.query("carros", columns, 
       "nome like ?", args, null, null, "nome");

    c.moveToFirst();
    while(!c.isAfterLast()){
      Carro carro = fillCarro(c);
      lista.add(carro);
      c.moveToNext();
    }
    c.close();
    db.close();
    return lista;
  }

  private Carro fillCarro(Cursor c) {
    Carro carro = new Carro();
    carro.setId((int)c.getLong(0));
    carro.setNome(c.getString(1));
    carro.setPlaca(c.getString(2));
    carro.setAno(c.getString(3));
    return carro;
  }
}


Como vocês podem ver, estou usando uma classe Carro também. Não vou listá-la aqui por se tratar de um POJO com os campos: id, nome, placa e ano do veículo.

Na classe acima, o construtor recebe uma referência do contexto da aplicação. Ela é repassada para nossa classe SQLHelper, que por sua vez, se encarregará de criar o nosso banco de dados se ele não existir. A classe android.database.sqlite.SQLiteOpenHelper (superclasse do nosso helper) tem o método getWritableDatabase() que retorna uma instância da classe android.database.sqlite.SQLiteDatabase, que será nosso canal de comunicação com o banco de dados.

As operações de inserir e atualizar utilizam: uma string que representa o nome da tabela no banco de dados; e um objeto da classe ContentValues, que nada mais é do que um conjunto par/valor com os parâmetros do comando SQL. Mas notem que o método update recebe ainda uma String que representa a cláusula WHERE do comando UPDATE (do SQL), bem como os parâmetros da mesma, que estão mapeados via caracter '?'.
"Por tabela", o método excluir vocês já devem ter entendido :)

O último método é o buscarCarroPorNome(String). Toda consulta SQL, retorna um objeto do tipo Cursor. Ele é bem parecido com o ResultSet do JDBC. Os parâmetros do método query são, respectivamente: String nome da tabela, String[] colunasDaTabela, String clausulaWhere, String[] paramsWhere, String groupBy, String having, String orderBy.

Para exibir os dados, precisaremos criar um Adapter, que converterá um Carro em uma linha de uma lista. Crie o arquivo de layout list_item.xml e coloque-o na pasta res/layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout android:layout_height="wrap_content" 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:id="@+id/carroRow">

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtNome" 
android:layout_weight="1" 
android:textSize="20dp"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtAno" 
android:layout_marginRight="10px"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtPlaca"  
android:layout_marginRight="10px"/>

</LinearLayout>


E agora, vamos criar nosso adapter.

public class CarroListAdapter extends BaseAdapter {
  private Context context;
  private List<Carro> lista;

  public CarroListAdapter(Context ctx, 
    List<Carro> carros) {

    context = ctx;
    lista = carros;
  }

  public int getCount() {
    return lista.size(); 
  }

  public Object getItem(int arg0) {
    return lista.get(arg0);
  }

  public long getItemId(int arg0) {
    return lista.get(arg0).getId();
  }

  public View getView(int pos, View convertView, 
    ViewGroup parent) {

    Carro c = lista.get(pos);
    LayoutInflater inflater = 
      (LayoutInflater)context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE);

    View view = inflater.inflate(
      R.layout.list_item, null);

    ((TextView)view.findViewById(R.id.txtNome)).
      setText(c.getNome());

    ((TextView)view.findViewById(R.id.txtAno)).
      setText(c.getAno());

    ((TextView)view.findViewById(R.id.txtPlaca)).
      setText(c.getPlaca());

    return view;
  }
}


Crie uma classe que herde de ListActivity e no onCreate, digite o seguinte código:
RepositorioCarro carrosDB = new RepositorCarro(this);
List<Carro>lista = carrosDB.buscarCarroPorNome("");
setAdapter(new CarroListAdapter(this, lista));

Para utilizar os demais métodos é facinho, não é? Basta criar objetos do tipo Carro, no qual os valores serão preenchidos por widgets (componentes de UI) e passá-los como parâmetros para os métodos do RepositorioCarro.

Espero que tenham entendido e gostado :)

Editado em 03/11/2011
Sempre que fizermos uma operação no banco de dados é importante que encerremos a conexão com ele. Tinha faltado isso nesse post, e agora lembrei de corrirgir.

4br4ç05
nglauber