Actividad para insertar nueva mascota

Tutorial de SQLite con Android: CRUD (create, read, update, delete)

Para guardar datos en una base de datos usando Android tenemos que recurrir al maravilloso gestor SQLite3. Pues bien, hoy veremos cómo trabajar con SQLite desde Android usando Java.

Lo que veremos será un CRUD o ABC de Android con SQLite en donde veremos un insert, update, delete y select de SQL con Android.

Al final tendremos una app móvil simple que permitirá interactuar con SQLite para realizar las operaciones básicas que se ve así:

CRUD de SQLite con Android - Mascotas
CRUD de SQLite con Android – Mascotas

Todo esto usando SQLite y una clase que extiende de SQLiteOpenHelper.

Vistazo final

Al final tendremos múltiples actividades para interactuar con SQLite. La actividad principal tendrá una lista con RecyclerView y un botón FAB que abre la actividad para agregar.

Cuando se toque un elemento de la lista se mandará a una nueva activity para editar y finalmente en el toque largo se mostrará una alerta para eliminar.

Lo que vamos a gestionar es una base de datos de Mascotas, guardaremos el nombre y la edad. Será simple y a partir de ello podríamos extender los campos.

Proyecto, código fuente y descargas

Esta app es totalmente gratuita y open source, su repositorio lo encuentras aquí. Si quieres descargar la app visita la página releases.

Una demostración del proyecto puede ser vista en YouTube (ya que estamos en esto te invito a suscribirte):

Nota: a través del post iré mostrando fragmentos de código importantes, pero si quieres ver el código completo y actualizado míralo en GitHub.

Ahora sí comencemos.

La estructura de la base de datos

No vamos a hacer relaciones porque no son necesarias, tampoco agregaremos índices. Simplemente crearemos un campo autoincrementable y otros dos campos para el nombre y la edad.

Por lo tanto la creación de la tabla queda así:

La base de datos de SQLite igualmente se llamará mascotas. En resumen, tanto la base de datos como la tabla tendrán el mismo nombre.

Ayudante de base de datos

Comenzamos definiendo una clase que extiende de un Helper, mejor dicho, SQLiteOpenHelper. Hay que sobrescribir algunas funciones de la interfaz, las cuales son:

  1. El constructor
  2. onCreate
  3. onUpgrade

Cada cosa tiene su forma. Comenzando con el constructor que es en donde se recibe el contexto y se llama al método padre con la versión de la base de datos.

El método onCreate es llamado cuando la base de datos ha sido creada, es llamado con un objeto de tipo SQLiteDatabase que es la base de datos “física” que ha sido creada.

Por otro lado, el método onUpgrade es cuando actualizamos la estructura o algo de la base de datos pero nuestra app ya está instalada, entonces en este momento comparamos versiones y ajustamos para aquellos usuarios que tengan otra versión. Esto de onUpgrade lo veremos en otra ocasión.

Código fuente del helper

Aquí tenemos el código del ayudante que nos va a permitir interactuar con la base de datos de SQLite:

Además de sobrescribir los métodos estamos definiendo unas constantes que nos van a ayudar a escribir mejor código.

Una parte importante es dentro del método onCreate, pues ahí creamos la tabla. Si hubiera más tablas las crearíamos ahí con db.execSQL.

Nota sobre las inyecciones SQL

Algunos pensarán que como la base de datos permanece local, no hay problema de que haya inyecciones SQL porque el usuario se haría daño a sí mismo; sin embargo no vamos a confiarnos de ello y vamos a escapar las consultas para asegurar nuestra app.

No vamos a quebrarnos mucho la cabeza, pues los métodos que ofrece la API están enfocados a prevenir inyecciones SQL y a usar algo como sentencias preparadas. Es decir, este tutorial es totalmente seguro en cuanto a las inyecciones SQL.

El controlador de las mascotas

Esto no es MVC ni esas cosas que están de moda; pero he tratado de separar los componentes para que al final sea una programación modular y sencilla.

