PDA

Ver la Versión Completa : [ CONSULTA ] ¿Es necesario todo esto para implementar una BD SQLite?


superx335
26/02/15, 21:54:01
Buenas!

He estado haciendo mi primera base de datos SQLite y la verdad es que, aunque muchas cosas me parecen lógicas, otras me parecen demasiado engorrosas, y quizá es que no lo hago bien.

He creado una base de datos con una tabla de prueba, con un par de campos solamente, y para ello he extendido la clase SQLiteOpenHelper, implementando el onCreate y onUpgrade, y a continuación he definido los CRUD (añadir datos a la tabla, actualizarlos, eliminarlos, obtener todos los registros...).

El problema es que ahora que quiero hacer la base de datos real, he creado 4 tablas con muchos campos, y por poner el ejemplo de una de ellas, hago, en el onCreate:

String CREATE_CLIENTES_TABLE = "CREATE TABLE clientes ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"nombre TEXT, " +
"nombre_comercial TEXT, " +
"nif TEXT, " +
"telf INTEGER, " +
"movil INTEGER, " +
"email TEXT, " +
"calle TEXT, " +
"codpost INTEGER, " +
"ciudad TEXT, " +
"forma_pago TEXT )";
db.execSQL(CREATE_CLIENTES_TABLE);


Luego, para las operaciones CRUD:

private static final String TABLE_CLIENTES = "clientes";

Ahora las columnas

private static final String KEY_CLIENTES_ID = "id";
private static final String KEY_CLIENTES_NOMBRE = "nombre";
private static final String KEY_CLIENTES_NOMCOM = "nombre_comercial";
private static final String KEY_CLIENTES_NIF = "nif";
private static final String KEY_CLIENTES_TELF = "telf";
private static final String KEY_CLIENTES_MOVIL = "movil";
private static final String KEY_CLIENTES_EMAIL = "email";
private static final String KEY_CLIENTES_CALLE = "calle";
private static final String KEY_CLIENTES_CODPOST = "codpost";
private static final String KEY_CLIENTES_CIUDAD = "ciudad";
private static final String KEY_CLIENTES_FORMPAG = "forma_pago";
private static final String[] CLIENTES_COLUMNS = {KEY_CLIENTES_ID,KEY_CLIENTES_NOMBRE,KEY_CLIENTES_ NOMCOM,KEY_CLIENTES_NIF,KEY_CLIENTES_TELF,KEY_CLIE NTES_MOVIL,KEY_CLIENTES_EMAIL,KEY_CLIENTES_CALLE,K EY_CLIENTES_CODPOST,KEY_CLIENTES_CIUDAD,KEY_CLIENT ES_FORMPAG};

Y luego, uno a uno, los métodos, por ejemplo (este metodo no coincide con Clientes, es a modo de ejemplo):

public List<Parte> getAllPartes() {
List<Parte> partes = new LinkedList<Parte>();

//1. Build the query
String query = "SELECT * FROM " + TABLE_PARTES;

//2. Get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);

//3. Go over each row, build parte and add it to list
Parte parte = null;
if(cursor.moveToFirst()){
do{
parte = new Parte(null, null, null);
parte.setId(Integer.parseInt(cursor.getString(0))) ;
parte.setNombre(cursor.getString(1));
parte.setApellido(cursor.getString(2));
parte.setMoreInfo(cursor.getString(3));

//Add parte to partes
partes.add(parte);
} while(cursor.moveToNext());
}

Log.d("getAllPartes()", partes.toString());

//Return partes
return partes;
}

Si ahora tengo que hacer todo esto para la siguiente tabla... no es un proceso MUY precario? o es así y ya está? la verdad es que sería una vez y ya, pero aún así me parece raro.

Un poquito de luz, por favor! :)

Gracias de antemano

kriogeN
26/02/15, 22:07:20
Si y no.

Tienes que crear el SQLiteOpenHelper y como mínimo el onCreate, eso por supuesto.

Ahora bien, como te montes las CRUD es asunto tuyo, en todos los sitios hacen eso de definir las columnas y luego definir el array de las columnas, pero no es obligatorio, puedes usar los textos a pelo en las instrucciones SQL, lo mismo con el TABLE_NAME.

Puedes crearte una función que a partir del Cursor te devuelva el modelo (en el ejemplo que has puesto "parte"), así por cada función que tengas que hace un SELECT puedes usar la función para transformar el Cursor en objeto del modelo.

¿Tienes que ejecutar el getWritableDatabase? Si, pero puedes tener una función "open" y llamarla sólo la primera vez, o incluso hacerlo en el constructor.

¿Tienes que hacer todo el rollo del Cursor? Si, porque los Query funcionan así.

Como ya te he dicho, es como te lo montes, pero si que tiene un API que tienes que respetar. Si te sirve de consuelo en iOS es mucho más engorroso.

