Ir al contenido


Foto

manejo de excepciones y praxis?


  • Por favor identifícate para responder
48 respuestas en este tema

#1 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 02 abril 2016 - 12:11

Leyendo variedades de ejemplos (de buena fuente)  y supuestos del manejo de excepciones y el uso de TRY.....EXCEPT, me asaltan dudas con respecto a su buena praxis como principante, ya que no parece tan fácil como a primera vista se puede apreciar.

 

Se pueden usar tantos TRY-EXCEPT en la misma secuencia de código?. En mi pruebas como dispongo de varias sentencias SQL (Insert y Select)


php
  1. .....ZQuery1.close;
  2. ..............'INSERT INTO "personas" (nombre, edad) VALUES (:nombre, :edad)';
  3. Try
  4. ......('nombre').AsString:='pepito';
  5. ......('edad').AsInteger:=33;
  6. DataModule1.ZQuery1.ExecSQL;
  7.  
  8. .....ZQuery1.close;
  9. ................'SELECT * FROM "personas"';
  10. ......ZQuery1.Open;
  11. .... IntToStr(DataModule1.ZQuery1.RecordCount)+' '+ 'registros' + ' ';
  12.  
  13. except
  14. on E:Exception do
  15. begin
  16. ShowMessage('El error provocado en inserción ha sido: '+E.Message);
  17. // Datamodule1.Zconnection1.RollBack; ->
  18. end;
  19. end; //end-Try

Situación:

 

DgGrid cargado en pantalla con todos los datos base datos. al pulsar boton "nuevo" automaticamente realiza una

inserción (como he añadido al CREATE un PRIMARY KEY "nombre") al insertar un repetido me lo "captura" en la Excepción, dando el aviso, pero ocurre dos cosas:

 

"Salta" al compilador dando mensaje "El proyecto ha lanzado una 'EZSQLExcepción' con el mensaje SQL Constraint failed" y después el ShowMessage, pero la intención es dar aviso en pantalla 'REGISTRO REPETIDO..." y repetir operación al pulsar botón "nuevo" con el DBGrid cargado de fondo y no "saltar" al compilador e incluso de fondo está el DBGrid cargado de datos y desaparece todo la info. Sería necesario meter código en el On E:Exception do?

 

Es necesario hacer varios TRY para INSERT y SELECT, ya que captura el error de INSERT, pero si el insert no diera error y si al hacer la lectura SELECT? 

 

No quiero usar una mala praxis de esta sentencia desde el principio...

 

Un saludo


  • 0

#2 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 02 abril 2016 - 03:41

Pue me voy respondiendo según voy sacando conclusiones echándole horas y horas!!!

"Salta" al compilador dando mensaje "El proyecto ha lanzado una 'EZSQLExcepción' con el mensaje
SQL Constraint failed" y después el ShowMessage, pero la intención es dar aviso en pantalla 'REGISTRO REPETIDO..."
y repetir operación al pulsar botón "nuevo" con el DBGrid cargado de fondo y no "saltar" al compilador e incluso de fondo
está el DBGrid cargado de datos y desaparece todo la info. Sería necesario meter código en el On E:Exception do


La Excepción para que no "salte" al compilador. Ir a herramientas -> Opciones -> Notificacion de excepciones de CT. La he quitado y listo, ya salta el mensaje en pantalla sin pasar por el compilador.
 
El siguiente problema, no doy a el, ya que al saltar la excepción de "registro repetido" el DBGrid se queda vacio y pequeño. Lo cual debo repetir en hacer otra vez un SELECT.... para que vuelva aparecer el DBGrid con todos los registros, como estaba antes en pantalla. Este Select lo inserto al final de la excepción pero fuera de ella...correcto o no? pues no lo sé!!
 

delphi
  1. Try
  2.    ...
  3.    ...
  4.  
  5.    except
  6.           on E:Exception do
  7.    begin
  8.               ShowMessage('El error provocado en inserción ha sido: '+E.Message);
  9.              // Datamodule1.Zconnection1.RollBack; ->
  10.     end;
  11. end; //end-Try
  12.  
  13. SELECT'..............'
  14.  
  15. end; // del procedimiento

 
Un saludo
  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 02 abril 2016 - 05:40

No se si estoy entendiendo tu duda, ¿tu quieres capturar distintas excepciones/errores en el try? Si es eso debes hacer algo como esto:


