MANEJO DE DATOS EN ANDROID: SQLITE
A través de la sección Proponer una noticia, José María Tristán Martín, nos ha hecho llegar este interesantísimo artículo sobre manejo de datos a través de SQLite en Android. No os lo perdáis que es muy completo. Además desde aquí animamos a la gente a mandar sus artículos para que sean publicados.
A la hora de poder almacenar datos o consultar los mismos, Android, dispone de una serie de mecanismos, como son, la utilización de una base de datos relacional, en concreto SQLite, utilizar un sistema de preferencias, en el que se almacenarán pares de clave y valor y usar el sistema de ficheros. También dispone de una utilidad que nos permite compartir datos entre aplicaciones, los ContentProvider. A continuación vamos a analizar la forma de construir bases de datos así como la gestión de los datos.
- SQLiteCursor: nos permite recuperar los datos, mediante una select o con el método query que ejecuta también una select. Podremos iterar sobre cada uno de los registros, columna a columna, como si se tratase de un resultset.
- SQLiteDatabase: expone métodos para gestionar los datos en una base de datos, como insertar, actualizar, eliminar, ejecutar sentencias sql, abrir y cerrar las conexiones, trabajar de forma transaccional.
- SQLiteOpenHelper: nos permite diseñar, crear, actualizar la base de datos.
- y gestionar la versión de la misma.
- SQLiteQueryBuilder: es un helper para crear sqls.
- SQLiteStatement: para trabajar con sentencias precompiladas.
- eventos: cada evento nos indicará durante qué días y horas pueden sonar las llamadas de los contactos que indiquemos.
- contactos: contendrá todos nuestros contactos. En la aplicación podríamos llamar a un ContentProvider para acceder directamente a los contactos almacenados en el móvil, pero por simplificación de la aplicacición, almacenaremos a nivel de aplicación, los contactos.
- contactosDelEvento: contendrá simplemente la relación de qué contactos contiene cada evento.
01.
public
final
class
EventoTabla {
02.
public
static
final
String TABLE_NAME =
"eventos"
;
03.
public
static
class
EventoColumnas
implements
BaseColumns {
04.
public
static
final
String NOMBRE =
"nombre"
;
05.
public
static
final
String LUNES =
"lunes"
;
06.
public
static
final
String MARTES =
"martes"
;
07.
public
static
final
String MIERCOLES =
"miercoles"
;
08.
public
static
final
String JUEVES =
"jueves"
;
09.
public
static
final
String VIERNES =
"viernes"
;
10.
public
static
final
String SABADO =
"sabado"
;
11.
public
static
final
String DOMINGO =
"domingo"
;
12.
public
static
final
String DE_HORA =
"de_hora"
;
13.
public
static
final
String A_HORA =
"a_hora"
;
14.
public
static
final
String DE_MINUTO =
"de_minuto"
;
15.
public
static
final
String A_MINUTO =
"a_Minuto"
;
16.
public
static
final
String ACTIVO =
"activo"
;
17.
}
18.
19.
public
static
void
onCreate(SQLiteDatabase db){
20.
StringBuilder sb =
new
StringBuilder();
21.
sb.append(
"CREATE TABLE "
+ EventoTabla.TABLE_NAME +
" ("
);
22.
sb.append(BaseColumns._ID +
" INTEGER PRIMARY KEY, "
);
23.
sb.append(EventoColumnas.NOMBRE +
" TEXT UNIQUE NOT NULL, "
);
24.
sb.append(EventoColumnas.LUNES +
" INTEGER, "
);
25.
sb.append(EventoColumnas.MARTES +
" INTEGER, "
);
26.
sb.append(EventoColumnas.MIERCOLES +
" INTEGER, "
);
27.
sb.append(EventoColumnas.JUEVES +
" INTEGER, "
);
28.
sb.append(EventoColumnas.VIERNES +
" INTEGER, "
);
29.
sb.append(EventoColumnas.SABADO +
" INTEGER, "
);
30.
sb.append(EventoColumnas.DOMINGO +
" INTEGER, "
);
31.
sb.append(EventoColumnas.DE_HORA +
" INTEGER NOT NULL, "
);
32.
sb.append(EventoColumnas.A_HORA +
" INTEGER NOT NULL, "
);
33.
sb.append(EventoColumnas.DE_MINUTO +
" INTEGER NOT NULL, "
);
34.
sb.append(EventoColumnas.A_MINUTO +
" INTEGER NOT NULL, "
);
35.
sb.append(EventoColumnas.ACTIVO +
" INTEGER "
);
36.
sb.append(
");"
);
37.
db.execSQL(sb.toString());
38.
}
39.
40.
public
static
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion){
41.
db.execSQL(
"DROP TABLE IF EXISTS "
+ EventoTabla.TABLE_NAME);
42.
EventoTabla.onCreate(db);
43.
}
- NULL: el valor que va a contener la columna es NULL.
- INTEGER: pueden almacenarse enteros, de 1,2,3,4,6, o 8 bytes con signo.
- REAL: valores de punto flotante.
- TEXT: se trata de un string.
- BLOB: para valores BLOB( objetos binarios grandes) como imágenes, archivos multimedia, etc.
01.
public
final
class
ContactoDelEventoTabla {
02.
public
static
final
String TABLE_NAME =
"contactosDelEvento"
;
03.
public
static
class
ContactoDelEventoColumnas
implements
BaseColumns {
04.
public
static
final
String CONTACTO_ID =
"contacto_id"
;
05.
public
static
final
String EVENTO_ID =
"evento_id"
;
06.
}
07.
08.
public
static
void
onCreate(SQLiteDatabase db){
09.
StringBuilder sb =
new
StringBuilder();
10.
sb.append(
"CREATE TABLE "
+ ContactoDelEventoTabla.TABLE_NAME +
" ("
);
11.
sb.append(ContactoDelEventoColumnas.EVENTO_ID +
" INTEGER NOT NULL, "
);
12.
sb.append(ContactoDelEventoColumnas.CONTACTO_ID +
" INTEGER NOT NULL, "
);
13.
sb.append(
"FOREIGN KEY("
+ ContactoDelEventoColumnas.CONTACTO_ID +
")"
+
14.
" REFERENCES "
+ EventoTabla.TABLE_NAME +
"("
+
15.
BaseColumns._ID +
")"
);
16.
sb.append(
"FOREIGN KEY("
+ ContactoDelEventoColumnas.EVENTO_ID +
")"
+
17.
" REFERENCES "
+ ContactoDelEventoTabla.TABLE_NAME +
"("
+
18.
BaseColumns._ID +
")"
);
19.
sb.append(
");"
);
20.
db.execSQL(sb.toString());
21.
}
22.
23.
public
static
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion){
24.
db.execSQL(
"DROP TABLE IF EXISTS "
+
25.
ContactoDelEventoTabla.TABLE_NAME);
26.
ContactoDelEventoTabla.onCreate(db);
27.
}
28.
}
01.
public
class
Evento{
02.
private
Long id;
03.
private
String nombre;
04.
private
boolean
lunes;
05.
private
boolean
martes;
06.
private
boolean
miercoles;
07.
private
boolean
jueves;
08.
private
boolean
viernes;
09.
private
boolean
sabado;
10.
private
boolean
domingo;
11.
private
int
deHora;
12.
private
int
deMinuto;
13.
private
int
aHora;
14.
private
int
aMinuto;
15.
private
boolean
activo;
16.
private
Set contactos;
17.
public
Evento(){
18.
contactos =
new
LinkedHashSet();
19.
}
20.
...
01.
public
class
EventoDao
implements
DAO {
02.
private
static
final
String INSERT =
03.
"insert into "
+ EventoTabla.TABLE_NAME
04.
+
"("
+ EventoColumnas.NOMBRE +
", "
+ EventoColumnas.LUNES +
", "
05.
+ EventoColumnas.MARTES +
", "
+ EventoColumnas.MIERCOLES +
", "
06.
+ EventoColumnas.JUEVES +
", "
+ EventoColumnas.VIERNES +
", "
07.
+ EventoColumnas.SABADO +
", "
+ EventoColumnas.DOMINGO +
", "
08.
+ EventoColumnas.DE_HORA +
", "
+ EventoColumnas.A_HORA +
", "
09.
+ EventoColumnas.DE_MINUTO +
", "
+ EventoColumnas.A_MINUTO +
", "
10.
+ EventoColumnas.ACTIVO +
")"
11.
+
"values (?,?,?,?,?,?,?,?,?,?,?,?,?)"
;
12.
private
SQLiteDatabase db;
13.
private
SQLiteStatement insertStatement;
1.
public
EventoDao(SQLiteDatabase db){
2.
this
.db = db;
3.
insertStatement = db.compileStatement(EventoDao.INSERT);
4.
}
01.
public
long
save(Evento evento) {
02.
insertStatement.clearBindings();
03.
insertStatement.bindString(
1
, String.valueOf(evento.getNombre()));
04.
insertStatement.bindString(
2
, String.valueOf(evento.isLunes()));
05.
insertStatement.bindString(
3
, String.valueOf(evento.isMartes()));
06.
insertStatement.bindString(
4
, String.valueOf(evento.isMiercoles()));
07.
insertStatement.bindString(
5
, String.valueOf(evento.isJueves()));
08.
insertStatement.bindString(
6
, String.valueOf(evento.isViernes()));
09.
insertStatement.bindString(
7
, String.valueOf(evento.isSabado()));
10.
insertStatement.bindString(
8
, String.valueOf(evento.isDomingo()));
11.
insertStatement.bindLong(
9
, evento.getDeHora());
12.
insertStatement.bindLong(
10
, evento.getaHora());
13.
insertStatement.bindLong(
11
, evento.getDeMinuto());
14.
insertStatement.bindLong(
12
, evento.getaMinuto());
15.
insertStatement.bindString(
13
, String.valueOf(evento.isActivo()));
16.
return
insertStatement.executeInsert();
17.
}
1.
public
long
save(Contacto contacto) {
2.
final
ContentValues values =
new
ContentValues();
3.
values.put(ContactoColumnas.NOMBRE, contacto.getNombre());
4.
values.put(ContactoColumnas.TELEFONO, contacto.getTelefono());
5.
return
db.insert(ContactoTabla.TABLE_NAME,
null
, values);
6.
}
01.
public
void
update(Evento evento) {
02.
final
ContentValues values =
new
ContentValues();
03.
values.put(EventoColumnas.NOMBRE, evento.getNombre());
04.
values.put(EventoColumnas.LUNES, evento.isLunes());
05.
values.put(EventoColumnas.MARTES, evento.isMartes());
06.
values.put(EventoColumnas.MIERCOLES, evento.isMiercoles());
07.
values.put(EventoColumnas.JUEVES, evento.isJueves());
08.
values.put(EventoColumnas.VIERNES, evento.isViernes());
09.
values.put(EventoColumnas.SABADO, evento.isSabado());
10.
values.put(EventoColumnas.DOMINGO, evento.isDomingo());
11.
values.put(EventoColumnas.DE_HORA, evento.getDeHora());
12.
values.put(EventoColumnas.A_HORA, evento.getaHora());
13.
values.put(EventoColumnas.DE_MINUTO, evento.getDeMinuto());
14.
values.put(EventoColumnas.A_MINUTO, evento.getaMinuto());
15.
values.put(EventoColumnas.ACTIVO, evento.isActivo());
16.
db.update(EventoTabla.TABLE_NAME, values,
17.
BaseColumns._ID +
" = ? "
,
new
String[]{
18.
String.valueOf(evento.getId())});
19.
}
1.
public
void
delete(Evento evento) {
2.
if
(evento.getId()>
0
){
3.
db.delete(EventoTabla.TABLE_NAME,
4.
BaseColumns._ID +
" = ?"
,
new
String[]{
5.
String.valueOf(evento.getId())});
6.
}
7.
}
01.
public
Evento get(
long
id) {
02.
Evento evento =
null
;
03.
Cursor c = db.query(EventoTabla.TABLE_NAME,
04.
new
String[]{
05.
BaseColumns._ID, EventoColumnas.NOMBRE,
06.
EventoColumnas.LUNES, EventoColumnas.MARTES,
07.
EventoColumnas.MIERCOLES, EventoColumnas.JUEVES,
08.
EventoColumnas.VIERNES, EventoColumnas.SABADO,
09.
EventoColumnas.DOMINGO,
10.
EventoColumnas.DE_HORA, EventoColumnas.A_HORA,
11.
EventoColumnas.DE_MINUTO, EventoColumnas.A_MINUTO,
12.
EventoColumnas.ACTIVO},
13.
BaseColumns._ID +
" = ?"
,
new
String[]{String.valueOf(id)},
14.
null
,
null
,
null
,
"1"
);
15.
if
(c.moveToFirst()){
16.
evento =
this
.pasarDeCursorAEvento(c);
17.
}
18.
if
(!c.isClosed()){
19.
c.close();
20.
}
21.
return
evento;
22.
}
1.
public
Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
- table: el nombre de la tabla donde vamos a seleccionar los registros.
- columns: una lista con las columnas que debe devolver la select.
- selection: la condición where que deseamos aplicar. Si no pasamos ninguna condición nos devolvera todos los registros.
- selectionArgs: los argumentos a reemplazar en el where. Para ello, en el where utilizaremos el símbolo ?.
- groupBy: agrupará los valores por los campos indicados.
- having: ejecutará una condición de selección una vez agrupados los valores.
- orderBy: las columnas por las que se realizará la ordenación de los registros.
- limit: número máximo de registros que serán devueltos.
1.
moveToFirst().
2.
if
(c.moveToFirst()){
3.
evento =
this
.pasarDeCursorAEvento(c);
4.
}
01.
private
Evento pasarDeCursorAEvento(Cursor c) {
02.
Evento evento =
null
;
03.
if
(c !=
null
){
04.
evento =
new
Evento();
05.
evento.setId(c.getLong(
0
));
06.
evento.setNombre(c.getString(
1
));
07.
evento.setLunes(Boolean.getBoolean(c.getString(
2
)));
08.
evento.setMartes(Boolean.getBoolean(c.getString(
3
)));
09.
evento.setMiercoles(Boolean.getBoolean(c.getString(
4
)));
10.
evento.setJueves(Boolean.getBoolean(c.getString(
5
)));
11.
evento.setViernes(Boolean.getBoolean(c.getString(
6
)));
12.
evento.setSabado(Boolean.getBoolean(c.getString(
7
)));
13.
evento.setDomingo(Boolean.getBoolean(c.getString(
8
)));
14.
evento.setDeHora(c.getInt(
9
));
15.
evento.setaHora(c.getInt(
10
));
16.
evento.setDeMinuto(c.getInt(
11
));
17.
evento.setaMinuto(c.getInt(
12
));
18.
evento.setActivo(Boolean.getBoolean(c.getString(
13
)));
19.
}
20.
return
evento;
21.
}
01.
public
List getAll() {
02.
List list =
new
ArrayList();
03.
Cursor c = db.query(EventoTabla.TABLE_NAME,
04.
new
String[]{
05.
BaseColumns._ID, EventoColumnas.NOMBRE,
06.
EventoColumnas.LUNES, EventoColumnas.MARTES,
07.
EventoColumnas.MIERCOLES, EventoColumnas.JUEVES,
08.
EventoColumnas.VIERNES, EventoColumnas.SABADO,
09.
EventoColumnas.DOMINGO,
10.
EventoColumnas.DE_HORA, EventoColumnas.A_HORA,
11.
EventoColumnas.DE_MINUTO, EventoColumnas.A_MINUTO,
12.
EventoColumnas.ACTIVO},
13.
null
,
null
,
14.
EventoColumnas.NOMBRE,
15.
null
,
null
,
null
);
16.
if
(c.moveToFirst()){
17.
do
{
18.
Evento evento =
this
.pasarDeCursorAEvento(c);
19.
if
(evento !=
null
)
20.
list.add(evento);
21.
}
while
(c.moveToNext());
22.
}
23.
if
(!c.isClosed()){
24.
c.close();
25.
}
26.
return
list;
27.
}
01.
public
Evento find(String nombre){
02.
long
eventoId = 0L;
03.
String sql =
"select _id from "
+ EventoTabla.TABLE_NAME
04.
+
" where upper("
+ EventoColumnas.NOMBRE +
") = ? limit 1"
;
05.
Cursor c = db.rawQuery(sql,
new
String[]{nombre.toUpperCase()});
06.
if
(c.moveToFirst()){
07.
eventoId = c.getLong(
0
);
08.
}
09.
if
(!c.isClosed()){
10.
c.close();
11.
}
12.
return
this
.get(eventoId);
13.
}
01.
public
class
DataManagerImpl
implements
DataManager{
02.
private
Context context;
03.
private
SQLiteDatabase db;
04.
private
EventoDao eventoDao;
05.
private
ContactoDao contactoDao;
06.
private
ContactoDelEventoDao contactoDelEventoDao;
07.
public
DataManagerImpl(Context context){
08.
this
.context = context;
09.
SQLiteOpenHelper openHelper =
new
OpenHelper(
this
.context);
10.
db = openHelper.getWritableDatabase();
11.
eventoDao =
new
EventoDao(db);
12.
contactoDao =
new
ContactoDao(db);
13.
contactoDelEventoDao =
new
ContactoDelEventoDao(db);
14.
}
1.
public
Evento getEvento(
long
eventoId) {
2.
Evento evento = eventoDao.get(eventoId);
3.
if
(evento !=
null
){
4.
evento.getContactos().addAll(
5.
contactoDelEventoDao.getContactos(eventoId));
6.
}
7.
return
evento;
8.
}
01.
public
long
guardarEvento(Evento evento) {
02.
long
eventoId = 0L;
03.
try
{
04.
db.beginTransaction();
05.
eventoId = eventoDao.save(evento);
06.
if
(evento.getContactos().size() >
0
){
07.
for
(Contacto c: evento.getContactos()){
08.
long
contactoId = 0L;
09.
Contacto contacto = contactoDao.find(c.getNombre());
10.
if
(contacto ==
null
){
11.
contactoId = contactoDao.save(c);
12.
}
else
{
13.
contactoId = contacto.getId();
14.
}
15.
ContactoDelEvento contactoDelEvento =
16.
new
ContactoDelEvento(
new
Long(eventoId),
new
Long(contactoId));
17.
if
(!contactoDelEventoDao.exists(contactoDelEvento)){
18.
contactoDelEventoDao.save(contactoDelEvento);
19.
}
20.
}
21.
}
22.
db.setTransactionSuccessful();
23.
}
catch
(SQLException e){
24.
Log.e(
"DataManagerImpl"
,
"Error salvando el evento"
, e);
25.
eventoId = 0L;
26.
}
finally
{
27.
db.endTransaction();
28.
}
29.
return
eventoId;
30.
}
- .help: nos muestra todos los comandos disponibles
- .exit: cerramos la instancia abierta a la base de datos.
- .tables: nos muestra una lista de las tablas creadas
- .schema ?TABLE?: nos muestra la sentencia CREATE utilizada para crear la tabla.
- "Android in practice", editorial "Manning".
- "Android. Guía para desarrolladores", editorial "Anaya".
- Documentación oficial de android.
Reader Comments (10)
Hola.
Me interesaría saber la forma de acceder a una BBDD cliente-servidor.
gracias!
Es interesante su articulo, soy nueva en el uso de Android, pero me gustaria saber mas acerca del manejo de la Base de Datos. Tienen algun otro ejemplo... Gracias
Si elimino un regitro en una base de datos Sqlite que contenga un campo numerico autoincrement, ¿Cómo hago para que los demás registros actualicen dicho campo? ¿Tengo que actualizarlos uno por uno o existe una funcion en Sqlite para autonumerarlos?
gracias de antemano.
Felicitaciones muy buen artículo. hay posibilidad de que suban el código fuente para examinarlo.
Muy interesante tu artículo, gracias por compartir tus conocimientos.
Te doy las gracias porque aportes como el tuyo ayudan a ir viendo más en español ayudas y artículos que profundizan en la programación Android. Aún todavía se ve mucho más en lengua inglesa si se quiere uno meter en profundidades pero todo se andará.
¿Una duda sólo, hay limites de caracteres en los campos tipo text en BBDD SQLITE?
Gracias de nuevo.
Muchas gracias por la explicación, todo más claro ahora.
Hola una pregunta
En tu codigo no faltaria un + (copio las 3 lineas de codigo)
en la segunda linea despues de la ultima " o en la tercera linea antes de; "???
REFERENCES " + EventoTabla.TABLE_NAME + "(" +
.BaseColumns._ID + ")");
.sb.append("FOREIGN KEY(" + ContactoDelEventoColumnas.EVENTO_ID + ")" +
Buenas tardes.
Una duda... el tema de caracteres especiales como la "ñ" o vocales con tildes... a mi me corta la palabra cuando la recupero con el getstring()
ejemplo
creo mi cursor
....
String palabra = micursor.getString(1);
....
en la Base de datos está guardado España.... y getString(1) me devuelve "Espa"
Alguna solución? gracias...
Muy buena tu publicacion además e enriquecedora es interesante una pregunta debido a la cantidad de info q maneja Android y a pesar de multiples Bases de Datos mas robustas por qué escoje SQLITE? Y si tuvo alguna complicación?
Tu descripción es excelente. gracias