He puesto un controlador de mascotas, que no hace más que “pegar” la vista con el ayudante de la base de datos; así podemos tener múltiples métodos que gestionen nuestras mascotas, los cuales podrían ser reutilizados.

El controlador se encarga de gestionar todas las operaciones de la base de datos con la siguiente relación:

  • eliminarMascota: se encarga de eliminar una mascota a través de su id, es decir, hace la operación delete
  • nuevaMascota: crea o inserta una mascota dentro de la base de datos
  • guardarCambios: operación update de la base de datos. Recibe la mascota editada, de la cual tomará el id para hacer el where.
  • obtenerMascotas: la operación select. Regresa una lista de la clase Mascota.

Como te habrás fijado, todo esto depende de la clase Mascota, que es la abstracción de una mascota del mundo real. Veámoslo:

Veamos operación por operación.

Insertar en base de datos

El código queda así:

Comenzamos obteniendo la base de datos a la que se puede escribir, es decir, la writable. Preparamos un objeto de ContentValues que llevará el nombre del campo de la base de datos y el valor que se va a insertar.

Es importante que pongamos el mismo nombre de la columna de la tabla dentro del ContentValues. Como vemos, tomamos los datos del objeto mascota y sus métodos get.

Llamamos al método put por cada valor que vamos a insertar, y después de ello llamamos al método insert de la base de datos con estos argumentos:

  1. El nombre de la base de datos en donde se va a insertar
  2. Un posible string para insertar un valor nulo, más detalles aquí. En este caso no lo usamos y por eso lo ponemos en null.
  3. Valores para insertar, que serán los datos que realmente se insertarán, de tipo ContentValues.

Todo esto regresa un dato de tipo long que regresamos igualmente, el cual representa el id del valor insertado o -1 en caso de que algo haya fallado.

Obtener de la base de datos

Para hacer un select llamamos al método query de la base de datos. Por cierto, ahora solamente necesitamos la base de datos en modo lectura, no escritura.

Al inicio declaramos una lista de mascotas en un ArrayList de tipo Mascota; estamos reutilizando la clase.

Este método de query es realmente complejo a primera vista, pero déjame explicarte el significado de los argumentos. Por cierto, si no queremos mandar un argumento lo dejamos en null.

  1. El nombre de la tabla en donde se hará la consulta (o la query como dicen los chicos de hoy)
  2. Una lista de las columnas que se van a consultar. Es decir, lo que hacemos con select columna1, otracolumna, otracolumna lo hacemos aquí pero con un arreglo de tipo String y sin el select.
  3. Un filtro, lo que iría en el where. Por ejemplo, si fuera un where edad = 4 aquí mandaríamos un String únicamente con el valor edad = ?; para hacer la comparación veamos el siguiente argumento.
  4. Argumentos de selección, la complementación del anterior. En este caso es un arreglo de tipo String que sustituye los signos de interrogación del filtro anterior. Si en el anterior ponemos muchos signos (p. ej. edad = ? and nombre = ? entonces aquí podríamos mandar un arreglo de dos valores, la edad y el nombre.
  5. Agrupación. Pasar algo como group by edad pero sin el group by.
  6. Having, si necesitamos algo con having lo ponemos en forma de cadena sin el “having” en sí
  7. Ordenamiento. Pasar algo como order by pero sin el order by en sí.

Como estamos recuperando todas las mascotas, solamente nos importan los 2 primeros argumentos.

Este método de query regresa un cursor que vamos a iterar. Para empezar, si regresa nulo regresamos la lista vacía.

Luego comprobamos si tiene datos con moveToFirst y en caso de que no tenga, también regresamos la lista vacía.

Finalmente, en caso de que sí tenga datos, lo recorremos con un ciclo while que seguirá mientras el cursor se mueva hacia adelante, es decir, que el método moveToNext regrese true.

Dentro del ciclo obtenemos el valor de la base de datos; podemos llamar a cursor.getTipoDeDato por ejemplo cursor.getString o cursor.getInt; le pasamos como argumento el índice de la columna. Aquí importa el orden de las columnas que mandamos en el segundo argumento de query.

Luego creamos un objeto de tipo Mascota y la agregamos con add a nuestra lista

Al final de la función regresamos la lista de mascotas.

Actualizar en base de datos (update)

Este método es un poco más complicado que insertar pero menos difícil que obtener. Utilizamos de nuevo los ContentValues para mandar los nuevos datos, pero para hacer el where necesitamos dos cosas:

  1. El campo o columna que vamos a tomar para el where, por ejemplo, "where id = ?"
  2. El valor que sustituirá a los signos de interrogación

En este caso es solamente un valor pero podrían ser muchos, es por eso que se mandan en un arreglo de tipo String. Queda así:

Por cierto, regresamos lo mismo que regresa el método update, lo cual es un entero que indica el número de columnas que fueron afectadas al realizar la transacción. Teóricamente en este caso siempre sería 1, porque como actualizamos por id y el id es único, solamente se actualizaría una columna.

Sin embargo, si actualizáramos usando al nombre y hubiera nombres repetidos entonces se podrían actualizar más columnas. Casi lo olvido, si mandamos los 2 últimos argumentos en null, no se hace filtro y se modifica toda la tabla.

Eliminar de la base de datos con delete

Veamos el último método que igualmente recibe un objeto de tipo Mascota y elimina de la base de datos.

Es muy parecido al de actualizar. Primero recibe el nombre de la tabla, luego el where que se hará para eliminar y finalmente los argumentos que se le pasan al where.

Código completo y final del controlador

Poniendo todo junto, en controlador queda de la siguiente manera:

Para usarlo, se crea una instancia del mismo y se le pasa el contexto para que a su vez construya la base de datos con ese contexto.

Consumiendo controlador desde la vista

Ahora veamos cómo es que el controlador de las mascotas interactúa directamente con la vista, es decir, lo que ve el usuario. Comencemos con la actividad principal que tiene una lista (un RecyclerView) cuyos elementos son un layout:

Eso será repetido por el RecyclerView dentro de la actividad principal, cuyo diseño queda así:

En esa actividad ponemos un FAB y la lista. Todo va así:

Actividad principal con FAB y RecyclerView
Actividad principal con FAB y RecyclerView

Ahora veamos el código de la actividad principal:

Lo importante aquí es el método refrescarListaDeMascotas, el cual llama a nuestro controlador de mascotas y las dibuja. Por cierto, el controlador está siendo instanciado más arriba.

Más abajo se definen escuchadores. Por ejemplo, se escucha cuando se toca una fila (eso está dentro del adaptador que es algo más avanzado, así como el listener de la lista) o cuando se mantiene presionado un elemento.

Cuando se hace click en una fila, se va a la actividad de editar. Cuando se mantiene pulsada, sale una alerta que pide una confirmación.

Finalmente al presionar el FAB (el botón de la esquina) se va a la actividad de crear.

Eliminar mascota

Cuando se mantiene presionado el elemento se ejecuta este código:

Para obtener la mascota que fue tocada se usa el método get de la lista de mascotas, se le pasa el índice que obtenemos del listener.

Alerta al eliminar mascota en el LongClick
Alerta al eliminar mascota en el LongClick

Creamos un diálogo y cuando confirman la operación, la mascota se elimina desde el controlador.

 

Crear mascota

Cuando se toca el botón de la esquina, se dirige a una nueva actividad que tiene este diseño:

Actividad para insertar nueva mascota
Actividad para insertar nueva mascota

El código XML es este:

Y el de Java este:

Como vemos, se hace una validación simple y en caso de que se pase entonces se crea una nueva mascota para ser pasada como argumento al método del controlador que se encarga de hacer el insert.

En caso de que se inserte correctamente, se cierra la actividad. Y si no, se indica.

Editar mascota

Cuando se toca una mascota se va a una actividad que es idéntica al de insertar, pero que actualiza en lugar de insertar; es decir, llama a otro método del controlador.

Por cierto, como se va a editar debemos pasarle a la actividad un dato, el cual es la mascota editada. Por ello es que cuando se toca se hace esto:

Del otro lado, en la actividad que edita, se tiene este código:

Si se actualiza correctamente, la actividad se cierra. Si no, se indica.

Escuchar el método onResume

Cuando se cierra una actividad se vuelve a la principal, ahí escuchamos el método onResume y refrescamos la lista de mascotas.

De este modo, cuando se “regresa” de editar o crear una mascota, se vuelven a consultar. Como esto es después de haber realizado la operación, se obtiene una lista “fresca” de las mascotas.

Conclusión

Así es como terminamos este largo tutorial. ¿Crees que es difícil de leer? imagina qué tan complicado fue escribir el artículo y el código.

Recuerda, puedes comentar tus dudas y ver el código completo, así como descargar la app. No olvides seguirme en mis redes sociales.

Te invito a ver más sobre Android, Java o Bases de datos.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

17 comentarios en “Tutorial de SQLite con Android: CRUD (create, read, update, delete)”

  1. Buenas noches estoy guiándome con tu tutorial pero en la mía guardo productos con su nombre, precio y stock, tienes alguna idea de como hacer que al registrar un producto en el edittext stock aparezca por defecto el número 0 (No me refiero a la propiedad Text del EditText), después se guarde ese registro en la BD y luego al dar click en el recyclerview lo pueda editar para cambiar el 0 por otro numero? Soy novata en esto.

  2. Hola tengo una consulta, hice algunos cambios y al seleccionar un ítem de la lista muestro el activity solamente para ver los datos porque a los edit text les puse input type en null, luego con un botón para editar me mostraría otro activity pero con los edit text rellenados y listos para editar, pero al oprimir el botón que me manda a el 3er. Activity no sucede nada, talvez al ser el 3er. Activity no puede traer de manera correcta los datos seleccionados del primero, es lo único que se me ocurre, que puedo estar haciendo mal?

    1. Hola
      Yo lo veo claramente en el repositorio que dejé casi al inicio del post en letras resaltadas: https://github.com/parzibyte/CRUD-SQLite/blob/master/app/src/main/java/me/parzibyte/crudsqlite/AdaptadorMascotas.java
      También indico lo siguiente: “Nota: a través del post iré mostrando fragmentos de código importantes, pero si quieres ver el código completo y actualizado míralo en GitHub.”

      Le agradecería si lee las indicaciones correctamente
      Saludos

  3. hola tengo un problema ala hora de que me muestre los datos en los respectivos tvNombre , tvEdad, yo cree 5 TextView más, me edita los datos, y elimina y ademas los lista , pero soo muestra el nombre y la edad , nose en que estoy fallando , pero debo de tenerlo para mañana ,algún dato de como mostrar esos tv que añadi ?

  4. Que tal muy buen día colega, me ha servido bastante tu post sobre la app solo tengo una duda, si quisiera agregar un elemento SerchView en la app, para que este solo me muestre los resultados encontrados en la lista como se lo integrarías?

    Saludos!

  5. Pingback: Android: solución a Couldn't read row 0, col -1 from CursorWindow - Parzibyte's blog

  6. Pingback: Decompilar APK (app de Android) y obtener código fuente (casi) original - Parzibyte's blog

  7. Pingback: Android y Java - Pasar datos de un fragmento a otro - Parzibyte's blog

  8. Pingback: Android y SQLite: agenda | Ejemplo de app con RecyclerView - Parzibyte's blog

  9. Pingback: Android: Solución a "Variable is accessed within inner class. Needs to be declared final" - Parzibyte's blog

  10. Pingback: Generar códigos QR con Android y QRGen - Parzibyte's blog

Dejar un comentario