Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Transacciones. Duda Existencial. (https://www.clubdelphi.com/foros/showthread.php?t=91901)

usuario1000 30-05-2017 16:40:04

Transacciones. Duda Existencial.
 
Hola a todos.

Estoy realizando una aplicación en Delphi con FireDac (cliente - Servidor[ Base de datos FireBird 2.5]), decir que es la primera vez que hago este tipo de aplicaciones, y ya llevo creado bastante código y probado con las tablas de la base de datos que funcionan bien, pero no utilizo las transacciones. ¿es necesario utilzarlas?. Se supone que la aplicación trabajará en una red Local. Sobre las tablas hago todo tipo de operaciones, insertar, consultar, eliminar, modificar.

Necesitaría un poco de luz, para no tener que reescribir todo el codigo que tengo ya terminado.

Saludos y gracias.

Neftali [Germán.Estévez] 30-05-2017 17:48:35

Bueno la respuesta no es que SI ni que NO.
Es depende de si las necesitas...

Para saber si las necesitas primero debes tener claro qué es una transacción y para qué se usa.
Si te encuentras en ese caso, la necesitas, si no te encuentras en ese caso, no la necesitas.

Ejemplo más común, que seguramente aparecerá en tu aplicación:
Tienes registros de una tabla relacionados con otra (maestro/detalle) o una relación 1-1.
De forma que al hacer una inserción se deben insertar los datos de las dos tablas o de ninguna; Podría pasar que los registros de la primera se inserten correctamente, pero al insertar los de las segunda se produzca un error y estos no lleguen a insertarse.
Te quedaría la cosa "a medias", por decirlo así.
Eso se arregla con una transacción que englobe las 2 operaciones.

mamcx 30-05-2017 22:36:57

Cita:

Empezado por usuario1000 (Mensaje 517360)
pero no utilizo las transacciones. ¿es necesario utilzarlas?.

Deberias estudiar sobre SQL y bases de datos relacionales. Son herramientas muy versatiles y poderosas, que son altamente INFRA-VALORADAS y sub-utilizadas.

Y si, necesitas transacciones. De hecho, usas transacciones sepas o no.

santiago14 02-06-2017 22:26:44

Totalmente sí. Debes usar las transacciones, inclusive para las Consultas (Select...)

Una estructura muy usada y bastante buena en Delphi es: (Suponiendo que ya estás conectado y todo eso)

Código Delphi [-]
    try
      transaccion.StartTransaction;
      nuevo_cliente(cliente, consulta); //Este es un Insert
      transaccion.Commit;
      application.MessageBox('Cliente ingresado con éxito.',
        'Clientes', MB_OK + MB_ICONINFORMATION);
    except
      on E:Exception do
      begin
        transaccion.Rollback;
        application.MessageBox('No se pudo dar de alta al cliente. ' + e.Message,
        'Clientes', MB_OK + MB_ICONINFORMATION);
      end;
    end;

Funciona bastante bien y se acaban la mayoría de los problemas.
Recordar, siempre hacer las operaciones contra la BBDD en una transacción. SIEMPRE.

Santiago.

AgustinOrtu 03-06-2017 01:45:32

El StartTransaction debe ir fuera del bloque try; ya que si no puede iniciarse la transaccion, no podras hacer el Rollback

La documentacion de Embarcadero recomienda hacerlo de esta manera:

Código Delphi [-]
FDConnection1.StartTransaction;
try
  FDQuery1.ExecSQL;
  ....
  FDQuery1.ExecSQL;
  FDConnection1.Commit;
except
  FDConnection1.Rollback;
  raise;
end;

Se puede reemplazar el FDConnection por un componente de Transaction, no hay problema.

AgustinOrtu 03-06-2017 01:47:45

Cita:

Empezado por santiago14 (Mensaje 517593)
Totalmente sí. Debes usar las transacciones, inclusive para las Consultas (Select...)

Por otra parte, porque utilizar una Transaccion para lecturas? Excepto en casos en los que explicitamente se desee bloquear al resto para que no puedan actualizar hasta que la aplicacion termine de consumir esos datos; pero en general todos deberian poder leer sin bloquear al resto

Casimiro Notevi 03-06-2017 08:58:12

Yo no suelo hacerlo implícitamente, sino que dejo a los componentes que se encarguen ellos automáticamente, incluyo en un datamodule principal un componente de bases de datos y otro de transacciones, ambos enlazados entre sí y me despreocupo de mantener yo las transacciones.
Vista en texto sería esto:

Código:

object DB1: TIBDatabase');
  DatabaseName = ''localhost:192.168.1.100:\datos\unabasededatos.fdb''' );
  Params.Strings = (');
    ''user_name=SYSDBA''');
    ''password=masterkey''');
    ''lc_ctype=ISO8859_1'')');
  LoginPrompt = False');
  DefaultTransaction = TR1');
  SQLDialect = 3');
  Left = 157');
  Top = 56');
end');
object TR1: TIBTransaction');
  DefaultDatabase = DB1');
  Params.Strings = (');
    ''read_committed''');
    ''rec_version''');
    ''nowait'')');
  Left = 218');
  Top = 56');
end');


Neftali [Germán.Estévez] 06-06-2017 08:42:14

Cita:

Empezado por santiago14 (Mensaje 517593)
Debes usar las transacciones, inclusive para las Consultas (Select...)

¿Hay alguna razón para utilizar las consultas en SELECT? Tal vez se me escapa, pero a primeras yo tampoco le veo sentido...
Vas a sobrecargar el sistema con una lógica que no necesita y a la que no le vas a sacar ningún rendimiento.

Casimiro Notevi 06-06-2017 09:35:38

Cita:

Empezado por Neftali (Mensaje 517748)
¿Hay alguna razón para utilizar las consultas en SELECT? Tal vez se me escapa, pero a primeras yo tampoco le veo sentido...
Vas a sobrecargar el sistema con una lógica que no necesita y a la que no le vas a sacar ningún rendimiento.

Por ejemplo un select "gordo" que tarda unos segundos y mientras tanto están insertando nuevos registros, o borrándolos, en otros terminales.

De forma predeterminada, firebird usa transacciones siempre, incluso select. No hay que llamarlas implícitamente.

santiago14 06-06-2017 13:47:02

Cita:

Empezado por Casimiro Notevi (Mensaje 517749)
Por ejemplo un select "gordo" que tarda unos segundos y mientras tanto están insertando nuevos registros, o borrándolos, en otros terminales.

De forma predeterminada, firebird usa transacciones siempre, incluso select. No hay que llamarlas implícitamente.

Y sí. Firebird hace de manera automática la transacción. Por lo cual, al final de tu consulta deberías hacer un Commit, o un Rollback.

Casimiro Notevi 06-06-2017 16:04:01

1 Archivos Adjunto(s)
Cita:

Empezado por santiago14 (Mensaje 517790)
Y sí. Firebird hace de manera automática la transacción. Por lo cual, al final de tu consulta deberías hacer un Commit, o un Rollback.

Bueno, depende de los componentes que uses, por ejemplo con los IBX puedes indicar un valor por defecto para los casos en que no lo indiques tú mismo y entonces no tienes que hacer nada.
La propiedad "DefaultAction" del componente TIBTransaction.

Neftali [Germán.Estévez] 06-06-2017 17:47:29

Cita:

Empezado por Casimiro Notevi (Mensaje 517749)
Por ejemplo un select "gordo" que tarda unos segundos y mientras tanto están insertando nuevos registros, o borrándolos, en otros terminales.
De forma predeterminada, firebird usa transacciones siempre, incluso select. No hay que llamarlas implícitamente.

Cita:

Empezado por santiago14 (Mensaje 517790)
Y sí. Firebird hace de manera automática la transacción. Por lo cual, al final de tu consulta deberías hacer un Commit, o un Rollback.


Que FB lo haga implícitamente es una decisión interna, pero entiendo que a nivel general un usuario no debe usar transacciones para SELECT.
Esto me parece la excepción dentro de los SGBD's. ¿no?

Casimiro Notevi 06-06-2017 18:07:19

Cita:

Empezado por Neftali (Mensaje 517811)
... entiendo que a nivel general un usuario no debe usar transacciones para SELECT. ...

Por supuesto que no necesita usarlas.

usuario1000 04-12-2017 16:08:46

Buenas a todos, y gracias de antemano.

Retomo este asunto porque no me queda nada claro como se puede hacer esto. He leido, he remirado y he visto videos tostonazos en youtube, pero al final nada de nada.

Tengo realizada al 99% una apicación Cliente - Servidor. Y no he utilizado nunca en toda la aplicación expresamente las transacciones y me hubiera gustado, pero no he sabido en que momento utilizarlas.

Mi aplicación funciona aproximadamente de la siguiente forma:

Para dar de alta a un proveedor llamo a un una función como por ejemplo:

resultado:= funcion.altaProveedor(nombre, apellidos, nif, domicilio. .... etc.); // esta función está dada de alta en la parte del servidor.
if resultado = true /
todo correcto se ha grabado bien.
else
"Error a la hora de grabar".


resultado := funcion.grabarOtraCosa(m, n, o,p);
if resultado = true // todo ok
else
// error.

¿Donde pongo el "startTransaction" para hacer el commit o el rollback?

Ademas la cosa se complica, cuando esa operación anterior se realiza con muchas más tablas que deben ser todas modificadas, y si falla la grabación de una, la liamos parda.

Cada vez que utiizo una función (de las anteriores), modifica la base de datos de forma instantanea, sin posibilidad de hace rollback.


Espero que se me entienda, lo que pretendo es asegurar la integridad de los datos, pero con las funciones que utilizo, una detras de otra, puede darse el caso que alguna falle y eso... la liemos.

Alguna sugerencia de como poder utilizar las transacciones aquí.

Gracias.

NOTA: Es un aplicación DATASNAP, firebird y con delphi "Berlin".

Casimiro Notevi 04-12-2017 18:10:27

Echa un vistazo a esto.

usuario1000 30-10-2018 20:59:36

Gracias a todos por vuestra paciencia.


Retomo este tema porque aún no lo tengo claro.


Supongo que vuestras aclaraciones son buenas cuando la aplicación no es DATASNAP, es decir Cliente - Servidor y todos los componentes están accesibles, pero en mi caso no están accesible.



Me surge la siguiente duda:



- El control de las transacciones las hago desde el Cliente supongo, y no desde el Servidor. Y si las tengo que hacer desde el Cliente tengo que insertar en el formulario(donde realizo una llamada a una función de alta que se ejecuta en el servidor),un componente TFDTransaction y aquí viene el problema, ¿como le digo a este componente que se conecte con el componente de "TFDConnection" que está en el "formulario ServerMethods1" del programa SERVIDOR.



Perdonad que siga insitiendo, pero hasta que no resuelva este asunto no puedo seguir programando puesto que veo un riesgo muy alto introducir datos desde distintos terminales que tocan varias tablas sin realizar transacciones para asegurar la información.


Gracias de antemano a todos.

mamcx 30-10-2018 21:13:31

Ok. Transacciones es realmente algo simple. Es como tener certeza que el mundo no ha cambiado mientras diste unos pasos mas.

Si tienes un vble:

Código SQL [-]
a = 1
a = 2
total = 1 + a

puedes pensar que se ejecuta todo en el exacto orden que estas leyendo, y que total terminara en 3.

Pero en un ambiente paralelo o asincronico y al acceder una variable mutable accesible por MULTIPLES (posibles) PROCESOS:

Código SQL [-]
Proceso 1: a = 1
Proceso 1: a = 2
Proceso 2: a = 0 
Proceso 1: total = 1 + a

no puedes presumir que el orden de las acciones es el que estas leyendo en codigo. Porque multiples procesos pueden, en cualquier momento, "cambiar el mundo" bajo tus pies.

Asi, que mientras este solamente afectando una sola "variable"

Código SQL [-]
Proceso 1: a = 1

puedes dejarlo en manos del componente / motor.

Pero si necesitas mantener la CERTEZA que el mundo no ha cambiado, ten en cuanta que el motor NO PUEDE ASUMIR:

Código SQL [-]
12:00.1am  Proceso1: a = 1
12:00.2am  Proceso1:a = 2
12:00.3am  Proceso1total = 1 + a

porque totalente pude ser:

Código SQL [-]
12:00.1am  Proceso1: a = 1
4:00 pm  Proceso1:a = 2


asi que para indicarle que un conjunto de instrucciones REALMENTE son secuenciales, debes encapsularlas en una transaccion:

Código SQL [-]
TRASACCION 1
   12:00.1am  Proceso1: a = 1
   12:00.2am  Proceso1:a = 2

Asi, que si, es necesario usarlo incluso en SELECT, si hay UN CONJUNTO DE SELECTS que logicamente DEBEN LEER SOBRE EL MISMO CONJUNTO DE DATOS.

Si no, tendras una "lectura fantasma".


Debes leer sobre las propiedades ACID para completar tu entendimiento:

https://es.wikipedia.org/wiki/Aislamiento_(ACID)

usuario1000 30-10-2018 21:41:26

Cita:

Empezado por mamcx (Mensaje 529331)
Ok. Transacciones es realmente algo simple. Es como tener certeza que el mundo no ha cambiado mientras diste unos pasos mas.

Si tienes un vble:

Código SQL [-]a = 1 a = 2 total = 1 + a


puedes pensar que se ejecuta todo en el exacto orden que estas leyendo, y que total terminara en 3.

Pero en un ambiente paralelo o asincronico y al acceder una variable mutable accesible por MULTIPLES (posibles) PROCESOS:

Código SQL [-]Proceso 1: a = 1 Proceso 1: a = 2 Proceso 2: a = 0 Proceso 1: total = 1 + a


no puedes presumir que el orden de las acciones es el que estas leyendo en codigo. Porque multiples procesos pueden, en cualquier momento, "cambiar el mundo" bajo tus pies.

Asi, que mientras este solamente afectando una sola "variable"

Código SQL [-]Proceso 1: a = 1


puedes dejarlo en manos del componente / motor.

Pero si necesitas mantener la CERTEZA que el mundo no ha cambiado, ten en cuanta que el motor NO PUEDE ASUMIR:

Código SQL [-]12:00.1am Proceso1: a = 1 12:00.2am Proceso1:a = 2 12:00.3am Proceso1total = 1 + a


porque totalente pude ser:

Código SQL [-]12:00.1am Proceso1: a = 1 4:00 pm Proceso1:a = 2



asi que para indicarle que un conjunto de instrucciones REALMENTE son secuenciales, debes encapsularlas en una transaccion:

Código SQL [-]TRASACCION 1 12:00.1am Proceso1: a = 1 12:00.2am Proceso1:a = 2


Asi, que si, es necesario usarlo incluso en SELECT, si hay UN CONJUNTO DE SELECTS que logicamente DEBEN LEER SOBRE EL MISMO CONJUNTO DE DATOS.

Si no, tendras una "lectura fantasma".


Debes leer sobre las propiedades ACID para completar tu entendimiento:

https://es.wikipedia.org/wiki/Aislamiento_(ACID)


Hola Mamcx, en lineas generales sé que hacen las transacciones y te agradezco tu aclaración, pero mi problema es como conectar un componente que tengo en el Cliente con otro componente que tengo en el Servidor para poder usar el componente de Transacciones en el Cliente.


Gracias.

usuario1000 07-11-2018 17:30:29

Al hilo de lo anterior me surge una nueva duda y es la siguiente:


- Si un usuario realiza un cambio en la base de datos y por lo que sea se ejecuta el ROLLBACK, ¿esto afectaría a otro usuario que este realizando cambios en ese mismo momento en la base de datos?. Quiero entender que son sesiones diferentes y aunque la base de datos es única para todos los usuarios, unos no interfieren con los otros, no?


Gracias a todos.

Casimiro Notevi 07-11-2018 18:51:14

Cada uno ve "el mundo" en el momento que inició la transacción. Pero ve el mundo como estaba en ese momento confirmado. Si un usuario inicia una transacción y otro usuario inicia otra transacción, aunque el primero cancele los cambios, el segundo seguirá viendo lo que había cuando lo inició.
Resumiendo, solamente lo que se confirma (commit) es lo que ven todos.


La franja horaria es GMT +2. Ahora son las 23:24:46.

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