SQLite – Banco de dados no Android!

Para fazer download do código apresentando neste artigo clique no link abaixo:
https://github.com/leonvian/cadastroVeiculo

Introdução

Já imaginou se você tivesse que começar aquele seu jogo favorito do início todas as vezes que desligar o celular.
Ou tivesse que se cadastrar novamente no facebook todas as vezes que entrasse neste aplicativo.

Seria muito ruim não é mesmo!?

É por isso que a persistência de dados é um pilar muito importante em programação para dispositivos móveis. Sem isso as funcionalidades acima não seriam alcançadas assim como diversas outras.

Para atender a necessidade de persistir dados o Android oferece suporte nativo ao banco de dados 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 que trabalham com Android e/ou iOS uma vez que as duas tecnologias possuem suporte nativo para o SQLite.

É 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 que utilizam o paradigma da Programação Orientados a Objetos e que também conseguem atender bem a necessidade de persistência.

Neste artigo trabalharemos com o SQLite, mas vale a pena ficar por dentro dessas outras abordagens por serem muito interessantes.

Direto para a prática.

A partir de agora exemplificaremos o uso do SQLite vamos criar um Cadastro de veículos onde o usuário conseguirá salvar, editar, deletar e recuperar um veículo.
Para atender esse cenário, a primeira classe que precisaremos é a entidade Veiculo, que representará os dados que serão manipulados pelo nosso banco de dados.

O código dessa entidade se encontra logo abaixo:

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 de nome id que será o identificador único de cada registro no banco de dados (Primary Key da nossa tabela).

Agora que temos a classe que representa os dados que serão persistidos, vamos criar uma classe que sabe como persistir um Veiculo.

Para isso utilizaremos o padrão de projeto DAO (Data Access Object). Este padrão de projeto consiste no encapsulamento da parte de persistência de uma determinada entidade de negócio (no nosso exemplo esta entidade de negócio esta representada na classe Veiculo).
Para saber mais sobre esse padrão clique neste link.

Basicamente, criaremos uma classe que se chamará VeiculoDAO, e terá métodos que acessam o banco de dados, dentre esses métodos podemos destacar:

salvar(Veiculo veiculo), editar(Veiculo veiculo), deletar(Veiculo veiculo) e recuperarTodos().

Acompanhe o código da classe VeiculoDAO logo abaixo:


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;
	}
}

Antes de iniciar as explicações da classe VeiculoDAO, podemos identificar que ela faz referência a uma classe chamada PersistenceHelper.

Mas o que é essa tal de PersistenceHelper?

A classe PersistenceHelper é uma classe criada por nós que herda funcionalidades da classe SQLiteOpenHelper da API do Android e é responsável pela criação e o versionamento do banco de dados.
Então é nessa classe que iremos definir o nome do banco de dados, a versão do banco de dados e todas as tabelas que o banco terá.

Pra que serve essa versão?

Para que você consiga evoluir o banco de dados, quando o seu aplicativo sofrer alterações.

Imaginando um cenário onde você criou um aplicativo que tenha um cadastro de Veiculo. Depois de duas semanas nasceu a necessidade de colocar mais um cadastro agora de Pessoa.

Os usuários da primeira versão irão precisar sofrer uma modificação no banco de dados uma vez que uma nova tabela terá que ser criada.

Para que ele consiga criar essa nova tabela sem perder os veículos já cadastrados no momento da re-criação do banco com a tabela Pessoa ele muda a versão do banco de dados e faz uma lógica para salvar os veículos antes de deletar e criar novamente o banco.

Logo abaixo o código da PersistenceHelper:


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 como constantes públicas pois precisaremos dessas informações em vários pontos em nosso sistema.

Também podemos destacar outras duas constantes que representam o script de criação e de alteração da base dados.

O script de criação irá criar a tabela de veículo enquanto o script de alteração irá deleta-la.
Vamos identificar onde esses scripts serão acionados.

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 de código referente a classe PersistenceHelper que esta logo acima.

