• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
T2 - JDBC
 

T2 - JDBC

on

  • 1,072 views

 

Statistics

Views

Total Views
1,072
Views on SlideShare
1,065
Embed Views
7

Actions

Likes
0
Downloads
49
Comments
0

1 Embed 7

http://utnprogramacion.hol.es 7

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    T2 - JDBC T2 - JDBC Presentation Transcript

    • JDBC Daniel Pecos Martínez [email_address] Curso JDBC Febrero 2011
    • 1. Estándar JDBC
      • JDBC es el API de Java que permite el acceso a cualquier fuente de datos tabulados, ya sea una base de datos, hojas de cálculo o fichero plano.
      • Utiliza SQL es el lenguaje estándar que se utiliza para acceder a los datos mediante JDBC.
      • Gracias a esto, se consigue una separación limpia de datos y lógica de aplicación. Además encapsula el método de acceso a los datos, seguridad y acceso concurrente de múltiples usuarios.
    • 1.1 Arquitectura y componentes
      • JDBC está diseñado siguiendo un modelo de 2 capas, separando lógica de datos, por lo que conseguimos que las aplicaciones sean mucho portable y adaptable a otras fuentes de datos JDBC.
      • Es semejante a ODBC (Open DataBase Connectivity), un estándar de acceso a base de datos basado en C. Ambas APIs son interoperables, cada una puede hacer uso de la otra.
      • Debido a que el modelo de dos capas es poco flexible en cuanto a escalbailidad, JDBC también soporta un diseño basado en 3 capas, el cual es utilizado principalmente por aplicaciones JEE. 
    • 1.1 Arquitectura y componentes
      • Las capas definidas en el modelo de 3 capas son:
        • Capa cliente : con la lógica necesaria para conectar con la capa intermedia; también se le suele denominar thin layer o capa fina
        • Capa intermedia : contiene toda la lógica de gestión de conexiones y consultas; esta capa junto con la siguiente pueden ser vistas como un submodelo de dos capas
        • Capa servidor : en JDBC es el gestor de la base de datos
    • 1.1 Arquitectura y componentes
    • 1.1 Arquitectura y componentes
    • 1.1 Arquitectura y componentes
    • 1.1 Arquitectura y componentes
    • 1.1 Arquitectura y componentes
        • Los componentes más destacables de la arquitectura JDBC son:
          • Driver y DriverManager
          • Connection
          • Statement y sus variantes
          • ResultSet
    • 1.2 Drivers
        • Los drivers implementan la lógica necesaria para interactuar con los distintos gestores de bases de datos disponibles en el mercado
        • Son implementados por los propios fabricantes (generalmente), ya que son ellos los que mejor pueden optimizar el funcionamiento de sus productos
        • Se distribuyen como archivos JAR, los cuales deben estar disponibles en el classpath de la aplicación o del servidor de aplicaciones
    • 1.2 Drivers - Tipos
      • En función de cómo se hayan implementado los drivers, se habla de 4 familias o tipos distintos:
        • Tipo 1: Puente JDBC-ODBC
        • Tipo 2: Acceso a API nativa
        • Tipo 3: Driver multicapa
        • Tipo 4: Implementación 100% Java
    • 1.2 Drivers - Tipos - Tipo 1
      • Implementación java que utiliza un driver ODBC para conectar con la BBDD
      • Ventajas:
        • Fácil conexión
        • Conexión directa con BBDD
      • Inconvenientes:
        • Penalización en rendimiento
        • Necesidad de instalar driver ODBC en cliente
        • Más lento que otros tipos de driver
    • 1.2 Drivers - Tipos - Tipo 2
      • Implementación java que utiliza la librería cliente del gestor de BBDD (JNI)
      • Ventajas:
        •   Mayor rendimiento que el tipo 1
      • Inconvenientes:
        •   La librería cliente debe estar instalada en el cliente
        • No todos los gestores ofrecen estas librerias cliente
        • El driver depende de la plataforma
    • 1.2 Drivers - Tipos - Tipo 3
      • Implementación java que se comunica con una capa intermedia (servidor de aplicaciones), y ésta a su vez con el gestor de BBDD.
      • Ventajas:
        • El código cliente no depende del gestor de BBDD a utilizar (el cliente puede conectar a distintas BBDD)
        • Funciones de optimización en capa intermedia
      • Inconvenientes:
        • Precisa código específico al gestor en la capa intermedia
        • La capa intermedia puede convertirse en un cuello de botella
    • 1.2 Drivers - Tipos - Tipo 4
      • Implementación java que se comunica directamente con el gestor de BBDD.
      • Ventajas:
        • Es el que tiene un mayor rendimiento en comunicaciones
        • Facilita mucho el depurado ya que todo el código está controlado por la JVM
      • Inconvenientes:
        • Los clientes precisan de un driver concreto para cada BBDD
        • Los drivers son específicos a una BBDD
    • 1.2 Drivers - Tipos
    • 1.3 URLs
        • Las URLs (Unified Resource Link) se utilizan para obtener conexiones con las bases de datos que queremos utilizar. 
        • En general, una URL se utiliza para localizar un recurso en Internet
        • En el caso de JDBC, su formato viene definido en el estándar (aunque los fabricantes pueden introducir pequeñas variantes)
      • jdbc:[nombre_driver]:[data_source]
      • ( data_source identifica la instancia de base de datos que deseamos utilizar)
    • 2. API JDBC
      • Los pasos que se deben seguir para realizar una conexión a una base de datos y lanzar una query son:
        • Carga del Driver
        • Obtención de una conexión a la URL mediante el DriverManager o un DataSource
        • Crear un Statement a partir de la conexión
        • Lanzar una query mediante el Statement creado
        • Leer los resultados devueltos por la query, si aplica
        • Liberar recursos
    • 2.1 Carga de Drivers
      • Para cargar un driver JDBC, simplemente debemos cargar la clase que implementa el driver en ClassLoader , la JVM se encargará de registrarla como controlador JDBC. 
      • Class.forName("com.acme.databaseDriver")
      • Antes de JDBC 4.0 era necesario indicarle al DriverManager que registrara la clase como driver:
      • DriverManager.registerDriver( new com.acme.databaseDriver());
      ¿Cómo se os ocurre que la JVM determinará que una clase es un driver JDBC en el estándar JDBC 4.0?
    • 2.1 Carga de Drivers
      • Un detalle importante: 
        • Si utilizamos DriverManager para cargar el driver, la clase debe estar disponible en tiempo de compilación del código que lo va a utilizar
        • Por contra, utilizando Class.forName la clase debe estar disponible solo en tiempo de compilación, por lo que es posible crear código que se adapte a distintos drivers sin necesidad de modificaciones
    • 2.2 Conexiones
      • Existen dos formas distintas de obtener una conexión a una base de datos:
        • Mediante DriverManager + URL:
        • Connection con = DriverManager.getConnection(url, ...)
        • Mediante JNDI + DataSource
      • Context ctx = InitialContext();
      • DataSource ds = ctx.lookup(jndi)
      • Connection con = ds.getConnection(...)
    • 2.2 Conexiones
      • La opción de utilizar un DataSource es habitual en aplicaciones JEE, donde es importante la separación entre capas y la reutilización de recursos para optimizar rendimientos.
      • Es en el servidor de aplicaciones donde se da de alta o deploya un DataSource configurado contra una base de datos. Cuando el servidor arranca, inicializa un pool de conexiones contra dicha base de datos, de forma que las aplicaciones que piden una conexión al DataSource la obtienen de este pool, y cuando la liberan, esta conexión vuelve a estar disponible en el pool.
      • Si una aplicación intenta recuperar una conexión y el pool está vacío, la aplicación queda bloqueada hasta que obtiene una.
    • 2.2 Conexiones
      • Es por este motivo por el que es muy recomendable que el código que trabaja contra base de datos tenga un coste temporal mínimo y que esté incluido en un bloque try/catch como el siguiente:
      • Connection con = null;
      • try {
      •      // código de conexión y consulta de datos
      • } catch (SQLException e) {
      •      // tratamiento de errores
      • }  finally {
      •      if (con != null) {
      •          try {
      •              con.close();
      •          } catch (SQLException e) {
      •              // tratamiento de errores
      •          }
      •      }
      • }
    • 2.2 Conexiones
    • 2.2 Conexiones
    • 2.2 Conexiones - Metadatos
      • A partir de una Connection es posible obtener un conjunto de metadatos sobre la base de datos a la que se ha conectado mediante:
      • DatabaseMetaData meta = connection.getMetadata();
      • Estos metadatos incluyen, entre otros muchos:
        • Nombre y versión de la base de datos
        • Capacidades y limitaciones
        • Listado de tablas
    • 2.2 Conexiones - Metadatos
      • Ejemplo de uso de metadatos para obtener un listado de tablas disponibles en un schema:
      • DatabaseMetaData meta = connection.getMetaData();
      • System.out.println("Listado de tablas:");
      • ResultSet rs = meta.getTables(null, "TECNOCOM", null, 
      •      new String[] {"TABLE"}
      • );
      • while (rs.next()) {
      •      System.out.println ("· Tabla TECNOCOM." + 
      •          rs.getString("TABLE_NAME")
      •      );
      • }
      • rs.close();
    • EJERCICIO
      • Los datos necesarios para conectar contra nuestra base de datos derby son:
        •   Home: 
          • System.setProperty("derby.system.home", "C:curso_jdbc");
        •   Driver:
          •   org.apache.derby.jdbc.EmbeddedDriver
          •   org.apache.derby.jdbc.ClientDriver
        • URL:
          • jdbc:derby:ddbb_curso_jdbc
          • jdbc:derby://localhost:1527/ddbb_curso_jdbc
        • Usuario / Password
          • curso / curso
      Realizar un programa Java que haga una conexión contra una base de datos JavaDB (derby) y muestre algunos de sus metadatos.
    • DERBY Derby es un gestor de bases de datos relacionales OpenSource completamente implementada en Java. Es un proyecto de Apache y Oracle / Sun crearon un derivado (JavaDB) de la versión 10.5.3.0 incluido a partir de la JDK 6. Todos los datos relacionados con una base datos en Derby están persistidos en un directorio de la máquina donde se ejecuta, asegurando la integridad de éstos, además de su portabilidad entre distintos sistemas operativos y arquitecturas. Puede ser embebida en una aplicación Java o ejecutada como un servidor independiente. En función de este modo, debemos utilizar un driver diferente (incluido en el propio gestor de bases de datos), así como una cadena de conexión distinta. Derby : http://db.apache.org/derby/ JavaDB : http://www.oracle.com/technetwork/java/javadb/overview/index.html
    • 2.3 Excepciones SQL
        • Ante cualquier error que se produzca, JDBC lanzará una excepción SQLException (o derivada)
        • Es recomendable tener un control centralizado de excepciones que aporte suficiente información sobre el error para que pueda ser resuelto fácilmente
        • No es buena idea dejar bloques catch sin un tratamiento adecuado de la excepción
        • Repito, no es buena idea dejar bloques catch sin un tratamiento adecuado de la excepción
    • 2.3 Excepciones SQL
      • Ejemplo de código para tratamiento de excepciones:
      • public static void printSQLException(SQLException ex) {
      • Exception e = ex;
      • do {
      • if (e!= null && e instanceof SQLException) {
      • System.err.println("Estado SQL: " + ((SQLException)e).getSQLState());
      • System.err.println("Codigo Error: " + ((SQLException)e).getErrorCode());
      • System.err.println("Mensaje: " + ((SQLException)e).getMessage());
      • //e.printStackTrace(System.err);
      • Throwable t = ex.getCause();
      • while(t != null) {
      • System.err.println("Causa: " + t);
      • t = t.getCause();
      • }
      • }
      • e = ex.getNextException();
      • } while (e != null);
      • }
    • 2.3 Excepciones SQL
        • Los SQLWarning son excepciones que heredan de SQLException que se utilizan para informar de avisos del gestor de bases de datos
        • Las clases que pueden reportar SQLWarning, mediante un método getWarnings(), son:
          • Connection
          • Statement
            • PreparedStatement
            • CallableStatement
          • ResultSet 
        • Es aconsejable hacer siempre un tratamiento de estos avisos cada vez que se trabaje con instancias de las interfaces anteriores
    • 2.3 Excepciones SQL
      • Ejemplo de código para tratamiento de avisos:
      • public static void printWarnings(SQLWarning warning) throws SQLException { if (warning != null) { System.out.println("n---Warning---n"); while (warning != null) { System.out.println("Message: " + warning.getMessage()); System.out.println("SQLState: " + warning.getSQLState()); System.out.print("Vendor error code: "); System.out.println(warning.getErrorCode()); System.out.println(""); warning = warning.getNextWarning(); } } }
    • 2.3 Excepciones SQL Resúmen de excepciones SQL
    • EJERCICIO Reorganiza el código que tienes hecho utilizando una clase de utilidades (métodos estáticos) de forma que tu código principal quede de esta forma: public static void main(String[] args) { Connection connection = null; try { connection = JDBCUtils.getConnection(); /* NUESTRO CODIGO AQUI */ } catch (SQLException e) { JDBCUtils.printSQLException(e); } finally { JDBCUtils.closeConnection(connection); } }
    • EJERCICIO Mejora la gestión de errores que ya tenías implementado en el ejercicio anterior.
    • 2.4 Sentencias
      • La ejecución de sentencias SQL se realiza utilizando objetos de tipo Statement , PreparedStatement o CallableStatement .
        • Statement : se suelen utilizar para invocar sentencias que no requieren parámetros o para sentencias de un solo uso
        • PreparedStatement : son sentencias que se precompilan y a las que se les puede pasar parámetros, por lo que mejoran la eficiencia cuando se invocan de forma reiterada
        • CallableStatement : se utilizan cuando se quiere invocar un procedimiento almacenada de la base de datos. Hereda directamente de PreparedStatement
    • 2.4 Sentencias
      • Para obtener una instancia de cualquiera de los tipos de sentencia, debemos hacer uso de la interfaz Connection:
      • Statement:
      •      connection.createStatement()
      • PrepareStatement:
      •      connection.prepareStatement(SQL)
      • CallableStatement:
      •      connection.prepareCall(SQL)
    • 2.4 Sentencias - Métodos
        • boolean execute(String sql) : Ejecuta la query y devuelve true si la query ha devuelto resultados; estos resultados se deben recuperar con el método getResultSet()
        • ResultSet executeQuery(String sql) : Ejecuta la query y devuelvel conjunto de resultados generado
        • int executeUpdate(String sql) : Ejecuta la query y devuelve el número de filas afectadas por la ejecución
        • void addBatch(String sql) : Encola una query que formará parte de un batch
        • boolean executeBatch() : Ejecuta todas las querys que se hayan encolado en un batch
    • EJERCICIO
      • Crea el código necesario para crear tablas, mostrar las tablas de la base de datos y borrar las tablas existentes.
      • Amplia la clase de utilidad para ofrecer estas funcionalidades, haciendo el tratamiento oportuno de errores y avisos.
      • Crearemos una tabla "Personas" con los siguientes campos:
        • ID: int (auto incremental) (PK)
        • NOMBRE: string (40)
        • APELLIDO: string (60)
        • EMAIL: string (15)
        • FECHANACIMIENTO: fecha
      • Inserta algunos datos de prueba.
    • EJERCICIO CREATE TABLE tecnocom.personas ( id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), nombre VARCHAR(40) NOT NULL, apellidos VARCHAR(60), telefono VARCHAR(15) NOT NULL, fechaNacimiento DATE, PRIMARY KEY (id) )
    • EJERCICIO DERBY requiere crear un SCHEMA previamente para almacenar las tablas, de forma que luego éstas serán accesibles mediante NOMBRE_SCHEMA.NOMBRE_TABLA. Para crear un schema hay que lanzar la sentencia: CREATE SCHEMA TECNOCOM y para borrarlo: DROP SCHEMA TECNOCOM RESTRICT
        • Son sentencias que se precompilan , por lo que su ejecución es más rápida que la de las sentencias normales
        • Añaden un sobrecoste de precompilación, por lo que solo deben ser utilizadas cuando se vaya a lanzar repetidas veces la misma sentencia
        • Pueden indicarse que se utilizarán parámetros en el momento de crear el PreparedStatement . Para ello se debe utilizar ? en la posición donde se quiera fijar el parámetro:
      • SELECT * FROM Personas WHERE nombre = ?
      2.4 Sentencias - Prepared Statements
        • El valor de los parámetros que se hayan especificado en la sentencia debe ser fijado antes de ejecutarla
        • Para ello debemos utilizar alguno de los distintos métodos que ofrece PreparedStatement para fijar el valor, en función del tipo del parámetro:
          • void setBoolean(int index, boolean x)
          • void setInt(int index, intx)
          • void setFloat(int index, float x)
          • void setString(int index, String x)
          • void setDate(int index, Date x)
        • Si queremos eliminar los valores fijados anteriormente, podemos hacer uso de clearParameters()
        • NOTA: El índice de los parámetros empieza siempre por 1
      2.4 Sentencias - Prepared Statements
      • Ejemplo de creación y uso de un PreparedStatement:
      • PreparedStatement pStmt = connection.prepareStatement(
      • "INSERT INTO TECNOCOM.PERSONAS" +
      • " (nombre, apellidos, telefono, fechaNacimiento)" +
      • " VALUES (?, ?, ?, ?)"
      • );
      • pStmt.setString(1, "Pepe");
      • pStmt.setString(2, "Gotera");
      • pStmt.setString(3, "666777888");
      • pStmt.setDate(4, getDate(1980, 1, 5));
      • pStmt.execute();
      2.4 Sentencias - Prepared Statements
    • 2.4 Sentencias - Prepared Statements Método que crea un objeto java.sql.Date a partir del año, mes y día: private static Date getDate(int year, int month, int day) { Calendar cal = Calendar.getInstance(); cal.set(cal.YEAR, year); cal.set(cal.MONTH, month-1); cal.set(cal.DATE, day); return new Date(cal.getTimeInMillis()); }
    • EJERCICIO Modifica el código que realiza las inserciones de los datos de prueba para que utilice PreparedStatements
    • 2.4 Sentencias - Batch Updates
        • JDBC permite empaquetar múltiples sentencias update/insert para que sean enviadas y procesadas en una única petición
        • Las sentencias añadidas al batch solo pueden devolver el número de filas afectadas, nada más. En caso contrario, se lanzara un BatchUpdateException
        • Todos los tipos de statements soportan esta característica
    • 2.4 Sentencias - Batch Updates
        • Pasos para ejecutar un batch:
          • Lo primero que hay que hacer es desactivar el autocommit de la conexión
          • después debemos añadir al batch las sentencias mediante el método addBatch()
          • a continuación debemos invocar el método executeBatch() que retornará un vector de enteros con el número de filas afectadas por cada sentencia
          • por último debemos hacer un commit()  sobre la conexión para que los cambios sean efectivos en la base de datos
    • EJERCICIO Escribe el código necesario para hacer múltiples inserciones en modo batch
    • 2.5 Conjuntos de resultados
        • Un ResultSet es un objeto que almacena los resultados obtenidos mediante una query
        • Son accesibles "al estilo tabla": contienen cabeceras de columnas, tipos y valores
        • Los datos dentro de un ResultSet son accesibles mediante un cursor, el cual se puede desplazar hacia delante y hacia atrás (si así se configura)
        • Además, también es posible que un ResultSet se actualice de forma automática ante cambios ocurridos en la base de datos
        • Así pues, tenemos estas opciones:
          • TYPE_FORWARD_ONLY
          • TYPE_SCROLL_INSENSITIVE
          • TYPE_SCROLL_SENSITIVE
    • 2.5 Conjuntos de resultados
        • Es posible hacer modificaciones directamente sobre los ResultSet obtenidos y que estos cambios se reflejen en la base de datos. Para definir esta capacidad del driver, deberemos consultar si está soportado mediante el método DatabaseMetaData.supportResultSetConcurrency() :
          • CONCUR_READ_ONLY: El ResultSet no puede ser modificado programáticamente
          • CONCUR_UPDATABLE: Sí que puede ser modificado
    • 2.5 Conjuntos de resultados
        • Por último, también es posible configurar el ResultSet para que cuando se finalice la transacción, este siga siendo utilizable. Se deberá hacer un cerrado explícito del ResultSet :
          • HOLD_CURSORS_OVER_COMMIT: mantiene el ResultSet activo después de finalizar la transacción; esta opción se considera poco eficiente
          • CLOSE_CURSORS_AT_COMMIT: cierra el ResultSet con el fin de transacción
        • El valor por defecto lo obtendremos mediante DatabaseMetaData.getResultSetHoldability()
    • 2.5 Conjuntos de resultados
        • Para realizar todas estas configuraciones sobre el ResultSet, deben indicarse cada una de ellas al crear el Statement del que obtendremos el ResultSet:
      • Statement stmt = connection.createStatement(
      • ResultSet.TYPE_SCROLL_INSENSITIVE,
      • ResultSet.CONCUR_UPDATABLE,
      • ResultSet.CLOSE_CURSORS_AT_COMMIT);
    • 2.5 Conjuntos de resultados
        • La forma de acceder a los datos de un ResultSet es posicionando el cursor al registro que nos interese y recuperando el valor de cada columna por separado (con los métodos getXXX() del ResultSet )
        • Es posible acceder a la columna, bien por nombre, bien por posición (siendo la primera columna la número 1)
      • ResultSet rs = stmt.executeQuery(
      • "SELECT * FROM TECNOCOM.PERSONAS WHERE id=1"
      • );
      • while (rs.next()) {
      • System.out.println(rs.getString("nombre") + 
      • " " + 
      • rs.getString("apellidos"));
      • }
    • 2.5 Conjuntos de resultados - Metadatos
        • Los conjuntos de resultados ofrecen un conjunto de metadatos sobre los datos que han recuperado. Entre otros:
          • Número total de resultados
          • Número de columnas de los registros
          • Nombre y tipo de cada columna
        • Para acceder a estos metadatos debemos utilizar el objeto  ResultSetMetaData que nos devuelve el método  getMetaData del ResultSet:
      • ResultSetMetaData meta = rs.getMetaData();
    • 2.5 Conjuntos de resultados Principales métodos de ResultSet
    • 2.5 Conjuntos de resultados Principales métodos de ResultSet (Cont.)
    • EJERCICIO Recupera e imprime por pantalla todos los datos que hayas insertado en la tabla PERSONAS
    • EJERCICIO Crea un método que imprima todas las tablas de la base de datos y su contenido. Crea un segundo método que borre las tablas y el schema Tecnocom.
    • EJERCICIO
      • Crea distintos statements haciendo uso de distinto tipos de cursores y comprueba su funcionamiento.
        •   TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, TYPE_SCROLL_SENSITIVE
        •   CONCUR_READ_ONLY, CONCUR_UPDATABLE
    • 2.6 Transacciones
      • En el caso que estemos obteniendo las conexiones mediante el DriverManager, deberemos desactivar el autocommit de las sentencias:
      • Connection.setAutoCommit(false);
      • Si por contra obtenemos las conexiones de un DataSource (dado de alta en un servidor de aplicaciones), este paso no está permitido, ya que el control de transacciones se realiza desde el servidor.
    • PRÁCTICA - Diagrama de entidades
    • PRÁCTICA - Diagrama de capas
    • PRÁCTICA
        • Diseño de la aplicación por capas:
          • Capa de presentación:  InterfazConsola
          • Capa de negocio: Coleccion
          • Capa de acceso a datos: JDBC
        • Las sentencias que se utilicen deben estar optimizadas (código java de mayor rendimiento posible) y dentro de transacciones si aplica (p.ej. creación de estructura de tablas)
    • 2.6 Transacciones
      • Las transacciones son un mecanismo que permite al desarrollador agrupar sentencias en conjuntos, de forma que todas las ejecuciones de estas sentencias deben funcionar correctamente, ya que en caso contrario ninguna de ellas será efectiva.
      • Los tres métodos que ofrece JDBC para gestionar transacciones son:
        • Connection.commit() : Envía al gestor de bases de datos el conjunto de sentencias incluidas en la transacción
        • Connection.setSavePoint(String nombre) : Crea un punto al que volver en un posible rollback
        • Connection.rollback(Savepoint sp) / Connection.rollback() : Hace marcha atrás de las sentencias hasta un determinado savepoint dentro de la transacción o de la transacción completa