superx335
27/02/15, 21:14:14
Si y no.

Tienes que crear el SQLiteOpenHelper y como mínimo el onCreate, eso por supuesto.

Ahora bien, como te montes las CRUD es asunto tuyo, en todos los sitios hacen eso de definir las columnas y luego definir el array de las columnas, pero no es obligatorio, puedes usar los textos a pelo en las instrucciones SQL, lo mismo con el TABLE_NAME.

Puedes crearte una función que a partir del Cursor te devuelva el modelo (en el ejemplo que has puesto "parte"), así por cada función que tengas que hace un SELECT puedes usar la función para transformar el Cursor en objeto del modelo.

¿Tienes que ejecutar el getWritableDatabase? Si, pero puedes tener una función "open" y llamarla sólo la primera vez, o incluso hacerlo en el constructor.

¿Tienes que hacer todo el rollo del Cursor? Si, porque los Query funcionan así.

Como ya te he dicho, es como te lo montes, pero si que tiene un API que tienes que respetar. Si te sirve de consuelo en iOS es mucho más engorroso.

Muchísimas gracias antes de nada, me lo has explicado muy claro, sin embargo me quedan un par de dudas:

¿A qué te refieres con lo del cursor que devuelva el modelo? no sé si te refieres a algo que pensaba hacer pero no sé bien cómo, y es implementar solo una función add, delete, getAll... y de alguna manera enviarle un parámetro para indicarle a qué tabla quiero aplicarlo, no sé si van por ahí los tiros...

Por otro lado, entiendo lo que comentas de ponerlo en columnas o directamente crearlo con los textos, sin crear Strings y todo eso, pero aun así, como vengo de programación web, se me hace un poco raro, a ver si entiendo...

En web yo creo mi base de datos SQL de la forma que prefiera, y luego, por ejemplo, en php, en el momento que toca creo una variable con parámetros para hacer una acción en esa base de datos (puedo tener una pequeña librería con la conexión predeterminada, claro), mientras que en Android, cuando llega el punto en que quieres añadir un dato, no creas la sentencia SQL (¿podría?) sino que llamas a la función de la base de datos que permiti añadir justo el tipo de dato que esperas. ¿Es así?. Incluso siendo esto cierto, tengo un objeto creado "Parte" que en algún momento "relleno" e inserto a la base de datos, ya que contiene cada variable necesaria para encajar en los campos de la base de datos... ¿podría prescindir de esto y cuando vaya a meter un dato, por ejemplo, coger todos los datos de un formulario (por poner un ejemplo) y añadir todos estos datos mediante el método add de la base de datos? evidentemente entiendo que antes tendría que modificar el método para que no reciba un Parte sino un montón de variables.

A ver si puedieras responderme, me aclararías mucho :)

Gracias de antemano!

kriogeN
27/02/15, 21:40:46
Como poder hacer lo de tener una sola función add,delete,getAll para todas las tablas podrías, pero no es muy recomendable, es mejor cumplir el principio de responsabilidad única y tener un fichero por cada tabla de la BD.

Y si puedes hacer operaciones SQL tal cual, te pongo un ejemplo de operaciones SQL con modelo en una app que estoy desarrollando:


public boolean borrarOpciones(Ayuntamiento ayuntamiento) {
try {
database.execSQL("DELETE FROM " + TemporalesDBHelper.TABLA_OPCIONES + " WHERE "+columnas[0]+"=?", new Object[]{ayuntamiento.ayuntamiento});
} catch (SQLException e) {
return false;
}
return true;
}

En este caso tengo un OpcionesDataSource, donde está la función, y recibe como parámetro una instancia de Ayuntamiento, que es un modelo de datos que contiene todos los campos de la tabla Ayuntamiento.

EDIT:

Te pongo el comienzo de la clase Ayuntamiento para que lo veas:


public class Ayuntamiento implements Parcelable {

\@SerializedName("Ayuntamiento")
public long ayuntamiento;
\@SerializedName("Nombre")
public String nombre;

superx335
01/03/15, 20:45:10
Como poder hacer lo de tener una sola función add,delete,getAll para todas las tablas podrías, pero no es muy recomendable, es mejor cumplir el principio de responsabilidad única y tener un fichero por cada tabla de la BD.

Y si puedes hacer operaciones SQL tal cual, te pongo un ejemplo de operaciones SQL con modelo en una app que estoy desarrollando:


public boolean borrarOpciones(Ayuntamiento ayuntamiento) {
try {
database.execSQL("DELETE FROM " + TemporalesDBHelper.TABLA_OPCIONES + " WHERE "+columnas[0]+"=?", new Object[]{ayuntamiento.ayuntamiento});
} catch (SQLException e) {
return false;
}
return true;
}En este caso tengo un OpcionesDataSource, donde está la función, y recibe como parámetro una instancia de Ayuntamiento, que es un modelo de datos que contiene todos los campos de la tabla Ayuntamiento.

EDIT:

Te pongo el comienzo de la clase Ayuntamiento para que lo veas:


public class Ayuntamiento implements Parcelable {

\@SerializedName("Ayuntamiento")
public long ayuntamiento;
\@SerializedName("Nombre")
public String nombre;


Genial, no sabía que todo esto estaba tan establecido, es decir que en realidad en lugar de estar haciendo demasiado trabajo, estoy cumpliendo ciertos "estándares", me gusta :)

En realidad lo que me sorprende es que haya que hacer objetos de todo, demasiado acostumbrado a lenguaje web supongo xD por ejemplo, para las 4 tablas que tengo... lo suyo sería hacer un objeto para cada uno, con sus setter y getters y su constructor, durante el programa rellenarlos por ejemplo, y entonces añadirlos a la base de datos, o recuperarlos y mostrar los datos, o bien editarlos y volver a meterlos en una instancia de ese objeto para almacenarlos, voy bien?.

Tengo esta clase:

public class Parte {

private int id;
private String nombre;
private String apellido;
private String moreInfo;

public Parte(String nombre, String apellido, String moreInfo){
super();
this.nombre = nombre;
this.apellido = apellido;
this.moreInfo = moreInfo;
}

//Getters & Setters
public String toString(){
return "Parte [id=" + id + ", nombre=" + nombre + ", apellido=" + apellido + ", moreInfo=" + moreInfo + "]";
}

public String getNombre(){
return this.nombre;
}
public String getApellido(){
return this.apellido;
}
public String getMoreInfo(){
return this.moreInfo;
}
public int getId(){
return this.id;
}
public void setNombre(String nombre){
this.nombre = nombre;
}
public void setApellido(String apellido){
this.apellido = apellido;
}
public void setMoreInfo(String moreInfo){
this.moreInfo = moreInfo;
}
public void setId(int id){
this.id = id;
}

}

Ahí entiendo que el método toString es para utilizarlo a modo de log luego, porque para mostrarlo en una aplicación o trabajar con la base de datos no hace falta verdad? de hecho para mostrarla recuperaré datos y los listaré en un ListView o similar, no?.

Siento molestarte tanto, pero me ayudas mucho. Gracias de nuevo! :)

kriogeN
02/03/15, 09:43:30
Genial, no sabía que todo esto estaba tan establecido, es decir que en realidad en lugar de estar haciendo demasiado trabajo, estoy cumpliendo ciertos "estándares", me gusta :)

En realidad lo que me sorprende es que haya que hacer objetos de todo, demasiado acostumbrado a lenguaje web supongo xD por ejemplo, para las 4 tablas que tengo... lo suyo sería hacer un objeto para cada uno, con sus setter y getters y su constructor, durante el programa rellenarlos por ejemplo, y entonces añadirlos a la base de datos, o recuperarlos y mostrar los datos, o bien editarlos y volver a meterlos en una instancia de ese objeto para almacenarlos, voy bien?.

Tengo esta clase:

public class Parte {

private int id;
private String nombre;
private String apellido;
private String moreInfo;

public Parte(String nombre, String apellido, String moreInfo){
super();
this.nombre = nombre;
this.apellido = apellido;
this.moreInfo = moreInfo;
}

//Getters & Setters
public String toString(){
return "Parte [id=" + id + ", nombre=" + nombre + ", apellido=" + apellido + ", moreInfo=" + moreInfo + "]";
}

public String getNombre(){
return this.nombre;
}
public String getApellido(){
return this.apellido;
}
public String getMoreInfo(){
return this.moreInfo;
}
public int getId(){
return this.id;
}
public void setNombre(String nombre){
this.nombre = nombre;
}
public void setApellido(String apellido){
this.apellido = apellido;
}
public void setMoreInfo(String moreInfo){
this.moreInfo = moreInfo;
}
public void setId(int id){
this.id = id;
}

}

Ahí entiendo que el método toString es para utilizarlo a modo de log luego, porque para mostrarlo en una aplicación o trabajar con la base de datos no hace falta verdad? de hecho para mostrarla recuperaré datos y los listaré en un ListView o similar, no?.

Siento molestarte tanto, pero me ayudas mucho. Gracias de nuevo! :)

Si, lo suyo es hacer un objeto por todo:

Un DBHelper que construya las tablas, un objeto de modelo por cada tabla y un DataSource por cada tabla que sirva para insertar objetos de modelo en la tabla o para convertir datos de la tabla en objetos de modelo (o List de ellos en el caso de ser listados).

Si lo haces así si algún día pasas a iOS no te costará mucho, porque en iOS no es que se deba hacer así, es que SOLO se puede hacer así.