No método onCreate, executaremos o script de criação do banco de dados uma vez que o Android identificou que aquele dispositivo não possui versões anteriores do banco de dados do nosso aplicativo.
Já no método onUpdate executaremos o script de alteração, pois o Android identificou uma versão anterior do banco de dados do aplicativo e precisará apenas atualiza-lo.

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 para 2 (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.

Parâmetros exigidos pelo construtor do SQLiteOpenHelper

É 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 primordias dessa 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 outra função do SQLiteOpenHelper

A classe PersistenceHelper não cuidará apenas da criação e possíveis alterações da base. É através dela que conseguiremos recuperar 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

Veja na classe VeiculoDAO, que fazemos muito uso desses métodos.

Vamos as explicações de cada método utilizado

Começaremos pelo método insert é o comando para inserir um novo registro na base de dados.
Recebe como parâmetro o nome da tabela que o registro será inserido e os dados que serão inseridos. Esses dados serão representados pelo ContentValues falaremos mais dessa estrutura futuramente neste artigo, no entanto basta entender que o ContentValues é uma estrutura de chave e valor, onde a chave representa determinada coluna de uma tabela e o valor é o registro que deverá ser inserido nesta coluna.

O método update, é responsável pela alteração de registros.
Recebe como parâmetro o nome da tabela do registro que iremos editar.
A condição de identificação de registro, ou seja, uma query para sabermos quais registos serão editados.
E como último parâmetro novamente um ContentValues que representam os dados atualizados.

O método delete por sua vez deletará registros.
E receberá os mesmos parâmetros do método update, com excessão do ContentValues pois estamos deletando e não precisamos de registro algum para salvar ou editar.

O método rawQuery, recebe como parâmetro uma String que representará uma consulta na base de dados (um 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 uma consulta na tabela, o resultado desta consulta será representado pelo Cursor.

Por isso, 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. Recebemos um cursor como resultado de uma consulta na tabela veiculo e construímos um objeto Veículo a partir dele.

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.

Ou seja, se os métodos insert, update ou delete não te agradaram, você poderá utilizar esse método para escrever o você mesmo o SQL que deseja executar.

OBS: A classe DataBaseHelper faz uso deste método para criar nossas tabelas.

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.

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. Vamos aos passos:

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, iremos chama-lo de VeiculoTest após o preenchimento do seu nome pressione Next novamente.
Em seguida aparecerá uma lista de todos os projetos disponiveis no Workspace do seu eclipse.
Selecione o projeto que deseja testar (no nosso caso o Cadastro de Veiculo) e pressione o botão Finish.

Agora vamos criar a classe TestCRUDVeiculo dentro desse projeto. Essa classe irá extender funcionalidades da classe AndroidTestCase e terá um método que chamaremos 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. Futuramente neste mesmo artigo melhoraremos nosso código de teste.

Confira a classe de teste logo abaixo.

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.

E se tivermos que adicionar uma segunda entidade!?

Por menor que seja a aplicação é raro encontrar um cenário onde o aplicativo terá que persistir somente uma entidade de negócio, ou seja, teria apenas uma tabela no banco de dados.

Nesse nosso aplicativo já aprendemos como persistir uma entidade de negócio, o Veículo. Agora vamos adicionar uma segunda entidade para deixar o exemplo mais completo, esta entidade chamaremos de Pessoa, uma vez que todo Veículo esta vinculado a uma Pessoa.

O código da nossa entidade Pessoa ficará assim:

public class Pessoa implements EntidadePersistivel {
	
	private int id;
	private String nome;
	private int idade;
	
	public Pessoa() {
	}
	 
	public Pessoa(int id, String nome, int idade) {
		super();
		this.id = id;
		this.nome = nome;
		this.idade = idade;
	}
 
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public int getIdade() {
		return idade;
	}

	public void setIdade(int idade) {
		this.idade = idade;
	}

}

Observando o código podemos constatar que se trata de um POJO assim como a nossa entidade Veiculo, possuindo apenas três atributos ID, Idade, e nome. No entanto podemos reparar em uma mudança, a classe Pessoa esta implementando a interface EntidadePersistivel o que seria isso!?

Antes de persistirmos a entidade Pessoa, devemos preparar a arquitetura do sistema para suportar a persistência múltiplas entidades. A interface EntidadePersistivel é uma dessas mudanças.

Fazemos isso para evitar a duplicação de código. Se pegarmos o código do VeiculoDAO copiarmos e colarmos em uma nova classe, mudando o nome pra PessoaDAO e persistindo um objeto do tipo Pessoa ao invés de Veiculo nossa mudança estaria praticamente finalizada não é mesmo?

Mas muito código duplicado estaria presente no sistema e não conseguiríamos compartilhar funcionalidades entre os DAO’s.

Daí a necessidade de alguns ajustes na nossa camada de persistência, a primeira delas é a inserção da interface EntidadePersistivel vejamos o código desta interface logo abaixo:

public interface EntidadePersistivel {
	
	public int getId();
	public void setId(int id);

}

Esta interface obriga a implementação dos métodos setId e getId. O que na prática implica na criação de um atributo ID.
Todas as entidades que serão persistidas no nosso sistema (Veiculo e Pessoa) deverão implementar esta interface.

Mais a frente neste artigo veremos essa interface sendo utilizada na prática.

Criação do DAOBasico

A mudança mais crucial que fizemos foi criar a classe abstrata que chamamos de DAOBasico.

Sabemos que tanto o DAO de Veiculo quanto o DAO de Pessoa, devem saber como salvar, deletar, editar e recuperar objetos de sua respectiva entidade.

A única coisa que muda é qual entidade ela salvará, ou seja, os dados que ela irá manipular, uma será a entidade Pessoa e a outra a entidade Veículo.

O DAOBasico usa o conceito de Generics, isso implica que ele esperará um TIPO quando alguma classe for herdar funcionalidades dele.
Com essa estratégia poderemos criar métodos genéricos como os abaixo:

public void salvar(T entidade);

public void deletar(T entidade);

public void editar(T entidade);

public List<T> recuperarTudo(String consulta);

Onde o T será substituído pela entidade alvo seja ela Veiculo ou Pessoa.
No caso do DAOBasico colocamos uma restrição no nosso tipo genérico. Definimos que O tipo (T) seria qualquer classe que implemente a interface EntidadePersistivel.

Fazemos isso acrescentando o seguinte código após a declaração do nome da classe:

<T extends EntidadePersistivel>

Vejamos o código completo do nosso DAOBasico para um melhor entendimento:

public abstract class DAOBasico<T extends EntidadePersistivel> {

	protected SQLiteDatabase dataBase = null;

	public DAOBasico(Context context) {
		DataBaseHelper persistenceHelper = DataBaseHelper.getInstance(context);
		dataBase = persistenceHelper.getWritableDatabase();
	}

	public abstract String getNomeColunaPrimaryKey();
	public abstract String getNomeTabela();

	public abstract ContentValues entidadeParacontentValues(T entidade);
	public abstract T contentValuesParaEntidade(ContentValues contentValues); 


	public void salvar(T entidade) {
		ContentValues values = entidadeParacontentValues(entidade);
		dataBase.insert(getNomeTabela(), null, values);
	}

	public void deletar(T t) {

		String[] valoresParaSubstituir = {
				String.valueOf(t.getId())
		};

		dataBase.delete(getNomeTabela(), getNomeColunaPrimaryKey() + " =  ?", valoresParaSubstituir);
	}

	public void deletarTodos() {
		dataBase.execSQL("DELETE FROM " + getNomeTabela());
	}

	public void editar(T t) {
		ContentValues valores = entidadeParacontentValues(t);

		String[] valoresParaSubstituir = {
				String.valueOf(t.getId())
		};

		dataBase.update(getNomeTabela(), valores, getNomeColunaPrimaryKey() + " = ?", valoresParaSubstituir);
	}

	public List<T> recuperarTodos() {
		String queryReturnAll = "SELECT * FROM " + getNomeTabela();
		List<T> result = recuperarPorQuery(queryReturnAll);

		return result; 

	} 

	public T recuperarPorID(int id) {
		String queryOne = "SELECT * FROM " + getNomeTabela() + " where " + getNomeColunaPrimaryKey() + " = " + id;
		List<T> result = recuperarPorQuery(queryOne);
		if(result.isEmpty()) {
			return null;
		} else {
			return result.get(0);	
		}
	}

	public List<T> recuperarPorQuery(String query) {

		Cursor cursor = dataBase.rawQuery(query, null);

		List<T> result = new ArrayList<T>();
		if (cursor.moveToFirst()) {
			do { 
				ContentValues contentValues = new ContentValues();
				DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
				T t = contentValuesParaEntidade(contentValues);
				result.add(t);
			} while (cursor.moveToNext());
		}
		return result; 

	}

	public void fecharConexao() {
		if(dataBase != null && dataBase.isOpen())
			dataBase.close(); 
	}

}

Veja que quem herdar desta classe já terá implementados os métodos salvar, editar, deletar e recuperarTodos.

Porém será obrigado a implementar os métodos abstratos:


public abstract String getNomeColunaPrimaryKey();
: Apesar das colunas PrimaryKey tanto do Veiculo quanto da entidade Pessoa se chamarem id. Pode ser que alguma entidade no futuro tenha um nome diferente para a sua coluna Primary Key e por isso esse método existe.

public abstract String getNomeTabela(); : Este método é pedido como parâmetro nos métodos de insert, update, delete, e query. E é uma das informações que variam entre os DAO’s, o DAO da entidade Pessoa possui um nome de tabela diferente do DAO da entidade Veículo.

public abstract ContentValues entidadeParacontentValues(T entidade); : Este método é utilizado na hora de salvar e editar um registro. Veja que ele recebe como parâmetro uma entidade e retorna um ContentValues. Conforme já foi falado, o ContentValues é uma estrutura de chave e valor, onde neste caso a chave representa a coluna da tabela e o valor representa o valor que será inserido nessa coluna.

Exemplo de utilização:
placa:gxw1666.
placa seria a coluna da tabela veículo
gxw1666 é o valor que vai ser armazenado nessa coluna.

public abstract T contentValuesParaEntidade(ContentValues contentValues); : Este método é utilizado na hora de recuperar dados, e é o inverso do método entidadeParacontentValues que explicamos mais acima.
Neste caso, receberemos como parâmetro um ContentValues e devemos transforma-lo em uma entidade.

Os métodos acima representam as únicas informações que realmente podem variar de uma implementacão de DAO para a outra, por isso são abstratos, cada implementação de DAO deve informar as suas particularidades.

Vejamos então como ficaram os nossos novos DAO’s que agora vão herdar funcionalidades do DAOBasico.

public class PessoaDAO  extends DAOBasico<Pessoa> {

	private static final String NOME_TABELA = "PESSOA";
	public static final String SCRIPT_CRIACAO_TABELA = "CREATE TABLE  PESSOA ( id INTEGER PRIMARY KEY autoincrement, nome TEXT,idade INTEGER)";
	public static final String SCRIPT_DELECAO_TABELA =  "DROP TABLE IF EXISTS " + NOME_TABELA;

	private static final String COLUNA_ID = "id";
	private static final String COLUMA_NOME = "nome";
	private static final String COLUNA_IDADE = "idade";

	public PessoaDAO(Context context) {
		super(context); 
	}

	@Override
	public String getNomeColunaPrimaryKey() {
		return COLUNA_ID;
	}

	@Override
	public String getNomeTabela() {
		return NOME_TABELA;
	}

	public ContentValues entidadeParacontentValues(Pessoa pessoa) {
		ContentValues contentValues = new ContentValues();
                 if(pessoa.getId() > 0) {
		   contentValues.put(COLUNA_ID, pessoa.getId());
                }
		contentValues.put(COLUNA_IDADE, pessoa.getIdade());
		contentValues.put(COLUMA_NOME, pessoa.getNome());
		return contentValues;
	}

	@Override
	public Pessoa contentValuesParaEntidade(ContentValues contentValues) {
		Pessoa pessoa = new Pessoa();
		pessoa.setId(contentValues.getAsInteger(COLUNA_ID));
		pessoa.setIdade(contentValues.getAsInteger(COLUNA_IDADE));
		pessoa.setNome(contentValues.getAsString(COLUMA_NOME));
		return pessoa;
	}

}

Repare que logo no início da classe usamos a palavra reservada extends para declarar a herança da classe DAOBasico. Logo em seguida passamos o tipo que iremos manipular no caso acima a entidade Pessoa.

OBS: Se você tiver dúvidas em utilizar a estrutura Generics do Java, talvez este artigo possa te ajudar.
http://www.tiexpert.net/programacao/java/generics.php
é bem importante saber essa estrutura, pois é utilizada amplamente.

Os métodos salvar,editar,deletar e recuperarTodos, estão devidamente implementados na classe mãe DAOBasico. Tivemos apenas que implementar os métodos abstratos que foram explicados acima.

Pare um segundo para contemplar tudo que foi feito.
Veja como a nossa estrutura ficou bem mais robusta e flexível do que a anterior criada para persistir somente uma entidade.
Se futuramente tivermos que acrescentar uma terceira entidade no nosso sistema, tal como Imovel, você já sabe os passos que deve fazer para persistir essa nova entidade?

Basta criarmos a entidade Imovel com o seus atributos e fazer com que ela implemente a interface EntidadePersistivel, posteriormente deve ser criado a classe ImovelDAO fazendo ela herdar da classe DAOBasico.
E pronto.

Para concluir vamos ver como ficou o novo DAO da entidade Veiculo:

public class VeiculoDAO extends DAOBasico<Veiculo> {

	public static final String NOME_TABELA = "Veiculo";
	public static final String COLUNA_ID = "id";
	public static final String COLUNA_ID_PESSOA = "id_pessoa";
	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 autoincrement,"
			+ COLUNA_ID_PESSOA + " INTEGER,"
			+ COLUNA_MARCA + " TEXT," 
			+ COLUNA_PLACA + " TEXT,"
			+ COLUNA_MODELO + " TEXT" 
			+ ")";

	public static final String SCRIPT_DELECAO_TABELA =  "DROP TABLE IF EXISTS " + NOME_TABELA;

	private static VeiculoDAO instance;

	public static VeiculoDAO getInstance(Context context) {
		if(instance == null)
			instance = new VeiculoDAO(context);
		return instance;
	}

	public VeiculoDAO(Context context) {
		super(context);
	}

	@Override
	public String getNomeTabela() {  
		return NOME_TABELA;
	}

	@Override
	public String getNomeColunaPrimaryKey() {
		return COLUNA_ID;
	}

	public ContentValues entidadeParacontentValues(Veiculo veiculo) {
		ContentValues values = new ContentValues();
		if(veiculo.getId() > 0) {
			values.put(COLUNA_ID, veiculo.getId());
		}
		values.put(COLUNA_ID_PESSOA, veiculo.getIdPessoa());
		values.put(COLUNA_MARCA, veiculo.getMarca());
		values.put(COLUNA_MODELO, veiculo.getModelo());
		values.put(COLUNA_PLACA, veiculo.getPlaca());

		return values;
	}


	@Override
	public Veiculo contentValuesParaEntidade(ContentValues contentValues) {
		Veiculo veiculo = new Veiculo();
		veiculo.setId(contentValues.getAsInteger(COLUNA_ID));
		veiculo.setIdPessoa(contentValues.getAsInteger(COLUNA_ID_PESSOA));
		veiculo.setMarca(contentValues.getAsString(COLUNA_MARCA));
		veiculo.setModelo(contentValues.getAsString(COLUNA_MODELO));
		veiculo.setPlaca(contentValues.getAsString(COLUNA_PLACA));
		return veiculo;
	}
}

Outra pequena mudança que fizemos foi colocar, os scripts de criação e deleção de uma determinada tabela em seus respectivos DAO’s. Anteriormente esses scripts ficavam na classe DataBaseHelper onde eles são utilizados, só que imaginando um sistema que contenha 10 entidades, imagina o tamanho que ficaria a classe DataBaseHelper e o quão difícil seria dar manutenção na mesma.
Por isso a mudança.

Por falar nisso vamos ver como ficou o novo código da classe DataBaseHelper:

public class DataBaseHelper extends SQLiteOpenHelper {
 

	private static final String NOME_BANCO_DADOS = "CadastroVeiculo";
	private static final int VERSAO_BANCO_DADOS = 1;

	private static DataBaseHelper instance;

	public static DataBaseHelper getInstance(Context context) {
		if(instance == null)
			instance = new DataBaseHelper(context);

		return instance;
	}

	public DataBaseHelper(Context context) {
		super(context, NOME_BANCO_DADOS, null, VERSAO_BANCO_DADOS);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(PessoaDAO.SCRIPT_CRIACAO_TABELA);
		db.execSQL(VeiculoDAO.SCRIPT_CRIACAO_TABELA_VEICULOS);
		Log.i("DATABASE", "CRIANDO TABELA");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.i("DATABASE", "ATUALIZANDO TABELA");
		db.execSQL(VeiculoDAO.SCRIPT_DELECAO_TABELA);
		db.execSQL(PessoaDAO.SCRIPT_DELECAO_TABELA);
		onCreate(db);
	}

}

Também tivemos uma mudança na entidade Veículo:

public class Veiculo implements EntidadePersistivel {
 
    private int id;
    private int idPessoa;
    private String marca;
    private String modelo;
    private String placa;
 
    public Veiculo() {
    }
     
    public Veiculo(int id, int idPessoa, String marca, String modelo, String placa) {
        super();
        this.id = id;
        this.marca = marca;
        this.modelo = modelo;
        this.placa = placa;
        this.idPessoa = idPessoa;
    }
 
 
    public int getIdPessoa() {
		return idPessoa;
	}
    
    public void setIdPessoa(int idPessoa) {
		this.idPessoa = idPessoa;
	}
 
    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;
    }
} 

Foi criado um novo atributo nesta entidade, o chamamos de idPessoa do tipo int.

É neste atributo ficará armazenado o ID de uma entidade Pessoa. Assim poderemos recuperarmos sempre que necessário uma pessoa Passando o idPessoa presente no veículo.

Vamos ver esse cenário funcionando e testar tudo que foi feito com os nossos testes de unidade.

Logo abaixo segue o código da classe que irá testar a classe PessoaDAO, o nome dela é PessoaDAOTest.

public class PessoaDAOTest extends AndroidTestCase {

	public void testSalvar() {
		PessoaDAO pessoaDAO = new PessoaDAO(getContext());
		
		int primeiroSize = pessoaDAO.recuperarTodos().size();
		
		Pessoa pessoa = criarPessoaExemplo();
		pessoaDAO.salvar(pessoa);
		
		assertTrue(pessoaDAO.recuperarTodos().size() > primeiroSize);
		pessoaDAO.fecharConexao();
	}
	
	
	public void testDeletar() {
		testSalvar();
		
		PessoaDAO pessoaDAO = new PessoaDAO(getContext());
		
		List<Pessoa> todasPessoas = pessoaDAO.recuperarTodos();
		assertNotNull(todasPessoas);
		assertFalse(todasPessoas.isEmpty());
		
		Pessoa pessoaRecuperada  = todasPessoas.get(0);
		assertNotNull(pessoaRecuperada);
		
		int primeiroSize = pessoaDAO.recuperarTodos().size();
		
		pessoaDAO.deletar(pessoaRecuperada);
		
		assertTrue(pessoaDAO.recuperarTodos().size() < primeiroSize);
		pessoaDAO.fecharConexao();
	}
	
	public void testRecuperar() {
		PessoaDAO pessoaDAO = new PessoaDAO(getContext());
		pessoaDAO.deletarTodos();
		
		Pessoa pessoa = criarPessoaExemplo();
		pessoaDAO.salvar(pessoa);
		
		List<Pessoa> todasPessoasRecuperadas = pessoaDAO.recuperarTodos();
		assertTrue(todasPessoasRecuperadas.size()  == 1);
		
		Pessoa pessoaRecuperada = todasPessoasRecuperadas.get(0);
		Pessoa pessoaCriada = criarPessoaExemplo();
		
		assertEquals(pessoaCriada.getNome(), pessoaRecuperada.getNome());
		assertEquals(pessoaCriada.getIdade(), pessoaRecuperada.getIdade());
		pessoaDAO.fecharConexao();
	}
	
	public Pessoa criarPessoaExemplo() {
		Pessoa pessoa = new Pessoa(0,"Leonardo",24);
		return pessoa;
	}

}

Veja que existe um método de teste para cada método da nossa classe PessoaDAO, fique a vontade pra criar outros métodos de teste achar necessário.

Agora só falta os testes da classe VeiculoDAO, para seguir o padrão chamamos a classe de teste de VeiculoDAOTest.

public class VeiculoDAOTest extends AndroidTestCase {
	
	public void testSalvar() {
		VeiculoDAO veiculoDAO = new VeiculoDAO(getContext());
		
		int primeiroSize = veiculoDAO.recuperarTodos().size();
		
		Veiculo veiculo = criarVeiculoExemplo(0);
		veiculoDAO.salvar(veiculo);
		
		assertTrue(veiculoDAO.recuperarTodos().size() > primeiroSize);
		veiculoDAO.fecharConexao();
	}
	
	
	public void testDeletar() {
		testSalvar();
		
		VeiculoDAO veiculoDAO = new VeiculoDAO(getContext());
		
		List<Veiculo> todasVeiculos = veiculoDAO.recuperarTodos();
		assertNotNull(todasVeiculos);
		assertFalse(todasVeiculos.isEmpty());
		
		Veiculo veiculoRecuperada  = todasVeiculos.get(0);
		assertNotNull(veiculoRecuperada);
		
		int primeiroSize = veiculoDAO.recuperarTodos().size();
		
		veiculoDAO.deletar(veiculoRecuperada);
		
		assertTrue(veiculoDAO.recuperarTodos().size() < primeiroSize);
		veiculoDAO.fecharConexao();
	}
	
	public void testRecuperar() {
		PessoaDAO pessoaDAO = new PessoaDAO(getContext());
		pessoaDAO.salvar(criarPessoaExemplo());
		Pessoa pessoa = pessoaDAO.recuperarTodos().get(0);
		
		VeiculoDAO veiculoDAO = new VeiculoDAO(getContext());
		veiculoDAO.deletarTodos();
		
		Veiculo veiculo = criarVeiculoExemplo(pessoa.getId());
		veiculoDAO.salvar(veiculo);
		
		List<Veiculo> todasVeiculosRecuperadas = veiculoDAO.recuperarTodos();
		assertTrue(todasVeiculosRecuperadas.size()  == 1);
		
		Veiculo veiculoRecuperada = todasVeiculosRecuperadas.get(0);
		
		Pessoa pessoaRecuperada = pessoaDAO.recuperarPorID(veiculoRecuperada.getIdPessoa());
	    Pessoa pessoaCriada = criarPessoaExemplo();
		
		assertEquals(pessoaCriada.getNome(), pessoaRecuperada.getNome());
		assertEquals(pessoaCriada.getIdade(), pessoaRecuperada.getIdade());
		
		Veiculo veiculoCriada = criarVeiculoExemplo(pessoa.getId());
		
		assertEquals(veiculoCriada.getModelo(), veiculoRecuperada.getModelo());
		assertEquals(veiculoCriada.getMarca(), veiculoRecuperada.getMarca());
		assertEquals(veiculoCriada.getIdPessoa(), veiculoRecuperada.getIdPessoa());
		veiculoDAO.fecharConexao();
	}
	
	public Veiculo criarVeiculoExemplo(int idPessoa) {
		Veiculo veiculo = new Veiculo(0, idPessoa, "Fiat", "Palio", "MNR4167");
		return veiculo;
	}
	
	public Pessoa criarPessoaExemplo() {
		Pessoa pessoa = new Pessoa(0,"Leonardo",24);
		return pessoa;
	}

}

Esta classe ficou mais extensa do que a classe PessoaDAO. Pois tivemos que estar o caso onde salvamos um Veiculo com um id Pessoa vinculado.
Depois recuperamos esse Veiculo da base e através do campo idPessoa recuperamos também uma Pessoa da base.

Todos os testes listados aqui simulam situações que iriam acontecer na mão de um cliente utilizando o seu aplicativo. Quanto mais testes de unidade, maior a qualidade do seu software, maior a confiança que um desenvolvedor tem de realizar manutenções nesse software e menor é a quantidade de horas gasta para achar e corrigir bugs.

A imagem abaixo mostra todos os testes exibidos aqui executados com sucesso.

Testes de unidade executados no eclipse.

Testes de unidade executados no eclipse.

Agora que temos uma classe de teste e o nosso projeto esta bem estruturado, por que não finalizamos ele completamente criando uma interface gráfica para que ele fique bem funcional.
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. =]