delphi
  1. try
  2. HacerAlgo;
  3. except
  4. on E: Exception1 do
  5. begin
  6. // aqui el código sobre que hacer cuando se da Exception1
  7. end; // fin para Exception1
  8. on E: Exception2 do
  9. begin
  10. // aqui para el de Exception2
  11. end; // fin para Exception2
  12. // etc
  13. end; // fin del try
  14. // más código

Como condición necesaria, la última excepción que se debe poner es la más general: Exception. ¿Porqué? Porque el funcionamiento de la "búsqueda" y captura de excepciones se realiza recorriendo el árbol de herencia. Es decir, que debe enlistarse desde las excepciones más específicas hacia la más general; si tu pones únicamente o como primera excepción la de tipo E: Exception el programa detendrá en ella, independiente de la excepción puntual ya que es la más general. Para que te hagas una idea,

Si yo tuviera un árbol de herencia de excepciones así:

EErrorPerro -> EErrorCuadrupedo -> EErrorAnimal.

 

Y estoy interesado en capturar estas tres, debo ir desde el extremo de la hoja hacia el padre:

E: EErrorPerro

E: EErrorCuadrupedo

E: EErrorAnimal

 

Si pongo al reveés estoy diciendo algo como: "¿La excepción es del tipo EErrorAnimal? Naturalmente, cualquiera excepción por debajo de su rama la cumple: una EErrorPerro ES un tipo de ErrorAnimal, pero obviamente no necesariamente un EErrorAnimal será un EErrorPerro.

 

Cuando tu armes el listado de excepciones deberás definir conscientemente que excepciones pretenderás capturar y luego fijarte como se da la rama de herencia. Para todas aquellas que ya no tengas interés pero que consideres que se pueden presentar es válido poner la general: Exception. ¿Cómo te fijas? La fácil: escribiendo código forzando a que se la situación a esperar, tomar nota de la excepción y añadirla al código. Luego manteniendo presionado Ctrl haz clic en el nombre, te llevará hacia la su declaración y podrás "navegar" sobre la rama.

 

Casi todas las excepciones relacionadas con las bases de datos siguen una rama como esta: ErrorConcreto -> ErrordeSQL -> ErrorDeConexion -> EDataBase (o EDataBaseException, no recuerdo, es la más general de las definidas para el contexto de las ramas de bases de datos) -> Exception. No tengo a mano Lazarus ahora para fijarme.

 

En el caso de Zeos, creo que tiene su propio árbol de excepciones. Tendrías que fijarte.

 

Ahora bien, no es para nada sano deshabilitar las excepciones al nivel del compilador/IDE. ¿Porqué? Porque de esa forma no te enteras de los posibles errores que se pueden presentar... no siempre podemos estar optimistas de que nuestro código es 100% seguro. Es necesario que lo habilites. Si, te entiendo que sea un tanto molesto ver el "doble mensaje" pero es que uno es a nivel del IDE mientras se está desarrollando la aplicación y el otro es el propio que se va a presentar si se ejecuta la aplicación. Si no crees, prueba ejecutando el exe fuera del IDE. Ya no verás el "doble mensaje". Mientras estés en "tiempo de desarrollo" es muy útil tener activo el aviso de errores.

 

Las excepciones como su nombre lo indica son situaciones que no estaban esperadas en nuestra lógica para la aplicación. En ocasiones es útil mostrar al usuario de tales situaciones, como el ShowMessage o el MessageBox, y en otras es deseable (y viable) la posibilidad de detectar el problema puesto que podremos volvernos hacia atrás y dejar al sistema en "estado de normalidad".

También debes saber que el uso del try no se limita al Try-Except, existe justamente el Try-Finally. Y justamente está pensado este último para llevar al programa a estados de normalidad. En ocasiones es válido el primero y en otras el segundo; también es válido incluso "concatenar" ambos tipos.


delphi
  1. try
  2. // algo
  3. try
  4. // algo más
  5. finally
  6. // calmemos...
  7. end;
  8. except
  9. // aqui podemos capturar exceptiones que se pueden dar dentro del try-finally
  10. end;

Nuevamente te invito a leer la Cara Oculta de Delphi 4, si no recuerdo mal el capítulo 4 explica el tema de las excepciones.

 

Saludos,


  • 1

#4 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 03 abril 2016 - 12:37

