Mostrando postagens com marcador Banco de Dados. Mostrar todas as postagens
Mostrando postagens com marcador Banco de Dados. Mostrar todas as postagens

quinta-feira, 2 de junho de 2011

Copiando um banco de dados SQLite no Android

Olá povo,

As pessoas constantemente me perguntam como criar um banco de dados SQLite no Android que já venha com alguns registros. No meu post sobre SQLite no Android eu recomendo que isso seja feito na classe SQLiteOpenHelper através de um arquivo XML (por exemplo) por ser uma forma padronizada e centralizada para manutenção dos BDs no Android.
Entretanto algumas pessoas querem mesmo é copiar um banco de dados já prontinho pra aplicação, e que diga-se de passagem pode ser uma boa opção. Desta forma, segue abaixo o código que faz isso:
class Util {
public static void copiaBanco(
Context ctx, String nomeDB){

// Cria o banco vazio
SQLiteDatabase db = ctx.openOrCreateDatabase(
nomeDB, Context.MODE_WORLD_WRITEABLE, null);

db.close();

try {
// Abre o arquivo que deve estar na pasta assets
InputStream is = ctx.getAssets().open(nomeDB);
// Abre o arquivo do banco vazio ele fica em:
// /data/data/nome.do.pacote.da.app/databases
FileOutputStream fos = new FileOutputStream(
ctx.getDatabasePath(nomeDB));

// Copia byte a byte o arquivo do assets para
// o aparelho/emulador
byte[] buffer = new byte[1024];
int read;
while ((read = is.read(buffer)) > 0){
fos.write(buffer, 0, read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Notem alguns detalhes interessantes aqui. O método openOrCreateDatabase além de criar a pasta "databases" dentro do diretório onde os bancos de dados devem ficar, ele cria também o arquivo de banco de dados, só que vazio. Uma vez com a estrutura pronta, basta abrir o banco de dados que está com as informações (que no meu exemplo coloquei na pasta "assets") e sobrepor o banco vazio.
Para chamar o método de uma Activity e visualizar uma informação do banco fazemos conforme abaixo:
// carroDB é nome do banco e deve
// ser o nome do arquivo do banco
Util.copiaBanco(this, "carroDB");

SQLiteDatabase db = openOrCreateDatabase(
"dbCarro", MODE_WORLD_WRITEABLE, null);

Cursor c = db.query("carro", null, null, null, null, null, null);
while (c.moveToNext()){
Log.d("NGVL", "Carro: "+ c.getString(1));
}
c.close();
db.close();


4br4ç05,
nglauber

sexta-feira, 4 de fevereiro de 2011

Artigo "SQLite no Android"

Olá povo,

A revista Web Mobile Magazine edição 34, trás uma matéria sobre SQLite no Android. Ela foi escrita por mim, em parceria com os meus colegas Edilson Mendes(Ronaldo!) e Breno Menezes (Cid).

Nesse artigo apresentamos, através de um exemplo completo, a persistência de dados na plataforma Android com SQLite, que traz para o mundo mobile a experiência de bancos de dados relacionais tão comuns nos ambientes web e desktop.

Espero que vocês gostem.

4br4ç05,
nglauber

segunda-feira, 7 de junho de 2010

Acessando banco de dados MySQL com JDBC

Olá povo,

Acabei de terminar de ministrar uma turma de Java Programmer na Especializa Treinamentos, e escutei a reclamação de um dos alunos dizendo que eu só postava assuntos sobre Android. Então resolví dá um espaço pro Java básico aqui no blog. Vou começar mostrando como acessar um banco de dados MySQL com a JDBC API.

Apesar dos desenvolvedores contarem com frameworks JPA como Hibernate, que facilitam o acesso de aplicações Java a SGDB (Sistemas Gerenciadores de Banco de Dados), é bom saber o que acontece por trás dos bastidores desses frameworks.

Normalmente, isola-se a parte de acesso aos dados das telas da aplicação através de uma classe que recebe objetos de negócio e as persiste no banco de dados para posterior recuperação. Esse processo é o que chamamos de "mapeamento objeto-relacional".

Apresentaremos a classe realiza as operações de cadastrar, recuperar, atualizar e excluir registros em um banco de dados. Essa classe não se preocupa com qual interface gráfica o desenvolvedor está trabalhando: swing, jsp, applet, etc.

Mãos a obra! A API JDBC provê um conjunto de interfaces que permite um acesso uniforme a banco de dados em Java, deixando a cargo de cada fabricante implementar o acesso ao seu banco de dados da maneira que achar mais conveniente. Essas interfaces estão no pacote java.sql.*. Dessa forma, a primeira coisa que precisamo é baixar o banco de dados do banco de dados que você utilizará. Para baixar o Driver para MySQL clique aqui.

Uma vez de posse do seu JAR, adicione ao classpath do seu projeto e escreva o código abaixo:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class RepositorioPessoaDB  {

// Método que estabele a conexão com o banco de dados
private Connection getConnection() throws
  ClassNotFoundException, InstantiationException,
  IllegalAccessException, SQLException {
  
 String userName = "root";
 String password = "root";
 String url = "jdbc:mysql://localhost/pessoasDB";
 String driver = "com.mysql.jdbc.Driver";

 // Registra o driver do banco de dados
 Class.forName(driver).newInstance();

 // Estabele a conexão passando url, usuário e senha
 Connection conn = DriverManager.getConnection(
   url, userName, password);

 return conn;
}

// Método que inclui um objeto pessoa
// no banco de dados
public void incluir(Pessoa pessoa) {
 Connection conn = null;
 try {
   // Obtém a conexão
   conn = getConnection();

   // Declara e prepara a instrução
   // pra ser executada no BD
   PreparedStatement s;
   s = conn.prepareStatement(
     "INSERT INTO Pessoa (nome, telefone)"+
     "VALUES(?, ?)");

   // Cada interrogação será substituída
   // pelos atributos do objeto
   s.setString(1,  pessoa.getNome());
   s.setString(2,  pessoa.getTelefone());

   int count = s.executeUpdate();
   s.close();

   System.out.println(count +
     " Linhas foram inseridas");

 } catch (Exception e) {
   throw new RuntimeException(
     "Falha ao inserir registro.");

 } finally {
   // Fecha a conexão
   if (conn != null) {
     try {
       conn.close();
       System.out.println("Conexão encerrada.");
     } catch (Exception e) {}
   }
 }
}

// Método que exclui uma pessoa do
// banco de dados baseado no código
public void excluir(int codigoPessoa) {
 Connection conn = null;
 try {
   conn = getConnection();
     
   PreparedStatement s;
   s = conn.prepareStatement(
     "DELETE FROM Pessoa WHERE codigo = ?");
   s.setInt(1, codigoPessoa);

   int count = s.executeUpdate();
   s.close();

   System.out.println(
     count + " linhas foram excluídas");
  
 } catch (Exception e) {
   throw new RuntimeException(
     "Falha ao excluir registro.");

 } finally {
   if (conn != null) {
     try {
       conn.close();
       System.out.println("Conexão encerrada.");
     } catch (Exception e) {}
   }
 }
}

// Método que atualiza as informações
// de uma pessoa no banco
public void alterar(Pessoa pessoa) {
 Connection conn = null;
 try {
   conn = getConnection();

   PreparedStatement s;
   s = conn.prepareStatement(
     "UPDATE Pessoa SET nome = ?, telefone = ? "+
     "WHERE codigo = ?");

   s.setString(1, pessoa.getNome());
   s.setString(2, pessoa.getTelefone());
   s.setInt(3, pessoa.getCodigo());

   int count = s.executeUpdate();
   s.close();
   System.out.println(
     count + " linhas atualizadas");

 } catch (Exception e) {
   throw new RuntimeException(
     "Falha ao alterar");

 } finally {
   if (conn != null) {
     try {
       conn.close();
       System.out.println(
         "Conexão com o banco foi finalizada.");
     } catch (Exception e) { }
   }
 }
}

// Método que retorna uma lista de objetos
// pessoa vindos do banco de dados
public List<Pessoa> pesquisar(String nome) {

 ArrayList<Pessoa> lista =
   new ArrayList<Pessoa>();

 Connection conn = null;
 try {
   conn = getConnection();

   System.out.println("Conexão estabelecida!");
   PreparedStatement s = conn.prepareStatement(
     "SELECT * FROM PessoaWHERE nome LIKE ?");
   s.setString(1, "%"+ nome +"%");
   s.executeQuery();

   // Resultset representa o resultado da
   // busca no banco de dados
   ResultSet rs = s.getResultSet();

   // Percorrendo o resultado da busca
   int count = 0;
   while (rs.next()) {
     // Criando e preenchendo objeto pessoa
     Pessoa pessoa = new Pessoa();
     pessoa.setCodigo( rs.getInt("codigo") );
     pessoa.setNome( rs.getString("nome"));
     pessoa.setTelefone( rs.getString("telefone"));

     // adicionando objeto à lista a ser retornada
     lista.add(pessoa);

     ++count;
   }
   rs.close();
   s.close();

   System.out.println(
     count + " linhas recuperadas");

 } catch (Exception e) {
   System.err.println("Falha ao conectar.");
   e.printStackTrace();

 } finally {
   if (conn != null) {
     try {
       conn.close();
       System.out.println("Conexão finalizada");
     } catch (Exception e) {}
   }
 }

 return lista;
}
}


A entidade de negócio que utilizamos é a classe Pessoa que tem os seguintes atributos: código, nome e telefone. Ela é uma classe simples com GETs e SETs, por isso não mostraremos ela aqui.

O código está comentado, mas a explicação do código é bem simples. Para todas as operações uma conexão é estabelecida para daí ser feita a solicitação para o banco. Para as operações de incluir, alterar e excluir é feita uma chamada para o método executeUpdate() em um objeto do tipo PreparedStatement. Esse objeto representa a solicitação que é feita ao banco de dados. Já para a busca de registros é utilizado o método executeQuery(). Após a busca, obtemos um objeto ResultSet que armazena as informações do banco de dados. Um vez estando com o resultado da busca, preenchemos uma lista de objetos Pessoa.

Vejam que com essa classe isolamos o acesso ao banco de dados em uma única classe. Cabe a você agora implementar a interface gráfica de sua preferência.

Qualquer dúvida, mandem seus comentários.

4br4ç05,
nglauber

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