Implementação de Operações CRUD com ORMLite no Android utilizando Singleton e Cache de DAOs

O ORMLite é uma biblioteca eficiente que simplifica a persistência de dados em aplicações Android, mapeando objetos Java para tabelas SQLite. Neste guia, exploraremos como realizar operações fundamentais de banco de dados, aplicar o padrão Singleton para gerenciamento de conexões e otimizar o acesso a objetos de acesso a dados (DAOs).

1. Estrutura da Interface de Usuário

Para interagir com o banco de dados, utilizaremos um layout simples composto por botões que acionam as operações de inserção, consulta, deleção e atualização para diferentes entidades.

<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:columnCount="2">

        <Button android:text="Novo Usuário" android:onClick="onInsertUser" />
        <Button android:text="Listar Usuários" android:onClick="onFetchUsers" />
        <Button android:text="Remover Usuário" android:onClick="onDeleteUser" />
        <Button android:text="Atualizar Usuário" android:onClick="onUpdateUser" />
        
        <Button android:text="Novo Setor" android:onClick="onAddDepartment" />
        <Button android:text="Ver Setores" android:onClick="onListDepartments" />
        <Button android:text="Novo Colaborador" android:onClick="onAddEmployee" />
        <Button android:text="Ver Colaboradores" android:onClick="onListEmployees" />
    </GridLayout>
</LinearLayout>

2. Definição das Entidades (Modelos)

As classes de modelo utilizam anotações do ORMLite para definir a estrutura das tabelas e os relacionamentos.

Entidade Usuário

@DatabaseTable(tableName = "usuarios")
public class Usuario implements Serializable {
    @DatabaseField(generatedId = true)
    private int id;
    
    @DatabaseField(canBeNull = false)
    private String login;
    
    @DatabaseField(canBeNull = false)
    private String senha;
    
    @DatabaseField(dataType = DataType.DATE)
    private Date dataCriacao;

    public Usuario() {} 
    public Usuario(String login, String senha, Date data) {
        this.login = login;
        this.senha = senha;
        this.dataCriacao = data;
    }
    // Getters e Setters...
}

Entidades com Relacionamento (Setor e Colaborador)

Utilizamos ForeignCollectionField para representar o lado "um" de um relacionamento um-para-muitos.

@DatabaseTable(tableName = "setores")
public class Setor implements Serializable {
    @DatabaseField(generatedId = true)
    private int id;
    
    @DatabaseField
    private String nomeSetor;

    @ForeignCollectionField(eager = false)
    private ForeignCollection<Colaborador> colaboradores;
    // Construtores e métodos omitidos...
}

@DatabaseTable(tableName = "colaboradores")
public class Colaborador implements Serializable {
    @DatabaseField(generatedId = true)
    private int id;
    
    @DatabaseField
    private String nome;

    @DatabaseField(columnName = "setor_id", foreign = true, foreignAutoRefresh = true)
    private Setor setor;
    // Construtores e métodos omitidos...
}

3. Gerenciamento do Banco de Dados (DatabaseHelper)

Implementamos o DatabaseHelper utilizando o padrão Singleton para evitar vazamentos de memória e conflitos de conexão. Além disso, incluímos um cache de DAOs para melhorar a performance.

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
    private static final String DB_NAME = "app_database.db";
    private static final int DB_VERSION = 1;
    private static DatabaseHelper instance;
    private Map<String, Dao> daoCache = new HashMap<>();

    private DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    public static synchronized DatabaseHelper getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseHelper(context.getApplicationContext());
        }
        return instance;
    }

    @Override
    public void onCreate(SQLiteDatabase db, ConnectionSource source) {
        try {
            TableUtils.createTable(source, Usuario.class);
            TableUtils.createTable(source, Setor.class);
            TableUtils.createTable(source, Colaborador.class);
        } catch (SQLException e) {
            Log.e("DB", "Erro ao criar tabelas", e);
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, ConnectionSource source, int oldVer, int newVer) {
        try {
            TableUtils.dropTable(source, Usuario.class, true);
            onCreate(db, source);
        } catch (SQLException e) {
            Log.e("DB", "Erro no upgrade", e);
        }
    }

    public <D extends Dao<T, ?>, T> D getCachedDao(Class<T> clazz) throws SQLException {
        String className = clazz.getSimpleName();
        if (!daoCache.containsKey(className)) {
            daoCache.put(className, getDao(clazz));
        }
        return (D) daoCache.get(className);
    }

    @Override
    public void close() {
        super.close();
        daoCache.clear();
    }
}

4. Implementação da Camada DAO

Os DAOs isolam a lógica de persistência da lógica de interface.

public class UsuarioRepository {
    private Dao<Usuario, Integer> usuarioDao;

    public UsuarioRepository(Context context) {
        try {
            usuarioDao = DatabaseHelper.getInstance(context).getCachedDao(Usuario.class);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void salvar(Usuario u) throws SQLException {
        usuarioDao.create(u);
    }

    public List<Usuario> buscarTodos() throws SQLException {
        return usuarioDao.queryForAll();
    }

    public void atualizar(Usuario u) throws SQLException {
        usuarioDao.update(u);
    }
    
    public List<Usuario> buscarPorNome(String termo) throws SQLException {
        QueryBuilder<Usuario, Integer> qb = usuarioDao.queryBuilder();
        qb.where().like("login", "%" + termo + "%");
        return qb.query();
    }
}

5. Integração na Activity

Finalmente, conectamos as ações da interface aos repositórios de dados.

public class MainActivity extends Activity {
    private UsuarioRepository userRepo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        userRepo = new UsuarioRepository(this);
    }

    public void onInsertUser(View v) {
        try {
            Usuario novo = new Usuario("dev_user", "pass123", new Date());
            userRepo.salvar(novo);
            Toast.makeText(this, "Usuário salvo!", Toast.LENGTH_SHORT).show();
        } catch (SQLException e) {
            Log.e("UI", "Falha ao inserir", e);
        }
    }

    public void onFetchUsers(View v) {
        try {
            List<Usuario> lista = userRepo.buscarTodos();
            for (Usuario u : lista) {
                Log.d("DB_RESULT", "ID: " + u.getId() + " - Login: " + u.getLogin());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Tags: android ormlite sqlite singleton dao-pattern

Publicado em 5-31 20:46 por Thomas