Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Uso adecuado de Rollback (https://www.clubdelphi.com/foros/showthread.php?t=16232)

rochi 20-11-2004 03:44:25

Uso adecuado de Rollback
 
Hola a todos, estoy comenzando con Firebird (v1.5) y si bien la voy llevando tengo ciertas dudas. Estoy haciendo una aplicación con Delphi 7, uso los componentes IBX y no estoy usando CachéUpdate.
Params. de la transacción: readcommited, nowait,rec_version,concurrency.
Los errores los capturo en en evento OnException de la aplicación, todo esto funcionando al pelo.
En este caso particular, el Commit está al cerrar el form, no en el AfterPost o AfterDelete.
Si bien a fuerza de ensayo y error, lecturas varias, incluso mensajes de este foro he sacado algunas conclusiones, aun tengo ciertas lagunas.
Ahora bien, intentando ver como funciona el 'bloqueo' y las transacciones abrí 2 ventanas de mi aplicación (el .exe) para simular digamos un funcionamiento en red. Llamemosle aplicacion 1 y 2 respectivamente.

Supongamos ambas modifican el mismo registro, pero una realiza un post antes que la otra (aun no se realizó el Commit), la 1 x ej. Luego la 2 al intentar hacer el post, lanza la excepción que es capturada y todo lindo.
Mi duda es si las modificaciones hechas por 2, deben ser deshechas con Rollback, o eso se aplica solo si falla el Commit o que otros casos es aplicable. (Aun trabajando desde una sola aplicación, sin otra aplic. concurrente, cuando esto en modo de edición de la tabla, al cancelar los cambios, ya es suficiente, desaparecen sin necesidad de hacer Rollback)

Bueno, fue extenso, espero nadie haya quedado dormido, pero es para que se entienda. Gracias
saludos a todos, rochi

athlontado 20-11-2004 10:56:14

Lo ideal es mantener las transacciones abiertas el menor tiempo posible. Claro que, como dice el refrán: "Del dicho al hecho hay un gran trecho".

El problema está en que, si haces Commit cada vez que guardas, modificas ó borras algún registro, los componentes DataSet se te cierran y tienes que volver a abrirlos uno a uno, con los consiguientes inconvenientes. Con los IBX no es posible mostrar los datos teniendo la transacción cerrada, pero sí podemos reducir a unas milésimas de segundo el tiempo que permanecen los cambios a la BBDD en el limbo, forzando a que éstos se guarden nada más hacer un Post ó un Delete.

Para evitar que se cierren los DataSets cada vez que hacemos Commit ó RollBack, se crearon los métodos "...Retaining", que hacen lo mismo que sus análogos, pero manteniendo los DataSets abiertos y los registros mostrados en su posición original en los DataSources y controles enlazados a éstos.

Yo lo haría de la siguiente forma:

Código Delphi [-]
  // Cuando llegamos aquí, la Transacción está abierta para poder mostrar los datos ...
 
 tTabla.Edit;
 tTabla.FieldByName('NOMBRE').AsString := 'Pepito Grillo';
 tTabla.Post;
 
 // Esto es lo que yo añadiría, que puede ir perfectamente en el
 // AfterPost y AfterDelete ...
 try
   CommitRetaining
 except
   RollBackRetaining
 end;

rochi 20-11-2004 16:06:48

Hola athlontado :) , gracias x responder. Entiendo lo que decís, sobre el estado en que quedan los DataSets y la necesidad que duren lo menos posible las transacciones. Sobre este último punto, estoy en 'falta', ya que como dije, hago el Commit al cerrar el Form donde se realizan las ABM => no necesito mantener el estado del DataSet pq me voy. Quizás no sea una buena práctica.

Y tu código, creo que responde mi pregunta: en que casos es aplicable el Rollback/RolbackRetaining => solo cuando falla el Commit/CommitRetaining.
try
CommitRetaining
except
RollBackRetaining
end;

Es decir, antes de hacer un Post, si decido cancelar datos, no tiene sentido hacer RollBack. No veo otros casos....quizás por mi inexperiencia.

