Programación Android, Base de Datos III

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

Android SQLite
En la última entrada mostramos cómo crear la base de datos para nuestra aplicación, así como las tablas, y qué métodos eran necesarios para ello. Lo último que nos falta por hacer es poder gestionarla. Es decir, añadir, borrar, actualizar y recuperar datos de la BD. Esto es lo que vamos a tratar de explicar en esta ocasión.

En la creación de la BD ya mostramos el método que nos permite pasar una sentencia SQLite completa, cuando creamos la tabla. En esta ocasión vamos a explicar una serie de métodos que nos facilitarán la tarea y, en la mayoría de los casos, evitarán que tengamos que escribir sentencias SQLite completas, aunque también explicaremos un nuevo método con el que recuperar la información de la BD a partir de una sentencia, para aquellos casos en que se haga necesario.

En Android, como hemos dicho, disponemos de métodos que nos van a facilitar las acciones más comunes en nuestra BD, de manera que gran parte de nuestras necesidades quedarán cubiertas sin necesidad de escribir largas sentencias SQLite, que en su lugar cambiaremos por algunos parámetros que pasamos a una determinada función. Empezaremos por explicar cómo insertar datos en nuestra tabla.

Insertar registros en la Base de Datos

En el objeto que creamos de la clase SQLiteDatabase, que en nuestro caso nombramos sqlDB, disponemos de un método que nos permite insertar datos en nuestras tablas de forma sencilla:

public long insertOrThrow (String table, String nullColumnHack,
                             ContentValues values)

Como vemos este método recibe tres parámetros distintos, el primero que indica el nombre de la tabla en la que queremos hacer la insercción de datos, el segundo parámetro es un parámetro opcional que nos permite insertarvalores null(en caso de no pasarlo como null), en el campo que indiquemos de la tabla, pero que generalmente pasaremos como null, y por último un objeto de la clase ContentValues, que es donde se encontrará la información que deseamos insertar en la tabla. El valor de retorno es la id de la tupla o del registro que se ha añadido, o -1 en el caso de que ocurra algún error.

Para mostrar cómo utilizar ese objeto ContentValues y que se entienda cómo añadir datos en nuestras tablas, consideremos una tabla en la que guardamos nombres y números de teléfono y hagamos la inserción de un nuevo registro:

ContentValues values = new ContentValues();
values.add("nombre", "Juanito");
values.add("telefono", 18945);

sqlDB.insert("tbTelefonos", null, values);

Como vemos en el código, lo que hacemos con el objeto ContentValues es añadir tantos datos como campos queramos insertar en nuestra tabla, indicando en un primer parámetro el nombre del campo y en el segundo el valor que tomará. Si sólo hubiéramos querido añadir el nombre habríamos omitido la línea de código en la que se añade el teléfono al objeto values. De esta manear podemos indicar todos los campos en los que queramos introducir información, omitiendo aquellos en los que no se vaya a insertar nada.

Recordemos que todas nuestras tablas tienen un primer campo _id, que es nuestra Primary Key. Sin embargo a la hora de insertar datos no vamos a prestar atención a este campo, ya que es un integer autoincrement, de manera que se gestionará por si sólo.

Otro dato a tener en cuenta es que esta operación sólo nos permite insertar un registro cada vez que ejecutemos el método insert, por lo que si queremos insertar varios registros ejecutaremos una vez el método por cada registro que queramos añadir, ya que añadir a la vez diversos nombres y teléfonos en el ejemplo anterior, daría como resultado un único nombre y un único teléfono, que coincidirían con los últimos añadidos, ya que la información anterior se sobrescribiría y se perdería:

ContentValues values = new ContentValues();
values.add("nombre", "Juanito");
values.add("telefono", 18945);

// Perdemos el nombre anterior al asignar uno nuevo.
values.add("nombre", "Paquito");

// Perdemos el número anterior al asignar uno nuevo.
values.add("telefono", 69848);

// Insertamos nombre = Paquito y teléfono = 69848.
sqlDB.insert("tbTelefonos", null, values);

Lo correcto  para añadir varios registros sería:

ContentValues values = new ContentValues();
values.add("nombre", "Juanito");
values.add("telefono", 18945);

// Insertamos el primer registro.
sqlDB.insert("tbTelefonos", null, values);

// Perdemos el nombre anterior al asignar uno nuevo.
values.add("nombre", "Paquito");

// Perdemos el número anterior al asignar uno nuevo.
values.add("telefono", 69848);

// Insertamos el segundo registro.
sqlDB.insert("tbTelefonos", null, values);

Lógicamente para que esta tarea estuviera optimizada esta acción la introduciríamos en un bucle que añada un nuevo registro en cada iteración.

Eliminar registros de la Base de Datos

Para eliminar determinada información de una tabla de la BD también disponemos de un método en nuestro objeto de la clase SQLiteDatabase que nos facilita la tarea:

public int delete (String table, String whereClause, String[] whereArgs)

Este método recibe también tres parámetros, que hacen referencia al nombre de la tabla el primer parámetro, el nombre del campo por el que queremos filtrar en el segundo parámetro, y una lista de argumentos en el tercero. Como valor de retorno nos devolverá el número de tuplas o registros que se han eliminado de la tabla. Para que quede más claro de nuevo acudamos al ejemplo de la tabla que almacena nombres y teléfonos:

String where = "nombre=?";
String[] args = { "Juanito" };

// Eliminamos todos los registros que coincidan con nombre = Juanito.
sqlDB.delete("tbTelefonos", where, args);

Si en el ejemplo anterior hubiéramos pasado únicamente el primer parámetro, dejando los dos restantes a null, el resultado habría sido el borrado de todos los registros de la tabla, dejándola sin información. Sin embargo si lo que deseamos es buscar la coincidencia de argumentos en varios campos la clausula where habría sido la siguiente:

String where = "campo1=? or campo2=?";

Y en este caso el tercer argumento tendría que tener dos valores, el primero para la coincidencia deseada para el campo1 y el segundo para la coincidencia deseada para el campo 2, y aquellos registros que cumplan ambas coincidencias resultarían eliminados:

String[] whereArgs = {"argCampo1", "argCampo2"};

Del mismo modo si lo que buscamos es la eliminación de aquellos registros que cumplan la igualdad en ambos campos, en lugar de sólo en uno, bastaría cambiar el or por un and.

Actualizar registros de la Base de Datos

A la hora de actualizar registros de nuestras tablas también disponemos de un método en SQLiteDatabase que nos ayuda a realizar la tarea:

public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

En este caso vemos que la firma es muy similar a la del método delete(), y esto es así porque el funcionamiento es prácticamente el mismo, salvo que en este caso utilizamos las coincidencias para actualizar los datos de los campos indicados en el objeto values. El valor de retorno, al igual que en delete() son el número de tuplas o registros afectados por la operación.

Un ejemplo en el que se actualiza el nombre de un contacto sería:

ContentValues values = new ContentValues();
values.put("nombre", "Pepito");
String where = "nombre=?";
String[] args = { "Juanito" };

sqlDB.update("tbTelefonos", values, where, args);

En este caso se actualizará el nombre, poniendo “Pepito” en lugar de “Juanito”, en todos los casos en que coincidan los argumentos. El uso del parámetro whereClause y whereArgs es idéntico al ya explicado anteriormente a la hora de querer buscar las coincidencias en base a diferentes campos de la tabla.

Recuperar información de la Base de Datos

También para las consultas a nuestra BD disponemos de un método que nos evita tener que escribir la sentencia completa. En realidad en este caso disponemos de varias sobrecargas para realizar esta tarea, en función de si queremos la información de todos los campos de la tupla, sólo algunos campos, etc. Lo mejor para saber qué método utilizar a la hora de realizar consultas a la BD es estudiarse la documentación que nos facilita Android Developers para la clase SQLiteDatabase.

Sin embargo vamos a poner un ejemplo para que se pueda entender más fácilmente el uso de este método. La sobrecarga que utilizaremos en el ejemplo será:

public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

Como vemos en la firma el método devuelve un objeto de la clase Cursor. Todas las sobrecargas devuelven este objeto, que contiene toda la información que devuelve la BD como resultado de la consulta. Más adelante veremos cómo utilizarlo para poder recuperar la información que contenga, de momento veamos cómo utilizar el método query():

String[] columns = { "nombre", "telefono" };
String selection = "nombre";

Cursor c = sqlDB.query("tbTelefonos", columns, selection, new String[] { "Juanito" }, null, null, null);

En esta ocasión como vemos en lugar de crear un objeto String[] y luego pasarlo como parámetro, directamente hemos pasado un objeto anónimo, que hemos creado en la llamada al método, y como no hemos querido utilizar las clausulas group by, having y order by, en su lugar hemos pasado null. Para entender el significado de estas cláusulas en una consulta a la BD es necesario estudiar la documentación de SQLite.
Atendiendo al código del ejemplo, la llamada al método query() nos ha devuelto un objeto Cursor, que contiene en su interior el nombre y telefono de todos los contactos que se llamen “Juanito”. Recoger el nombre de la BD en este caso no habría sido necesario, ya que somos nosotros los que estamos decidiendo cuál es el nombre que queremos, pero lo utilizamos en el ejemplo para mostrar su utilización.

En determinados casos necesitaremos escribir nosotros mismos la sentencia SQLite completa para recuperar la información de la BD (por ejemplo en el caso de necesitar un inner join de varias tablas), en ese caso utilizaremos un método distinto, que también dispone de varias sobrecargas. Mostraremos la más sencilla:

public Cursor rawQuery (String sql, String[] selectionArgs)

Como vemos, también nos devuelve un objeto de la clase Cursor, y recibe como parámetros la sentencia SQLite y los argumentos por los que queramos filtrar. Aunque generalmente ponemos la sentencia completa en el String sql, y pasamos null en el segundo objeto (por cuestión de comodidad), se puede dejar la sentencia con “select * from tbTelefonos where nombre=?” y en el segundo parámetro utilizar el objeto con los argumentos que queramos. Para ver su utilización veamos el siguiente ejemplo:

String query = "select * from tbTelefonos where nombre = 'Juanito'"
String query2 = "select * from tbTelefonos where nombre=?";

Cursor c;
c = sqlDB.rawQuery(query, null);
c = sqlDB.rawQuery(query2, new String[] { "Juanito" });
[/sorucecode]
Ambas consultas darán como resultado toda la información de tuplas o registros que coincidan con el nombre "Juanito".

Para ver cómo recoger la información que se encuentra en el objeto que nos han devuelto los métodos <em>query()</em> y <em>rawQuery()</em> crearemos una clase modelo <em>Contact</em>, que contendrá nombre y teléfono de los contactos:
1
public class Contact {
	private String nombre;
	private int telefono;

	public Contact(String nombre, int telefono) {
		this.nombre = nombre;
		this.telefono = telefono;
	}

	public String getNombre() {
		return nombre;
	}

	public int getTelefono() {
		return telefono;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public void setTelefono(int telefono) {
		this.telefono = telefono;
	}
}

Y ahora que disponemos de esta clase, recorreremos el Cursor que obtendríamos de las anteriores consultas para recuperar la información e introducirla en un ArrayList de contactos:

// Creamos el ArrayList
private ArrayList<Contact> contacts = new ArrayList<Contact>();

// Preguntamos si el Cursor contiene algún dato
if(c.moveToFirst())
	// Si se puede mover a la primera posición será porque
	// contenga información.
	do {
		// Recogemos toda la información
		String nombre = c.getString(c.getColumnIndex("nombre"));
		int numero = c.getInt(c.getColumnIndex("telefono"));

		contacts.add(new Contact(nombre, numero));
	} while(c.moveToNext);
	// Permanecemos en el bucle siempre y cuando el cursor tenga un nuevo dato.
}

En el código en primer lugar nos encontramos con un condicional if, en el que sólo entraremos en el caso de que el cursor tenga al menos un dato, de manera que nunca intentemos recuperar una información inexistente y así evitemos toda posible excepción. Una vez entramos en el if vemos que nos encontramos con un bucle do-while, en el que permaneceremos siempre y cuando el cursor sea capaz de pasar a un nuevo registro. Es decir, el bucle do-while se ejecutará tantas veces como tuplas o registros contenga nuestro cursor.

Una vez entramos en el bucle vemos que a través de los métodos getString() y getInt() recuperamos nombre y teléfono del contacto. En ambos casos hay que pasar un entero, que representa el número del campo al que queremos acceder. Pero como este número no depende de la posición que ocupa la columna en la tabla, sino de cómo se ha hecho la consulta a la BD, es preferible, siempre que se sepa el nombre del campo, preguntarle al propio cursor qué posición ocupa dicho campo. Esto lo hemos hecho con el método getColumnIndex(“nombre del campo”), que nos devuelve exactamente la posición que ocupa.

Sin embargo, en el caso de que estas columnas las hayamos recogido a partir de un inner join es posible que tengamos que decir nosotros mismos la posición que ocupa el campo que buscamos, para eso deberemos recordar el orden en que hemos recogido cada campo en nuestra consulta, teniendo en cuenta que el primer campo se encontrará en la posición 0.

Una vez recogidos estos datos introducimos un nuevo contacto en nuestro ArrayList, de manera que una vez que hayamos salido del bucle do-while en el objeto contacts contendremos toda la información recogida de nuestra consulta.

Con esto cubrimos todas las acciones básicas de nuestras Bases de Datos, en la próxima entrada haremos una aplicación de ejemplo en la que veremos cómo integrar y utilizar todas estas clases que hemos explicado en las últimas entradas.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

3 thoughts on “Programación Android, Base de Datos III

  1. Esque no lo entiendo, llevo ya dandole vueltas bastante rato, es especial este tutorial es bastante confuso, la clase ¿SQLiteDataBase cuando se ha creado?, y ¿donde esta la clase principal? donde se llama a la base de datos BasadeDatos db = new BasedeDatos(getBaseContext());

    1. Con este tutorial tan sólo quise dar una primera información básica y necesaria para entender cómo funciona en esencia la base de datos en Android, pero si te fijas en el final de esta entrada hay un enlace a un nuevo tutorial, en el que integro todos los datos que se han explicado aquí en un ejemplo de “aplicación real”, mostrando datos en un ListView, copiando datos entre tablas, eliminando datos de tablas, etc.

      Ese tutorial sí tiene un ejemplo que puedes descargar, y la integración de la base de datos con la aplicación queda mucho más clara.

      Un saludo.

  2. Hola, muchas gracias por todos los tutoriales, sirven de gran ayuda. Una pregunta ¿el código donde lo puedo descargar? para tener una mejor visión de todas las partes.

Deja un comentario