La Excepción para que no "salte" al compilador. Ir a herramientas -> Opciones -> Notificacion de excepciones de CT. La he quitado y listo, ya salta el mensaje en pantalla sin pasar por el compilador.

 

 

Cuidado con esto, es muy util que el IDE (no el compilador!) capture las excepciones cuando estas depurando la aplicacion; de esta forma te permite parar el programa e ir a la linea puntual en donde se genero la excepcion. Si configuras el IDE para que no capture las excepciones, quiere decir que va a ignorar todas las excepciones y perdes esta gran funcionalidad que es "ir rapidamente a donde ocurrio un error"

 

Ahora, tenes que tener en cuenta que eso pasa cuando estas depurando; si ejecutas el programa desde afuera del IDE, entonces te va a saltar tu mensaje de error personalizado

 

 

 

No digo que sea una mala practica desactivar las notificaciones de excepciones; simplemente puntualizar bien que significa. Yo personalmente voy jugando bastante con esto; en Delphi hay un boton que esta ahi al lado del Run que sirve como "toggle" que permite activar y desactivar rapidamente esta funcion, incluso en tiempo de ejecucion. Es cierto que si a veces es molesto que nos salgan excepciones que a veces capturamos adrede, ejemplo:


delphi
  1. try
  2. Result := 5 / 0; // obviamente genera una excepcion..
  3. except
  4. Result := 0; // decimos que el resultado es 0
  5. end;

Ese simple bloque de codigo, si estan activadas las notificaciones de excepciones, va a provocar que se muestre un cartel cuando se depura el programa y llega a ese punto; imaginate un bucle for que ejecute 100 veces ese bloque, van a ser 100 carteles que obvio molestan; en casos como ese es legitimo desactivar las notificaciones

 

Para todo lo demas, existe Delphius  :ap: 


  • 0

#5 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 03 abril 2016 - 05:23

Pues volveré a activar las notificaciones, pensando que tenía resuelto el problema por esa vía.
 
De ese libro y otras demo he podido sacar bastantes conclusiones Delphis, el primer día que me lo recomendabas pude echarle una ojeada, pero aun así asaltan muchas dudas y aunque pruebo en pantalla ejemplo y ejemplo muchas veces el codigo delphi cambia algo con respecto a Lazarus/CT.
 
Mi intención era usar una sola instruccion try con un "case" pero había problemas en el código, para diferenciar errores de lectura, de escritura, apertura de BB.DD.