Gracias, y bienvenida cualquier sugerencia
Saludos

jhonny 20-11-2004 19:54:40

Cita:

Empezado por rochi
Es decir, antes de hacer un Post, si decido cancelar datos, no tiene sentido hacer RollBack. No veo otros casos....quizás por mi inexperiencia.

Como yo entiendo esto que haz dicho es cierto, pero
te puedo poner un ejemplo de como usar las transacciones(ya que no encuentro la forma de explicar en un ejemplo el Rollback sin poner el StartTransaction o incluso el Commit) desde un punto de vista:

Imaginate que necesitas hacer una aplicacion en la que tienes que guardar una tabla maestra(DOC) y una tabla detalle(DOCDET) (estas relacionadas al "Documento" que el usuario esta digitando) y que ademas deba llenar otras dos tablas(CONTA y CONTADET) que estan relacionadas tambien al mismo "Documento" pero en las cuales guardaras información contable....

En dichas tablas deben haber llaves foraneas(Recuerden que es un ejemplo), indicando por ejemplo que el codigo de IVA que se guardara en la tabla CONTADET debe existir en la tabla llamada IVA.

Nota: Podemos extender un poco mas el ejemplo utiizando Tquery's para guardar los registros en las tablas pero temo que esto se puede poner algo Tedioso haciendolo de esa manera, asi que solo usaremos el DataSet.POST simulando que hemos trabajado con un TTable.

Entonces, supongamos que en el Boton Guardar(btGuardar) ponemos el siguiente codigo.

Código Delphi [-]
procedure TForm1.btnGuardarClick(sender :TObject);
begin
   tblDOC.Post;
   tblDOCDET.Post;
   tblCONTA.Post;
   tblCONTADET.Post;
end;

en teoria, eso guardaria correctamente los registros en dichas tablas, pero ¿que sucede si el codigo de IVA que tiene que guardar en la tabla CONTA no existe en la tabla llamada IVA?

R//.
  1. Pues se guardaran solo los registros de la tabla DOC y DOCDET y los de las tablas CONTA y CONTADET no.
  2. Debido a esto se "Desincronizara" la información que se debe mostrar en posiblemente futuros reportes.
  3. Mostraria un horrible error de INTEG_### que asustaria a cualquiera (De hecho deberia hacerlo).
El tercer problema tiene una solución muy sencilla, podriamos poner por ejemplo:

Código Delphi [-]
procedure TForm1.btnGuardarClick(sender :TObject);
begin
   try
     tblDOC.Post;
     tblDOCDET.Post;
     tblCONTA.Post;
     tblCONTADET.Post;
   except
 
   end;
end;

Y listo, no mostraria el temido error, pero ¿ese en realidad es el verdadero problema?

R//. No, el problema real es que debemos cuidar la integridad de nuestra aplicación y garantizar de que se guardaron correctamente los datos o no.

Para eso podriamos utilizar un "Bloque protegido con transacciones", podria ser algo como esto:

Código Delphi [-]
procedure TForm1.btnGuardarClick(sender :TObject);
begin
   //Donde DB puede ser un componente como un TDataBase
 
   DB.StartTransaction;//Comienza una transacción
   try
     tblDOC.Post;
     tblDOCDET.Post;
     tblCONTA.Post;
     tblCONTADET.Post;
     DB.Commit;//Si todo sale bien...
   except
     DB.RollBack;//Si algo sale mal, no guarde nada en la base de datos.
     raise;//De esta manera mostraria el horrible error ocurrido (De hecho es 
    //necesario que lo muestre). Nota: con un poco mas de investigación 
    //y de ingenio se puede poner mas bonito y 
    //entendible para el usuario final;).
   end;
end;

De esta manera podemos garantizar que todos o ninguno de los datos seran guardados.

Espero me haga entender y que ademas te sirva de algo.

rochi 20-11-2004 20:10:55

Clarito, fue un buen ejemplo. Gracias.
Saludos :) rochi


La franja horaria es GMT +2. Ahora son las 12:01:17.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi