SQLite – Banco de dados no Android!

É muito raro encontrarmos um aplicativo para dispositivos móveis que não façam uso de qualquer forma de persistência de dados. Isso acontece porque todas as vezes que o aplicativo é fechado, os dados que la estavam iriam ser perdidos caso não fossem persistidos.
Como solução para esse problema, o Android oferece suporte nativo ao SQLite. O SQLite, é uma base de dados leve e poderosa, que vem ficando cada vez mais popular na comunidade de TI. Principalmente entre os desenvolvedores de software para Android ou iOS uma vez que o SQLite esta presente nas duas plataformas.
É importante ressaltar que o SQLite não é a única forma de persistência que o desenvolvedor pode adotar em um projeto Android. Existem outras tecnologias como por exemplo: o DB4O ou o NeoDatis que são banco de dados Orientados a Objetos. Essas tecnologias representam uma quebra de paradigma e embora seu conceito já esteja amadurecido é considerado “novo” na história de TI. Por tanto, vale a pena estudar essas novas estratégias de persistência que estão aparecendo no mercado. Mas fique atento, pois elas estão em desenvolvimento, surpresas ruins podem aparecer.
Além de outras abordagens, hoje em dia existem diversos frameworks de persistência que utilizam o SQLite internamente. Como por exemplo o ORMLite e o GreenDAO ambos visam encapsular o SQLite e oferecer funções simples de recuperação, deleção, edição e persistência dos dados. Após utiliza-las percebi que ainda tem muitos pontos para serem melhorados. Devido a isso, nesse artigo abordaremos o SQLite utilizando apenas a API nativa do Android para manipula-lo.
Agora vamos a prática.
Para exemplificar o uso do SQLite vamos criar um Cadastro de veículos onde o usuário conseguirá salvar, editar, recuperar e deletar um veículo.
A primeira classe que precisaremos em nosso projeto é a própria entidade Veiculo, que será manipulada pela nossa base de dados. O código dessa entidade se encontra logo abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Veiculo {
 
    private int id;
    private String marca;
    private String modelo;
    private String placa;
 
    public Veiculo() {
 
    }
     
    public Veiculo(int id, String marca, String modelo, String placa) {
        super();
        this.id = id;
        this.marca = marca;
        this.modelo = modelo;
        this.placa = placa;
    }
 
 
 
    public String getMarca() {
        return marca;
    }
 
    public void setMarca(String marca) {
        this.marca = marca;
    }
 
    public String getModelo() {
        return modelo;
    }
 
    public void setModelo(String modelo) {
        this.modelo = modelo;
    }
 
    public String getPlaca() {
        return placa;
    }
 
    public void setPlaca(String placa) {
        this.placa = placa;
    }
     
    public int getId() {
        return id;
    }
     
    public void setId(int id) {
        this.id = id;
    }
     
    @Override
    public String toString() {
        return placa + " " +  modelo + " " + marca;
    }
}
Veja que se trata de uma classe POJO bem simples com apenas 3 atributos do tipo String (Placa, Marca e Modelo) e um atributo int que será o identificador único (Primary Key da nossa tabela). Serão esses dados que serão armazenados pela nossa base de dados.
Agora vamos criar uma classe de infraestrutura que terá os métodos que irão manipular esses dados, dentre eles podemos destacar:
salvar(Veiculo veiculo), editar(Veiculo veiculo), recuperarTodos() e deletar(Veiculo veiculo).
A classe que terá todos esses métodos se chamara VeiculoDAO, pois seguirá o padrão de projeto Data Access Object (DAO) para saber mais sobre esse padrão clique neste link.
A classe VeiculoDAO pode ser vista logo abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public class VeiculoDAO {
 
 
    public static final String NOME_TABELA = "Veiculo";
    public static final String COLUNA_ID = "id";
    public static final String COLUNA_MARCA = "marca";
    public static final String COLUNA_MODELO = "modelo";
    public static final String COLUNA_PLACA = "placa";
 
 
    public static final String SCRIPT_CRIACAO_TABELA_VEICULOS = "CREATE TABLE " + NOME_TABELA + "("
            + COLUNA_ID + " INTEGER PRIMARY KEY," + COLUNA_MARCA + " TEXT," + COLUNA_PLACA + " TEXT,"
            + COLUNA_MODELO + " TEXT" + ")";
 
    public static final String SCRIPT_DELECAO_TABELA =  "DROP TABLE IF EXISTS " + NOME_TABELA;
 
 
    private SQLiteDatabase dataBase = null;
 
 
    private static VeiculoDAO instance;
     
    public static VeiculoDAO getInstance(Context context) {
        if(instance == null)
            instance = new VeiculoDAO(context);
        return instance;
    }
 
    private VeiculoDAO(Context context) {
        PersistenceHelper persistenceHelper = PersistenceHelper.getInstance(context);
        dataBase = persistenceHelper.getWritableDatabase();
    }
 
    public void salvar(Veiculo veiculo) {
        ContentValues values = gerarContentValeuesVeiculo(veiculo);
        dataBase.insert(NOME_TABELA, null, values);
    }
 
    public List<Veiculo> recuperarTodos() {
        String queryReturnAll = "SELECT * FROM " + NOME_TABELA;
        Cursor cursor = dataBase.rawQuery(queryReturnAll, null);
        List<Veiculo> veiculos = construirVeiculoPorCursor(cursor);
 
        return veiculos;
    }
 
    public void deletar(Veiculo veiculo) {
 
        String[] valoresParaSubstituir = {
                String.valueOf(veiculo.getId())
        };
 
        dataBase.delete(NOME_TABELA, COLUNA_ID + " =  ?", valoresParaSubstituir);
    }
 
    public void editar(Veiculo veiculo) {
        ContentValues valores = gerarContentValeuesVeiculo(veiculo);
 
        String[] valoresParaSubstituir = {
                String.valueOf(veiculo.getId())
        };
 
        dataBase.update(NOME_TABELA, valores, COLUNA_ID + " = ?", valoresParaSubstituir);
    }
 
    public void fecharConexao() {
        if(dataBase != null && dataBase.isOpen())
            dataBase.close();
    }
 
 
    private List<Veiculo> construirVeiculoPorCursor(Cursor cursor) {
        List<Veiculo> veiculos = new ArrayList<Veiculo>();
        if(cursor == null)
            return veiculos;
         
        try {
 
            if (cursor.moveToFirst()) {
                do {
 
                    int indexID = cursor.getColumnIndex(COLUNA_ID);
                    int indexMarca = cursor.getColumnIndex(COLUNA_MARCA);
                    int indexModelo = cursor.getColumnIndex(COLUNA_MODELO);
                    int indexPlaca = cursor.getColumnIndex(COLUNA_PLACA);
 
                    int id = cursor.getInt(indexID);
                    String marca = cursor.getString(indexMarca);
                    String modelo = cursor.getString(indexModelo);
                    String placa = cursor.getString(indexPlaca);
 
                    Veiculo veiculo = new Veiculo(id, marca, modelo, placa);
 
                    veiculos.add(veiculo);
 
                } while (cursor.moveToNext());
            }
             
        } finally {
            cursor.close();
        }
        return veiculos;
    }
 
    private ContentValues gerarContentValeuesVeiculo(Veiculo veiculo) {
        ContentValues values = new ContentValues();
        values.put(COLUNA_MARCA, veiculo.getMarca());
        values.put(COLUNA_MODELO, veiculo.getModelo());
        values.put(COLUNA_PLACA, veiculo.getPlaca());
 
        return values;
    }
}
Classe Persistence Helper, responsável por manter o versionamento da base de dados. Além de ser a classe responsável por criar uma referência do objeto SQLiteDatabase.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class PersistenceHelper extends SQLiteOpenHelper {
 
    public static final String NOME_BANCO =  "ExemploVeiculo";
    public static final int VERSAO =  1;
     
    private static PersistenceHelper instance;
     
    private PersistenceHelper(Context context) {
        super(context, NOME_BANCO, null, VERSAO);
    }
     
    public static PersistenceHelper getInstance(Context context) {
        if(instance == null)
            instance = new PersistenceHelper(context);
         
        return instance;
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(VeiculoDAO.SCRIPT_CRIACAO_TABELA_VEICULOS);
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(VeiculoDAO.SCRIPT_DELECAO_TABELA);
        onCreate(db);
    }
 
}
Logo no começo da classe podemos notar a presença das constantes que representam o nome da tabela e as suas colunas.
Estão presentes ai pois precisaremos dessas informações em vários pontos.
Também podemos destacar outras duas constantes que como o próprio nome indicam representam o script de criação de alteração da base dados.
Veja que o script de criação irá criar a tabela de veículo enquanto o script de alteração irá deleta-la. Esses scripts serão executados de acordo com a versão da base de dados. Caso a versão seja 1 assim que o aplicativo for iniciado pela primeira vez o script de criação será acionado. Caso o desenvolvedor mude a versão da base de dados (em função de alguma mudança, provavelmente na entidade que será persistida) o script de alteração será acionado, a intenção é que ele delete a tabela antiga para em seguida chamar o script de criação com as mudanças já realizadas.
Este versionamento que mencionamos e outros detalhes da criação da base de dados da sua aplicação ficam a cargo da nossa classe PersistenceHelper esta classe por sua vez herda funcionalidades da classe SQLiteOpenHelper presente na API do Android.
Ao extender a classe SQLiteOpenHelper, somos forçados a implementar um construtor e dois métodos abstratos (onCreate e onUpdate) conforme foi explicado anteriormente esses métodos serão acionados caso haja alteração na versão da base de dados ou em caso de criação da mesma. Podemos ver a implementação desses métodos na listagem referente a classe PersistenceHelper que esta logo abaixo do código referente ao VeiculoDAO.
É importante ressaltar que o construtor da classe PersistenceHelper nos obriga a informar, um Context (uma classe do Android que esta presente nas Activitys, Services e outras classes primórdias da tecnologia), o nome do banco de dados, a versão, e um CursorFactory (Esse objeto é utilizado quando queremos fazer uso de um tipo diferente de Cursor, como não é o nosso caso passamos null) – O objeto Cursor também será explicado mais a frente nesse artigo.
A classe PersistenceHelper não cuidará apenas da criação e possíveis alterações da base. É através dela que conseguiremos construir um objeto da classe SQLiteDatabase, esse objeto é de fundamental importância para a manipulação de dados no SQLite. É nessa classe que esta contido a maioria dos métodos de manuseamento de registros na base de dados como:
insert, update, delete, rawQuery, execSql
Vamos as explicações de cada método:
Como o próprio nome sugere, o método insert é o comando para inserir um novo registro na base de dados.
O método update, é responsável pela alteração de registros e o método delete por sua vez deletara os mesmos.
O método rawQuery, recebe como paramêtro uma String que representará uma consulta na base de dados(SELECT), esse método retorna um objeto do tipo Cursor.
O Cursor, é o objeto que será encarregado de armazenar o retorno de uma determinada consulta. Então sempre que realizar um Select na tabela, o resultado desta consulta será representado pelo Cursor. Logo, o desenvolvedor terá que desenvolver uma função para pegar esses dados do Cursor e construir um Objeto de negócio a partir dele.
É exatamente o que fazemos no método construirVeiculoPorCursor, da classe VeiculoDAO.
Por sua vez, o método execSQL também recebe como paramêtro uma String que representará uma ação na base de dados. No entanto, não retornará valor algum. Isso implica que este método não é ideal para realizar consultas e sim ações tais como Insert ou Update.
Por fim, não podemos esquecer do método close. Este método, deve ser chamado quando o banco de dados não for mais utilizado. E serve para liberar a memória destinada ao mesmo. Aconselha-se o seu uso junto do método onDestroy do ciclo de vida de uma Activity.
Agora que temos a classe Veiculo que representa os dados que serão armazenados, e a classe VeiculoDAO que sabe como armazenar um Veiculo. Nos resta apenas testar toda essa estrutura.
Para isso, vamos criar um projeto de Teste do nosso aplicativo. Não ha mistério na criação de um projeto de Test no eclipse. No menu superior pressione o item File em seguida clique em New e posteriormente Other. Um Wizard aparecerá, procure e selecione a opção Android Test Project e clique no botão Next. Aparecerá uma tela onde você deve informar o nome do projeto, chame o de VeiculoTest e pressione Next novamente. Em seguida aparecerá uma lista de todos os projetos disponiveis no Workspace do seu eclipse. Selecione o projeto que deseja testar e pressione o botão Finish.
Agora vamos criar a classe TestCRUDVeiculo dentro desse projeto, essa classe extenderár funcionalidades da classe AndroidTestCase e terá um método que chamaos de testCRUD();
Este método testara todas as principais funcionalidades da nossa classe VeiculoDAO (Salvar, delatar, alterar e recuperar).
É importante ressaltar que o ideal é que seja criado um método para testar cada ação da classe VeiculoDAO. No entanto creio que desse modo fica mais fácil para realizar uma demonstração.
Confira a classe de teste logo abaixo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TestCRUDVeiculo extends AndroidTestCase {
 
    public void testCRUD() {
         
        Veiculo veiculo = new Veiculo(0, "Fiat", "Palio", "1666");
        VeiculoDAO veiculoDAO =  VeiculoDAO.getInstance(getContext());
         
        veiculoDAO.salvar(veiculo);
         
        List<Veiculo> veiculosNaBase = veiculoDAO.recuperarTodos();
        assertFalse(veiculosNaBase.isEmpty());
         
        Veiculo veiculoRecuperado = veiculosNaBase.get(0);
        veiculoRecuperado.setModelo("Stilo");
         
        veiculoDAO.editar(veiculoRecuperado);
         
        Veiculo veiculoEditado = veiculoDAO.recuperarTodos().get(0);
         
        assertSame(veiculoRecuperado.getId(), veiculoEditado.getId());
        assertNotSame(veiculo.getModelo(), veiculoEditado.getModelo());
         
        veiculoDAO.deletar(veiculoEditado);
         
        assertTrue(veiculoDAO.recuperarTodos().isEmpty());
         
        veiculoDAO.fecharConexao();
         
    }
 
}
Após a construção do projeto, clique com o botão direito do mouse sobre ele e selecione a opção Run Android Junit Test. E pronto, o teste será executado.
Agora que temos uma classe de teste e o nosso projeto esta bem estruturado, por que não concluílo criando uma interface gráfica, representando uma tela de cadastro e uma lista de itens para que o aplicativo “tome vida” de vez. Bom, essa tarefa eu deixo como desafio para o leitor.
Até o próximo post!

Fonte: AndroidDevBR

Comentários

Postagens mais visitadas deste blog

Rails CanCan

Meus insights mais valiosos sobre criptomoedas para 2018 e além

Como pegar a senha do Whatsapp de um Android ou Iphone