
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.
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