Observação final – Você não precisa fazer tudo na mão

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. Experimente-os e veja se eles conseguem atender as suas necessidades.

Até o próximo post!

, ,

  1. #1 by read more on Dezembro 14, 2019 - 1:02 am

    What’s up, after reading this remarkable post i am too delighted to share
    my know-how here with friends.

  2. #2 by Marcos on Junho 4, 2017 - 7:53 pm

    Muito bem explicado! Me ajudou muito! Obrigado !

  3. #3 by Robson on Janeiro 26, 2017 - 8:57 pm

    Olá ! Prezado tenho esta implementação de Dao´s da forma como esta neste artigo agora estou sem conseguir implementar uma junção ou seja um Join obtendo um dados ou lista que junta dois Pojos, e até buscar registros relacionados filtrando citando até o comentário da Jessica quando descreveu pesquisas obtendo localidades de uma determinado municipio.

    Podes ajudar nesta questão?

    Robson

    • #4 by ROBSON DA COSTA LIRA on Junho 4, 2017 - 9:00 pm

      Olá Prezados boa tarde, sera que estaria correto eu criar um outro pojo com os atributos de duas classes apenas para recuperar as informações e na classe DAO exemplo em PedidoDAO criei um método e fiz desta forma

      public List getLista(){

      List listaRetorno = new ArrayList();

      String query = “SELECT p.id, p.emissao, p.status, p.total, c.razaosocial, p.processamento, c.pessoa, c.cnpjcpf” +
      ” FROM pedido p INNER JOIN cliente c” +
      ” ON p.id_cliente = c.id ” +
      ” WHERE 1 = 1 ” +
      ” ORDER BY p.id” ;

      Cursor c = recuperarPorQueryCursor(query);

      if(c!=null && c.getCount()>0){

      if(c.moveToFirst()){
      do {

      ListPedido aux = new ListPedido();
      aux.setId(c.getInt(0));
      aux.setEmissao( new Date(c.getLong(1)));
      aux.setStatus(c.getString(2));
      aux.setTotal(c.getDouble(3));
      aux.setNome(c.getString(4));
      aux.setProcessamento(c.getString(5));
      aux.setPessoa(c.getString(6));
      aux.setCnpj_cpf(c.getString(7));

      listaRetorno.add(aux);
      } while (c.moveToNext());
      }
      }

      return listaRetorno;
      }

      // Robson

  4. #5 by Michael Rodrigues on Fevereiro 29, 2016 - 1:00 am

    Cara, muito 10 o seu tutorial, só queria uma dúvida, como eu faço a implementação dentro das Activities? ou seja, qual classe chamo, como instancio o banco, crio a tabela, insiro os registros etc..

    Obrigado!

  5. #6 by Antonio Flavio on Outubro 23, 2015 - 2:11 pm

    Cara preciso criar um banco de dados no Andróid onde coloco um texto com numeros e letras e retorna um texto que esta relacionado a este numero.
    exemplo
    modelo retorna – peça fabricada em aço carbono altamente flexivel devido ao tratamento termico especial
    CAR232

  6. #7 by Robson de Souza on Março 9, 2015 - 2:23 pm

    Como eu poderia implementar inner join entre duas tabelas no DAOBasico?

  7. #8 by Robson De Souza on Fevereiro 19, 2015 - 1:25 pm

    Ótimo tutorial. Caso eu queira fazer uma busca pelo searchview na actionbar, e ao digitar uma palavra na searchview, uma listview vá aparecendo com a palavra digitada. Como eu poderia fazer isso? O método que eu posso utilizar é o recuperarPorID ?

  8. #12 by Marília Neves on Janeiro 14, 2015 - 10:25 pm

    Ficou muito bom parabéns, esta me ajudando muito o seu tutorial 😉

  9. #13 by Silvio Clécio on Novembro 20, 2014 - 12:30 am

    Leonardo, virei teu fã! Rs…

    Tietagem a parte, tenho duas sugestões pra ti. =)

    Primeira sugestão: nesse trecho de código:

                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());
                }
    

    Seria interessante trocar por:

                int indexID = cursor.getColumnIndex(COLUNA_ID);
                int indexMarca = cursor.getColumnIndex(COLUNA_MARCA);
                int indexModelo = cursor.getColumnIndex(COLUNA_MODELO);
                int indexPlaca = cursor.getColumnIndex(COLUNA_PLACA);
    
                if (cursor.moveToFirst()) {
                    do { 
                        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());
                }
    
    
    Assim você evita executar o método "getColumnIndex()" (que por sua vez tem um loop interno para fazer a busca do índice do campo) para cada passo do seu loop. Isso fará com que o código ganhe performance. Já melhorei códigos assim, conseguindo um ganho de mais de 90% de performance.
    
    Segunda sugestão: por favor, escreva um artigo sobre greenDAO. Estou estudando o framework e adoraria ler um artigo sobre ele de tua autoria. =)
    
    Abração!
    • #14 by Leonardo Casasanta on Novembro 20, 2014 - 1:37 pm

      Ola Silvio,

      Muito obrigado pelo comentário.

      Vou seguir essa sua dica de recuperação via cursor! Parece muito boa!

      Em relação ao artigo de GreenDAO, pretendo escrever um artigo sobre frameworks de persistência. Só to precisando de um pouquinho de tempo livro =]

      Abraço.

      • #15 by Silvio Clécio on Novembro 20, 2014 - 1:48 pm

        Oi Leonardo,

        Show de bola, vou ficar no aguardo do artigo, já assinei até os feeds. =)

        Só uma observação: o exemplo do artigo já está usando cursor, a sugestão no caso foi para fazer a busca do índice do campo fora do loop do resultset, tornando o código uns 90% mais performático. =)

  10. #16 by Juarez A. Franco Junior on Novembro 18, 2014 - 1:34 pm

    Só tenho uma dúvida.
    Vou exemplifica-la.

    Tenho uma Classe Livro.
    Que tem como atributo um Objeto Autor

    public class Livro{
    int id;
    Autor autor;
    //…
    }

    Na hora que vou recuperar o livro surgiu uma duvida

    public Livro contentValueParaEntidade(ContentValue valores){
    Livro livro = new Livro();
    livro.setId(valores.getAsInteger(COLUNA_ID));

    //qual a melhor forma de eu implementar para colocar o contexto aqui?
    AutoDAO daoAutor = AutorDAO.getInstance(context????);
    Autor autor = daoAutor.recuprearPorId(valores.getAsInteger(COLUNA_AUTOR));
    livro.setAutor(autor);
    daoAutor.fecharConexao();

    return livro;
    }

    • #17 by Leonardo Casasanta on Novembro 19, 2014 - 1:20 pm

      Isso mesmo Juarez,
      Como este código foi colocado no DAO do Livro você vai ter uma referência do Context ai, pois é obrigatório no construtor do DAO.

      Ai é só passar ele pra acessar outro DAO do Autor.

      Tem uma galera que curte usar uma estrutura chamada de Repositorio. Nesse caso você teria um repositorio de livros, e esse Repositorio iria acessar LivroDAO e AutorDAO. Seria um modo de deixar os DAOs independentes. Mas isso é só outro modo de fazer as coisas.

      Desse seu jeito eu acho que já atende bem.

      • #18 by Juarez on Novembro 19, 2014 - 3:57 pm

        OK mas minha duvida é eu n tenho um atributo do tipo Contexto na classe LivroDAO. Eu posso criar um atributo do tipo Context mas minha duvida é quando receber esse Context na classe livroDAO, pois se eu pegar o Context que foi passado no construtor, posso estar com um contexto errado pois o construtor só é chamado uma vez. Logo o context que eu passar na hora de recuperar um Autor pode ser errado. Espero que tenha entendido.

    • #19 by Leonardo Casasanta on Novembro 20, 2014 - 1:35 pm

      Entendi o seu receio Juarez.

      Neste caso, para ter total certeza você poderia passar o Context como parâmetro do seu método de recuperar.

      Mas pensando aqui pode existir uma maneira melhor de recuperar um livro com um Autor.

      Você pode fazer um SELECT com INNER JOIN na tabela Livros com Autor.

      Assim o ContentValues já vai chegar com os dados das duas classes e você só vai precisar criar o objeto autor e preenche-lo com os dados do banco.

  11. #20 by Juarez A. Franco Junior on Novembro 18, 2014 - 1:16 pm

    Muito bom, muito completo, um dos melhores modelos a se seguir. Obrigado!

  12. #21 by augusto on Setembro 12, 2014 - 8:06 pm

    Muito bom este artigo, um dos melhores que já li.
    Queria saber como seria para pesquisar se um dado já existe no banco antes de salva-ló!

  13. #22 by Carlos Marian on Setembro 4, 2014 - 11:22 am

    Olá uma outra opção que pode ser usada para facilitar o acesso a dados é o ORMLite-android.
    Estou usando em um projeto e tem facilitando bastante o acesso a dados.

    At++

  14. #23 by Diogenes on Agosto 31, 2014 - 11:33 pm

    Leonardo,
    Estou com uma dúvida. Como faço para criar consultas customizadas para um classe específica.

    • #24 by Leonardo Casasanta on Setembro 4, 2014 - 3:00 am

      Vou deixar um método de exemplo abaixo. Este método fica classe PessoaDAO e vai recuperar uma lista de Pessoas com base no nome. Veja como ficou:

      public List recuperarQueryPorNome(String nome) {
      String queryOne = “SELECT * FROM ” + getNomeTabela() + ” where nome = ‘ ” + nome+ “‘”;
      List result = recuperarPorQuery(queryOne);
      return result;
      }

  15. #25 by Gilmar François on Agosto 21, 2014 - 10:05 pm

    Muito bom, tudo bem explicado e funcionando.

  16. #26 by José Monteiro on Julho 31, 2014 - 7:00 pm

    Meu deus que artigo foi este :O

    sem palavras pra agradecer muito FODAAAAA!!!!!

  17. #28 by Danilo on Maio 24, 2014 - 4:35 pm

    cara, muito bom esse artigo, me ajudou muito… mais gostaria de sabe como faço pra colocar os veiculos cadastrados numa listview com o método .

  18. #29 by Luis Gustavo on Maio 6, 2014 - 7:40 pm

    Leonardo, muito boa a demostração.
    Teria como você postar tambem como ficaria o a classe mainActivity ?

  19. #30 by Mauricio Copetti on Abril 2, 2014 - 5:51 pm

    Cara o meu projeto ta dando um erro nessa linha(The blank field context may not have been initialized)
    sabe me dizer o que é?
    private VeiculoDAO(Context context) {
    PersistenceHelper persistenceHelper = PersistenceHelper.getInstance(context);
    dataBase = persistenceHelper.getWritableDatabase();
    }

  20. #31 by Mateus on Março 21, 2014 - 3:27 am

    Andei vendo sobre o SQLite, e vi que ele não tem um controle de acesso a usuario, senha e etc… caso queria fazer uma aplicação na qual cada usuario tenha o seu perfil, a aplicação possa ter um numero alto de usuarios e rodar pelo mundo por exemplo , Ex : Whatsapp . O SqLite continua sendo o indicado ?

  21. #32 by Lucas on Março 20, 2014 - 8:03 pm

    Tenho ideia de um aplicativo onde ele possa salvar no banco de dados conversar, e interações entre outros usuarios, um aplicativo online… para isso uso o Sqlite ou o MySql ?

    • #33 by Leonardo Casasanta on Março 20, 2014 - 8:14 pm

      Ola Lucas,

      O banco de dados nativo do Android é o SQLite.

      • #34 by Lucas on Março 20, 2014 - 9:46 pm

        ah sim, pensei que por ele não podia ter chat entre os usuários, e transferências de informações de um usuário para outro . Obrigado 🙂

  22. #35 by Thiago on Março 14, 2014 - 5:16 am

    olá, tenho uma dúvida com relação ao banco de dados….por exemplo, tem como salvar nnum banco soh dados do tipo:
    a minha aplicação eh a seguinte: tenho uma tela inicial onde possui 2 botão, um pra criar conta e outro ir pra tela de login….após logar abre uma outra tela onde eu posso fazer cadastro de rendas, despesas e gerar relatórios…cada usuário possui alguns atributos como id, nome e etc…soh que cada usuário tbm pode cadastrar suas rendas e despesas….como eu posso fazer pra salvar no banco todos esses dados? tenho que criar vários bancos? tem como criar um soh pra cada usuário onde ele pode salvar seus dados básico(id, nome e etc) e suas rendas e despesas?

    • #36 by Leonardo Casasanta on Março 19, 2014 - 7:04 pm

      Ola Thiago,

      Obrigado pelo comentário.

      Você esta se referindo a tabelas.
      Um banco de dados pode ter varias tabelas, tipo tabela de Usuario que vai ter (id, nome, telefone), tabela de receita (id,valor), tabela de despesa (id, valor).

      Recomendo que procure na internet, um livro ou um artigo pra te dar uma introdução legal em banco de dados.

      Abraço.

  23. #37 by Gerson Júnior on Dezembro 12, 2013 - 11:14 am

    Leonardo parabéns pelo artigo. Mas eu tenho uma dúvida, A aplicação não poderia ja ter o arquivo *.db do Sqlite ja criado com toda a estrutura de tabela? Porque sempre tem que criar o banco, é impressão minha ou toda vez que executar a aplicação o banco será criado?

    • #38 by Leonardo Casasanta on Dezembro 12, 2013 - 2:23 pm

      Ola Gerson, você pode fazer das duas maneiras. Ou o banco de dados ja vem contido no projeto ou ele é criado no momento do aplicativo ser instalado, mais precisamente é criado no momento exato em que o aplicativo tenta fazer uso pela primeira vez da base. Veja que no SQLiteOpenHelper agente passa uma versão, quando essa versão é alterada ai você pode mudar o banco de dados, trocar tabelas, campos e tal sem precisar perder dados. Se você tiver uma app no Google Play e ela tiver muitos downloads, depois de um tempo você quer colocar funcionalidades novas e mudar as tabelas. Se o seu banco tiver contigo no projeto o usuário vai ter que reinstalar o app e vai ter perdido os dados que já tinham antes, já com esse esquema que eu mostrei no artigo você pode mudar a versão do banco e fazer tratamentos para que ele não perca os dados que ele armazenou até ali. Não sei se fui claro, mas vou tentar dar uma melhorada nessa parte do artigo. Abraço.

  24. #39 by patrik on Outubro 15, 2013 - 11:39 am

    ola sou novo nesse tipo de aplicação teria um jeito mais simples de me ensinar algo?

    • #40 by Leonardo Casasanta on Outubro 15, 2013 - 5:53 pm

      Patrik,

      Você que esta iniciando, pode acompanhar o livro do Ricardo Lecheta.

      Segue o link: http://www.livroandroid.com.br/

      É um livro muito bom para se aprender Android, no entanto você já tem que ter uma noção básica de desenvolvimento de software.

      Abraço

      • #41 by patrik on Outubro 15, 2013 - 5:56 pm

        Obrigado espero que m ajude eheheh

  25. #42 by Carlos on Setembro 1, 2013 - 10:51 pm

    Olá, caso eu tenha mais de um DAO usando a estrutura que vc sugeriu posso ter problemas com acesso ao banco caso um dao tenha feito uam consulta e o outro dao em seguida tbm fizer uma consulta ou alteração?
    Pergunto isso pq, imagine que tenho uma lista que ao selecionar um item, a aplicação vá para outra lista que contenha itens relacionados ao primeiro. A primeira lista não foi destruída( close), e a nova lista abre uma nova conexao.

    • #43 by Leonardo Casasanta on Setembro 3, 2013 - 12:51 am

      Carlos,

      Sua pergunta é muito boa.
      A classe SQLiteDatabase é a que no fim das contas mantém o contato com o banco de dados. No caso de ter dois DAO’s, você teria que ter um BaseDAO, que seria herdado pelos dois DAO’s do seu projeto. Este BaseDAO é que vai ter a referencia do SQLiteDatabase, então você vai ter dois DAO’s mas somente uma conexão com o banco de dados. Vou ver se completo este post demonstrando isso que falei de maneira prática. Obrigado pelo comentário!

      • #44 by Luiz Carvalho on Abril 19, 2014 - 1:19 pm

        Usei esse seu Exemplo Leonardo e me foi muito útil, mas estou exatamente com o problema descrito pelo Carlos. Você ainda vai criar o complemento para esse post? agradeceria demais.

        Obrigado.

    • #45 by Leonardo Casasanta on Junho 30, 2014 - 6:32 am

      O Post foi atualizado. Agora esta persistindo duas entidades.

  26. #46 by Jessica on Maio 13, 2013 - 5:47 pm

    Olá, ótimo post.
    Mas fiquei com uma dúvida, eu tenho 2 spinners, a “primeira” eu seleciono o local que eu quero(hospital, escola,praças de uma determinada cidade)
    na “segunda” spinner eu seleciono o bairro que eu quero(bairro A, bairro
    B, bairo C)

    De maneira que quando eu clicar em pesquisar apareça em outra tela
    todos hospitais do bairro A por exemplo. Dependendo da opção que eu
    escolher em cada spinner. Só que eu não estou conseguido fazer esse
    filtro através.

    Eu teria que criar um banco de dados com todas essas informações correto?
    Um banco de dados com os bairros, e tabelas com os hospitais e seus
    respectivos bairros, outra tabela com todas as escolas e seus
    respectivos bairros e as praças com seus respectivos bairros, ok?

    Minha dúvida é na criação dessa tabela e como eu faria uma chave pra
    uni-las pra fazer a pesquisa! Eu não criei o banco de dados ainda pq
    to com essa dúvida!

  27. #48 by Leonardo Casasanta on Março 27, 2013 - 6:23 pm

    Supondo que existisse no exemplo deste post, a entidade Pessoa e que Veiculo tivesse Pessoa. Você poderia colocar um idPessoa na classe veiculo. E você teria que ter uma classe chamada PessoaDAO, que saberia como persistir esta outra entidade, assim como temos o VeiculoDAO. Também seria interessante ter uma classe abstrata chamada BaseDAO que seja herdada pelo PessoaDAO e o VeiculoDAO. Desse modo comportamentos comuns podem ser reaproveitados por todos os DAO’s da aplicação. Usando Generics e Reflection, a sua estrutura pode se tornar bem flexível em relação ao numero de entidades.

  28. #49 by Andreia on Março 26, 2013 - 7:32 pm

    muito bom! Agora me gerou uma duvida como eu fazeria se tivesse mais de uma tabela e com relacionamento??

Deixe uma resposta para patrik Cancelar resposta