delphi
  1. Try
  2. Zconnection1.open;
  3. Except
  4. on E:EZSQLException do
  5. begin
  6. case E.ErrorCode of
  7. 1152: ShowMessage ('Conexion abortada')
  8. 1088: ShowMessage ('Registro duplicado')
  9. else
  10. ShowMessage('Otro error no conocido' + E.Message')
  11. end;
  12. end;
  13. end;

pero EZSQLException y ErrorCode no lo identifica, los codigos para Sqlite no consigo encontrar nada.... he tenido
que cambiarlo a Exception, y eliminar el case.
Igualmente he vistos que hay que llamar a cada Excepcion, ... pero no se como aplicarlas.
Del mismo modo he podido leer que no se genera ningun codigo cuando se hace un Insert, Select, etc...
Como ya comento, al darme el error en pantalla, el DBGRID que está detrás se queda en blanco y con dos filas pequeñas, acepto mensaje de error y tengo que volver a cargar con un SELECT.... de nuevo el DBGrid fuera del Try, como puedo hacer para que si entra en la excepción de error, la pantalla anterior siga en su estado, es decir como estaba antes, con el DBGrid
cargado con todos los registros?
 
Un saludo
  • 0

#6 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 03 abril 2016 - 07:54

Te dejo un procedimiento para que puedas manejar las excepciones a tu antojo, sólo tienes que modificarlo a tu gusto:
 


delphi
  1. procedure MostrarExcepcion (E: EDatabaseError; var DataAction: TDataAction);
  2. Var
  3.  ErrNo : Integer;
  4.  Mensaje : String;
  5.  ErrCode : String;
  6. begin
  7.  ErrNo := EZSQLException(E).ErrorCode;
  8.  Mensaje := EZSQLException(E).Message;
  9.  ErrCode := IntToStr(ErrNo);
  10.  
  11.  //Aqui coloca todos los errores que deseas
  12.  //Recordar que sólo aquellos que está relacionados con BD
  13.  case ErrNo of
  14.     1 : Mensaje := 'No se pudo abrir la Base de Datos o no se encuentra.';
  15.     2 : Mensaje := 'Error de lóigica interna en SQLite.';
  16.     6 : Mensaje := 'Una Tabla en la BD está bloqueada.';
  17.     //...la lista continua
  18.  end;
  19.  MessageDlg ('Se ha producido un error.'+#13#10+'Error Nro.:'+ ErrCode + #13#10 + Mensaje , mtError,[mbOk],0);
  20.  
  21.  DataAction:=daFail;
  22. end;
  23.  
  24. //Su uso Sería:
  25. try
  26.  ZConnection1.connect;
  27. except
  28.  on E:EDatabaseError do
  29.     MostrarExcepcion(E);
  30. end;

Te dejo la lista de códigos de errores de SQLite:

 

https://www.sqlite.o...ef/c_abort.html

 

Saludos.


  • 0

#7 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 03 abril 2016 - 11:13

Gracias enecumene, en la web sqlite estuve buscando pero no lo encontré. Luego siempre que tenga una instruccion a base datos (connect, open, execSQL, Close etc...) deberé usar el Try. En mi caso como tengo en la mismas lineas de lprocedure un CREATE (execSQL), un INSERT (execSQL)  y un SELECT(open) debería hacer 3 try?

 

En cuanto a problema del DBGrid como podría darle solución, por más que intento y le tiro horas...


  • 0

#8 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 03 abril 2016 - 11:23

Gracias enecumene, en la web sqlite estuve buscando pero no lo encontré. Luego siempre que tenga una instruccion a base datos (connect, open, execSQL, Close etc...) deberé usar el Try. En mi caso como tengo en la mismas lineas de lprocedure un CREATE (execSQL), un INSERT (execSQL)  y un SELECT(open) debería hacer 3 try?

 

En cuanto a problema del DBGrid como podría darle solución, por más que intento y le tiro horas...

El DBGrid debe ir asociado a un componente DataSet..cual estas usando?

 

Si fuera un clientdataset podrias hacer un locate por el campo repetido..Ej.. si voy a insertar una localidad que ya existe, buscaria por el campo nombre...

 

De lo contrario si... debes hacer un select y corroborar que no exista.. la mayoria de las base de datos usan ci: case insensitive para estos casos..

o tu dataset tiene la propiedad params donde debes agregar los campos.. lo bueno de esto es que no necesitas hacer otra consulta.directamente recorres el dataset en ese campo...


  • 0

#9 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 03 abril 2016 - 12:04

Hola giulichajari, uso sqlite+zeos.

 

El problema viene, porque creo la tabla con un PRIMARY KEY (nombre).

Visualmente, pulso un button, carga en pantalla todo lo que tiene la tabla en un DBGrid. Ahora el problema viene si le doy a un boton "nuevo" que he creado y aparece a su derecha (panel aparte) al mismo tiempo que se visualizó el dBGRid. Si inserto el mismo codigo, me da aviso de clave repetida, pero automaticamente, el DBgrid se vacia en pantalla e incluso se reduce su tamaño, quedándo estéticamente muy mal. Para "solucionar" lo que hago es volver a cargar el DgGrid una vez acepte el mensaje de "clave repetida".

 

Yo no busco por ningun campo, sencillamente intento insertar los campos de la tabla instrucción SQL y PARAMBYNAME, si esta entra en la excepcion de error, aviso.

 

Un saludo


  • 0

#10 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 03 abril 2016 - 12:46

Hola giulichajari, uso sqlite+zeos.

 

El problema viene, porque creo la tabla con un PRIMARY KEY (nombre).

Visualmente, pulso un button, carga en pantalla todo lo que tiene la tabla en un DBGrid. Ahora el problema viene si le doy a un boton "nuevo" que he creado y aparece a su derecha (panel aparte) al mismo tiempo que se visualizó el dBGRid. Si inserto el mismo codigo, me da aviso de clave repetida, pero automaticamente, el DBgrid se vacia en pantalla e incluso se reduce su tamaño, quedándo estéticamente muy mal. Para "solucionar" lo que hago es volver a cargar el DgGrid una vez acepte el mensaje de "clave repetida".

 

Yo no busco por ningun campo, sencillamente intento insertar los campos de la tabla instrucción SQL y PARAMBYNAME, si esta entra en la excepcion de error, aviso.

 

Un saludo

Si el dbgrid se vacia.. es porque se cierra el dataset y se anula por ende la propiedad datasource... Ahora bien.. la clave primaria se declara  al crear la base de datos, y no deberia ser el nombre.. sino un tipo numerico. igualmente la primary key no puede repetirse su valor en dos registros distintos...

 

Pon tu codigo delphi aqui...para verlo


  • 0

#11 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 03 abril 2016 - 01:14

Adjunto el codigo.


php
  1. procedimiento boton, que carga dbgrid en pantalla y saca además un boton "nuevo" descrito más abajo.
  2. DataModule1.ZQuery1.Close; //Por si acaso
  3. DataModule1.ZQuery1.SQL.Text := 'CREATE TABLE IF NOT EXISTS "personas" ("nombre" VARCHAR(10) primary key, "edad" INTEGER)';
  4. DataModule1.ZQuery1.ExecSQL;
  5. Try   
  6.  
  7.  //Cerramos la tabla
  8.  DataModule1.ZQuery1.close;
  9.  DataModule1.ZQuery1.SQL.Text:= 'SELECT * FROM "personas";
  10.  DataModule1.ZQuery1.Open;
  11.  except
  12.       on E:Exception do
  13.             begin
  14.            ShowMessage('El error provocado en volcado lectura DBGRID ha sido: '+E.Message);
  15.    //Datamodule1.Zconnection1.RollBack;
  16.             end;
  17.   end; //end-Try 
  18.  
  19. -------------
  20.  
  21. procedimiento del boton nuevo
  22.  
  23. DataModule1.ZQuery1.Close; //Por si acaso DataModule1.ZQuery1.SQL.Text := 'INSERT INTO "personas" (nombre,edad) VALUES(:nombre,:edad)';
  24. Datamodule1.Zquery1.ParamByName('nombre').AsString:='Pepito';
  25. Datamodule1.Zquery1.ParamByName('edad').AsInteger:=21;
  26. DataModule1.ZQuery1.ExecSQL;
  27. Datamodule1.ZQuery1.Close;
  28. except
  29. on E:Exception do
  30. begin
  31. ShowMessage (' El nombre está duplicado': 'E.Message); //aquí ya esta el DBGrid vacio y reducido en tamaño.
  32. end;
  33. end;
  34. Datamodule1.Zquery1.Close;
  35. Datamodule1.ZQuery1.SQL.Text:= 'SELECT * FROM "personas"'; //Debo volver a cargar el DBGrid vacio y reducido anteriormente
  36. Datamodule1.Zquery1.Open; // =Datamodule1.ZQuery1.active:=true;

 

 

 


  • 0

#12 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 03 abril 2016 - 01:26

Claro.. estas ejecutando ambas sentencias en el mismo query... te recomendaria dos querys distntos..pero antes de eso prueba un 


php
  1. Zquery.sql.clear..

para limpiar el string sql..


  • 0

#13 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 03 abril 2016 - 02:04

giulichari, en este caso no aplica SQL.clear porque no está utilizando la propiedad .Add para agregar la consulta, con el close antes es más que suficiente.

 

dooper, trata de colocar los códigos por bloques y más ordenados para que todos podamos interpretarlo mejor.

 

intenta así en el primer botón:


delphi
  1. DataModule1.ZQuery1.Close; //Por si acaso
  2. DataModule1.ZQuery1.SQL.Text := 'CREATE TABLE IF NOT EXISTS "personas" ("nombre" VARCHAR(10) primary key, "edad" INTEGER)';
  3. Try
  4. DataModule1.ZQuery1.ExecSQL;
  5.  
  6. //Cerramos la tabla
  7. DataModule1.ZQuery1.close;
  8. DataModule1.ZQuery1.SQL.Text:= 'SELECT * FROM "personas"';
  9. try
  10. DataModule1.ZQuery1.Open;
  11. except
  12. on E:Exception do
  13. begin
  14. ShowMessage('Error al intentar abrir la tabla: '+E.Message);
  15. //Datamodule1.Zconnection1.RollBack;
  16. end;
  17. end;
  18. except
  19. on E:Exception do
  20. begin
  21. ShowMessage('El error provocado en volcado lectura DBGRID ha sido: '+E.Message);
  22. //Datamodule1.Zconnection1.RollBack;
  23. end;
  24. end; //end-Try

Con eso tienes un punto de partida.

 

Saludos.


  • 1

#14 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 03 abril 2016 - 02:57

enecumene
 
No entiendo porque usas un doble try-except en tu ultimo comentario, admito que no tengo muy estudiado el tema de excepciones
 
Por lo general en el codigo que escribo nunca pasa de esto:
 
1. Para casos que quiero asegurarme que, apesar de que se produzca una excepcion, se ejecute cierto codigo (por ejemplo rehabilitar un DataSet, liberar memoria, cerrar un archivo, etc)

 



delphi
  1. inicializar objetos-recursos-memoria-abrir archivos o similares
  2. try
  3. codigo que puede provocar excepciones y dejar recursos abierto o memoria sin liberar
  4. finally
  5. asegurarse que todos los recursos son liberados
  6. end;

2. Para casos en los que quiero controlar ciertas excepciones, ya sea mostrando un error personalizado, o realizando alguna otra accion especial que permita que el programa siga
 


delphi
  1. ...
  2. try
  3. codigo que puede provocar excepciones
  4. except
  5. on E: Excepcion1 do
  6. begin
  7.  
  8. end;
  9.  
  10. on E: Excepcion2 do
  11. begin
  12.  
  13. end;
  14.  
  15. // si no es ninguna de las excepciones que quiero capturar, propagarla (esto no es siempre necesario!)
  16. on E: Exception do
  17. raise E;
  18. end;

Lo mas "complejo" a lo que he llegado es a anidar los dos casos anteriores, es decir un try-finally para liberar recursos con un try-except adentro para tratar de manera especial ciertos casos particulares
 
Tambien puede que haya por ahi un doble try-finally
 
Pero nunca he escrito doble try-except como en el caso que muestras
 
En ese caso como se comporta el flujo del programa? Veo que no se vuelve a elevar la excepcion en ninguno de los dos casos, por lo que el manejador mas interno ejecuta la llamada ShowMessage('Error al intentar abrir la tabla: ' + E.Exception)
 
Y luego el programa sigue? No me ha quedado muy claro
 
Un saludo


  • 0

#15 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 03 abril 2016 - 03:21

Tomo nota Enecumene con las excepciones que apuntas en el código.

 

Con respecto al código Agustin_ortu, es cierto que en muchos sitios también he visto esa forma de codificar las excepciones, pero claro la duda que tengo es que es Excepcion1, Excepcion2,....?

 

El reto es como dejar el contenido del DBGrid cargado y visible, cuando salta a la excepción de "Registro repetido" por la clave campo "nombre" ya que estéticamente es inviable dejarlo así porque se ve detrás vacío, aunque la cerrar el aviso de "Registro repetido" aparezca nuevamente el contenido por la consulta select que obligo a recorrer la tabla.

 

Debería crear un Zquery2 para que eso no pase, como dice giulichajari? hay alguna propiedad que lo solucione?

 

un saludo


  • 0

#16 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 03 abril 2016 - 03:53

Era pseudocodigo, Excepcion1, Excepcion2 deberian ser reemplazadas por clases de Exception como EDatabaseError y similares

 

Con respecto al DBGrid vacio verifica que no te quede cerrado el DataSet que esta conectado al grid

 

No estoy seguro de como es tu codigo, pero en el otro hilo que hablabamos del DataSet y el DataSource usabas ZQuery1 para mostrar en el DBGrid

 

Ahora estas usando ese mismo ZQuery para insertar y logicamente el DBGrid se queda sin su tabla para mostrar los datos

 

Deja el ZQuery1 para mostrar y usa otro ZQuery para insertar

 

Esa es la solucion sencilla y que aparte a mi me parece correcta

 

 

Se le puede buscar la vuetla para usar un solo ZQuery? Si, combinando try-finally con un try-except


delphi
  1. try
  2. // inicializar Insert y parametros
  3. ZQuery1.SQL.Text := ' insert into ...';
  4. ZQuery1.Param ...
  5.  
  6. try
  7. ZQuery1.Open; // se intenta insertar
  8. except
  9. // puede pasar que el registro sea repetido, en ese caso mostras el mensaje de error
  10. end;
  11. finally
  12. // este bloque de codigo se ejecuta SIEMPRE (esto es lo bueno del try-finally
  13. // sea la insercion correcta o no, hay que volver a mostrar los registros en el dbgrid
  14. ZQuery1.SQL.Text := ' select * from personas';
  15. ZQuery1.Open;
  16. end;


Editado por Agustin Ortu, 03 abril 2016 - 03:56 .

  • 0

#17 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 04 abril 2016 - 10:28

Entonces Agustin_Ortu, inserto un componente ZQuery2 en el Datamodule1 y le doy la misma configuración que el Zquery1 que ya tengo?
o puedo asignar en el codigo Zquery2:=Zquery?
 
Luego se pueden crear tantos Zquery en Datamodule1 como necesitemos?
 
 
Acabo de realizar tus pasos Agustin (creo un ZQuery2 para insertar) y siempre me entra por la excepción, aun no estando repetido la clave, y efectivamente si no cierro el Zquery1.Close, el dbgrid ahora conserva en pantalla los datos, "salta" el mensaje de "clave repetida" pero siempre...


delphi
  1. //Datamodule1.ZQuery1.Close; // Si lo activo, desaparecen los datos del DgGrid
  2. DataModule1.ZQuery2.SQL.Text := 'INSERT INTO "personas" (nombre, edad) VALUES (:nombre, :edad)';
  3. Try
  4. Datamodule1.ZQuery2.ParamByName('nombre').AsString:='marianito';
  5. Datamodule1.ZQuery2.ParamByName('edad').AsInteger:=89;
  6. DataModule1.ZQuery2.ExecSQL;
  7.  
  8. except
  9. on E:Exception do
  10. begin
  11. ShowMessage('El nombre está duplicado!: '+E.Message);
  12. // Datamodule1.Zconnection1.RollBack;
  13. end;
  14. end; //end-Try
  15. DataModule1.ZQuery2.close;

Un saludo


  • 0

#18 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 04 abril 2016 - 11:35

Entonces Agustin_Ortu, inserto un componente ZQuery2 en el Datamodule1 y le doy la misma configuración que el Zquery1 que ya tengo?
o puedo asignar en el codigo Zquery2:=Zquery?
 
Luego se pueden crear tantos Zquery en Datamodule1 como necesitemos?
 
 
Acabo de realizar tus pasos Agustin (creo un ZQuery2 para insertar) y siempre me entra por la excepción, aun no estando repetido la clave, y efectivamente si no cierro el Zquery1.Close, el dbgrid ahora conserva en pantalla los datos, "salta" el mensaje de "clave repetida" pero siempre...


delphi
  1. //Datamodule1.ZQuery1.Close; // Si lo activo, desaparecen los datos del DgGrid
  2. DataModule1.ZQuery2.SQL.Text := 'INSERT INTO "personas" (nombre, edad) VALUES (:nombre, :edad)';
  3. Try
  4. Datamodule1.ZQuery2.ParamByName('nombre').AsString:='marianito';
  5. Datamodule1.ZQuery2.ParamByName('edad').AsInteger:=89;
  6. DataModule1.ZQuery2.ExecSQL;
  7.  
  8. except
  9. on E:Exception do
  10. begin
  11. ShowMessage('El nombre está duplicado!: '+E.Message);
  12. // Datamodule1.Zconnection1.RollBack;
  13. end;
  14. end; //end-Try
  15. DataModule1.ZQuery2.close;

Un saludo

 

Puedes poner tantos querys, tables, conections, etc como quieras y necesites.

No hagas por código algo como:

 

Query2 := Query1;

 

Si lo haces estarás asignando en la variable Query2 la dirección de memoria del Query1. Por tanto, cualquier cosa que hagas con él Query2 será equivalente a que lo hagas con el Query1.

Simplemente pon el Query en el DataModule o el form que necesites, configura como lo necesites y úsalo.

 

No te deja insertar más registros ya que definiste como clave primaria al campo Nombre. De ese modo, cualquier intento de insertar un nuevo registro con el mismo nombre te lo rechaza.

Por lo general la clave primaria es un campo adicional que uno dispone como clave artificial (es decir, un campo nuevo inventado) para permitir unicidad. Por ello es habitual llamar a dichos campos ID.

 

Te sugiero que la estructura de la tabla sea como esta:

 

Tabla: Personas

---------------------

ID: INTEGER NOT NULL (PRIMARY KEY)

Nombre: VARCHAR

Edad: INTEGER

 

No recuerdo si es que SQLite soporta o tiene mecanismos para autoincremental, pero si lo soporta quizá sea buena idea que el campo ID así lo sea.

 

Saludos,


  • 0

#19 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 04 abril 2016 - 11:55

Muy buenas conclusiones saco de tus comentarios Delphius. Cierto es que no creo un ID, porque no quiero "emborronar" más el código y sobre el que ya dispongo y que funciona con una simple tabla(nombre y edad) hacer pruebas de toma de contacto como insertar, crear, etc... que es lo que ahora estoy haciendo... de ahí que no haya creado un ID.

 

De igual modo, el caso es que incluso un "nombre" distinto, es decir no repetido, se bifurca por la excepción, saltando el mensaje ShowMessage('El nombre está duplicado!: '+E.Message); cuando en realidad no está duplicado en la BB.DD y

debería insertarlo.

 

un saludo


  • 0

#20 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 04 abril 2016 - 12:08

El problema es que estas capturando "cualquier excepcion"

 

Como dijiste que habias leido documentacion y libros crei que esto ya lo sabias, pero en el ejemplo lo deje claro:

 


delphi
  1. ...
  2. try
  3. codigo que puede provocar excepciones
  4. except
  5. on E: Excepcion1 do
  6. begin
  7.  
  8. end;
  9.  
  10. on E: Excepcion2 do
  11. begin
  12.  
  13. end;
  14.  
  15. // si no es ninguna de las excepciones que quiero capturar, propagarla (esto no es siempre necesario!)
  16. on E: Exception do
  17. raise E;
  18. end;

 

Dentro de un bloque try-except, en la seccion del except es donde se capturan las excepciones

 

Las excepciones en Pascal son objetos, y como tal, siguen las mismas reglas de herencia y polimorfismo que los "objetos comunes"

 

Del mismo modo que existe el cuento "todos los objetos son TObject (la clase de la que todos los objetos heredan implicita o explicitamente)", tambien existe el "todas las excepciones son Exception"

 

Exception es una clase, de la cual se desprenden un monton de excepciones especificas

 

Revisa este fragmento de codigo


delphi
  1. try
  2. // codigo
  3. except
  4. on E: Exception do
  5. begin
  6. // codigo cuando se produce una excepcion
  7. end;
  8. end;

Si tradujera ese codigo a prosa, seria lo siguiente: Ejecutar "codigo, si se produjera algun error de tipo Exception, ejecutar codigo cuando se produce una excepcion"

 

Ahora bien, revisa este fragmento de codigo:


delphi
  1. try
  2. // codigo
  3. except
  4. on E: EErrorBaseDeDatos do
  5. begin
  6. // codigo que se ejecuta cuando ocurre una excepcion EErrorBaseDeDatos
  7. end;
  8.  
  9. on E: ErrorDeDisco do
  10. begin
  11. // codigo que se ejecuta cuando ocurre una excepcion ErrorDeDisco
  12. end;
  13.  
  14. on: E: EErrorMemoriaCorrupta do
  15. begin
  16. // codigo que se ejecuta cuando ocurre una excepcion EErrorMemoriaCorrupta
  17. end;
  18. end;

En este fragmento de codigo, para cada tipo especial de excepcion, se la trata de una manera especifica. En el primer fragmento de codigo (y en el tuyo) todas las excepciones se atienden de la misma forma, diciendo que "el registro esta duplicado".

 

Y el problema es que seguramente el verdadero error, la verdadera excepcion, no necesariamente puede ser la que esperas (registro duplicado)

 

Como sabes que no fue una EOutOfMemory? Como sabes que no fue una EZeroDivide? Como sabes que no fue una EInvalidPointer? Como sabes que no fue una EAccessViolation?

 

Eso es porque escribir un try-except a secas, o un try-except capturando E: Exception, tu codigo se come todas las excepciones y las mete todas en la misma bolsa

 

Por otra parte, veo que en el cuadro de dialogo imprimes el mensaje de la excepcion, asi que deberias estar viendo en pantalla algo como "Registro duplicado" y a continuacion el mensaje "original" de la excepcion. Que dice ese mensaje?


Editado por Agustin Ortu, 04 abril 2016 - 12:10 .

  • 0




IP.Board spam blocked by CleanTalk.