Gestión de Transacciones en Jakarta EE: Lo básico

Gestión de Transacciones en Jakarta EE: Lo básico

La gestión de transacciones es fundamental e integral en las aplicaciones empresariales para garantizar la preservación de la consistencia e integridad de los datos. Una transacción, desde el punto de vista de una base de datos, representa una unidad lógica de trabajo que abarca una o múltiples operaciones de base datos(insertar, update, borrar).

Jakarta EE, al igual que su predecesor JavaEE, ofrece una abstracción integral para gestionar transacciones, ya sea programáticamente a través de las API de JTA o de manera declarativa mediante anotaciones. Esta última es más popular debido a su simplicidad y la capacidad de mantener límites de transacción claros sin abrumar el código lógico del negocio. En este artículo, exploraremos la anotación @Transactional en Jakarta EE, y veremos  diferentes comportamientos bajo varias configuraciones y algunas mejores prácticas.

La Anotación @Transactional

La anotación @Transactional es la que ofrece las capacidades de gestión de transacciones declarativas de Jakarta EE. Básicamente, cuando un método está decorado con @Transactional, Jakarta EE se asegura de que se inicie una transacción antes de la ejecución del método y posteriormente se confirme si la ejecución continúa normalmente, o se revierta si se produce una excepción.

Por defecto, la anotación @Transactional tiene un comportamiento de propagación REQUERIDO. Es decir, Si hay una transacción activa, únase a ella. Si no hay una transacción activa, inicie una nueva.

Vease:

https://jakarta.ee/specifications/transactions/2.0/apidocs/jakarta/transaction/transactional

Ejemplo:

@Stateless
public class MyService {

  @Inject
  private MyRepository repository;
  
  @Transactional
  public void performBusinessOperation() {
    // lógica del negocio
    repository.save(entity);
  }
}

Repository Code.

@Stateless
public class MyRepository {

  @PersistenceContext
  private EntityManager entityManager;
  
  @Transactional
  public void save(MyEntity entity) {
    entityManager.persist(entity);
  }
}

En el ejemplo anterior, invocar performBusinessOperation() inicializa una nueva transacción. Cuando save() se llama posteriormente dentro de esa transacción, save() se une a la transacción existente en lugar de iniciar una nueva debido a su comportamiento de propagación REQUERIDO por defecto.

Sin embargo, vale la pena señalar que el uso superfluo de anotaciones @Transactional, especialmente cuando las transacciones ya están definidas en la capa de servicio, puede provocar confusión y comportamientos no anticipados. Por lo tanto, generalmente es una buena práctica definir transacciones a nivel de operaciones comerciales (como en la capa de servicio).

Manejo de errores (RollBack/Deshacer)

La anotacion @Transactional no es magica, hay alcances y caracteristicas que se deben manejar como el asilamiento de la persistencia de datos y el RollBack, esto se logra lanzando excpeciones del tipo Runtime, si usa una excepcion custom, esta debe heredar o ser del tipo Runtime.

Mejores Prácticas

Los límites de transacción deben reflejar los requisitos comerciales. Por lo general, es recomendable iniciar una nueva transacción para cada operación comercial distinta. Las demarcaciones de transacciones suelen ser mejor ubicadas en la capa de servicio, que se alinea bien con la comprensión de las operaciones comerciales. Por otro lado, anotar cada método a nivel de repositorio puede hacer que cada operación CRUD sea transaccional y, en consecuencia, puede crear una multitud de transacciones breves.

Sin embargo, vale la pena considerar el costo de una transacción. Cuanto más larga sea una transacción, más tiempo persistirán los bloqueos en los datos, limitando las posibilidades de acceso concurrente a los datos.

Conclusión

Todo lo que es entendible es hackeable: Un sólido entendimiento de las transacciones y @Transactional es indispensable para diseñar aplicaciones empresariales robustas. Seleccionar límites y configuraciones precisas para sus transacciones es crucial para mantener la integridad de los datos, el rendimiento y la escalabilidad de sus aplicaciones. Si bien Jakarta EE simplifica significativamente las complejidades de la gestión con la anotación @Transactional, ser consciente de sus comportamientos bajo diferentes configuraciones es crucial.


Como conectar JasperStudio a SAP HANA

JasperStudio trae precargado controladores para base de datos, pero no el de SAP HANA.

NOTA: Recuerda que los Data-sources son independientes, por lo que se debe configurar por cada datasource a menos que se agreguen las librerías en las carpetas del programa. Eso lo veremos en otra ocasión.

En este articulo damos por hecho que ya haz descargado e iniciado JasperStudio

Paso 1. Descargar el jar del JDBC

Lo mas importante es tener obtener el driver correcto, si no estas utilizando Maven o Gradle lo puedes descargar manualmente desde:

https://mvnrepository.com/artifact/com.sap.cloud.db.jdbc/ngdbc

Vista de Maven Central:

NOTA : Hacer click en VIEW ALL

Una vez entrar a la lista de archivos descargar el correspondiente.

Vista de los archivos posibles a descargar.

Una vez descargado ubicarlo en una ruta apropiada según su forma de trabajo, como una carpeta para todos los jars o para todos los JDBC o por proyecto.

Referencia:
https://help.sap.com/docs/SAP_HANA_PLATFORM/0eec0d68141541d1b07893a39944924e/ff15928cf5594d78b841fbbe649f04b4.html

https://developers.sap.com/tutorials/hana-clients-jdbc.html

Paso 2. Configurar en JasperStudio.

Lo primero que debemos hacer es Agregar el DataSource.

Dado que estamos haciendo una conexión manual, sin usar jasperreport o un servidor EE, en la ventana que aparecerá elegiremos JDBC Connection:

JasperStudio: Ventana de opciones para nuevo adaptador

.

JasperStudio: Ventana de asistente para nueva conexión JDBC

Si hacemos click en el combobox del driver aparecerán muchas opciones pero no la de SAP HANA, por lo que debemos agregarlo manualmente.

Primero le pondremos el nombre: Conexion a SAP HANA Pruebas. y luego se hace click en el tab: ” Driver ClassPaht”

JasperStudio: Asistente de configuración de adaptador.

Al hacer click aparecerá una ventana de búsqueda para localizar el JAR, el que acabamos de descargar, el driver para SAP HANA.

Una vez localizado Regresamos al tab inicial yconfiguramos.

JasperStudio: Asistente de configuración de adaptador. Propiedades
  • 1) hacer click en el tab “Database Location”
  • 2) JDBC Driver Class: com.sap.db.jdbc.Driver
  • 3) JDBC URL: jdbc:sap://192.168.30.100:305 (la ruta y puerto del nuestro servidor)
  • 4) El username
  • 5) la clave
  • 6) Hacemos click en Test.
JasperStudio: Test OK

Si todo está correcto deberá aparecer este mensaje.

Paso 3. Probar.

Una vez configurado nuestro driver y el DataSource ya podemos probar nuestro reporte con datos.

¿como probamos el reporte?

JasperStudio : Ventana principal de trabajo.

En esta ventana, en el punto uno seleccionamos la vista de nuestro proyecto para ver todos los reportes.
En el punto 2, seleccionamos el reporte que nos interesa.

En el punto 3 podemos ver y/o editar la consulta del reporte.

y en el punto 4: es para ver la vista previa del reporte, si tiene parámetro, este pedirá que ingrese el valor para ejecutar la consulta apropiada.

Agregar imágenes en un reporte de JasperReport

Una de las características de Jaspersoft Reports es la capacidad de incluir gráficos estáticos (logotipos, por ejemplo) en los informes completos. Estos archivos gráficos (jpg, png, etc) normalmente se especifican en el archivo fuente del reporte el archivo JRXML registrándolos por referencia, lo que significa que lo que se almacena es la ruta absoluta o relativa mas el nombre de archivo, no el gráfico en sí. En una implementación empresarial o enfocado en reportes la norma es que el archivo fuente y los archivos de gráficos auxiliares se cargan en un servidor JasperReports para su ejecución.

Dependiendo de nuestra solución o del requerimiento podemos usar recursos externos relativos o cargar los datos desde una base de datos. En este post vamos a dar ejemplos de 4 escenarios y mostrando como hacerlo.

Imagen en JasperReport : Estas son las opciones del contenedor de imagen.

Opción 1 . Usando recursos Externos

Un recurso externo es la forma mas común de colocar una imagen en el reporte

Opción 2: Incrustando la imagen en el reporte (Base64)

Paso 1: codifica tu imagen en base64

Definición: Base64 es un estandar que permite tomar un archivo binario y codificarlo en caracteres ASCII. Como sabemos los archivos XML tienen limitaciones en cuanto a lo que pueden contener, por lo que codificar el archivo de imagen binario en base64 proporciona una forma de incrustar los datos de la imagen en XML manteniendonos dentro del ASCII de XML. Hay muchas opciones de codificar; en este caso puedes usar este codificador de imágenes Base64 online, al terminar lo que obtendrá es una cadena de texto que comienza así:

data:image/jpeg;base64,/9j/4AAQSkZJRAABCCCQEAY...

Esta cadena llamada URI DE DATOS contiene detalles al principio que no forman parte de los datos de la imagen en sí. Desde el inicio hasta la coma incluida – data:image/jpeg;base64,– debe eliminarse. El resto de la cadena son los datos de imagen codificados en base64 que vamos a utilizar.

imagen convertida en Base64

Paso 2: coloque los datos de la imagen base64 en una variable de informe

Primero debemos crear la variable.

Creando una variable en JasperReport.
Asignamos nombre a la variable, y dejamos el resto de los campos iguales.

Se deja todos los demás valores iguales (el nombre de clase de valor es java.lang.String, sin función de cálculo o tipo de incremento, el tipo de restablecimiento es Reporty sin datos en la expresión de valor inicial o el nombre de clase de fábrica de incrementador).

Paso 3: Agregar imagen al informe

Haga clic y arrastre un elemento de imagen desde la Paleta hasta el informe. Aparece un cuadro de diálogo “Crear nuevo elemento de imagen” con varias opciones para el “Modo de creación de imagen”, incluido un recurso de espacio de trabajo, una ruta absoluta o una URL. Elija la última opción, “Expresión personalizada”, e ingrese este fragmento de Java a continuación. Hay un lugar en el fragmento donde se incluye la variable con la imagen codificada en base64,  reemplácelo con el nombre de la variable del paso anterior.

Seleccione “Expresión personalizada” y pegue el fragmento de Java.
Expresion

Paso 4: agregue la importación de clase Base64 al informe

Último paso… para usar la Base64.decodeBase64función cuando se ejecuta el informe, necesitamos importar explícitamente esa clase cuando se ejecuta el informe. En el editor de informes hay pestañas para “Diseño”, “Fuente” y “Vista previa”. Haga clic en “Fuente” para ver el JRXML sin procesar. Debajo de la última línea que comienza con <property name="</code">y encima de la <querystring>línea, agregue esta línea:

<import value="org.apache.commons.codec.binary.Base64"></import>
Agregando la importacion de clase Paso 01.

Hemos terminado: Guarde y obtenga una vista previa de su informe, y verá la imagen incluida en el informe.

Las otros métodos de inserción desde la base de datos se describe en el siguiente post.

JasperReport: Usar imágenes dinámicas desde base de datos (Base64)

JasperReport: Usar imágenes dinámicas desde base de datos (Base64)

La forma mas común de usar logos o imágenes dinámicas en los reportes es que sean administrados via base de datos, en las cuales pueden almacenarse en binario y codificados en Base64. A continuación te mostraremos como hacerlo en estos 2 escenarios.

Software Utilizado: JasperStudio 6.19.0 de 2022.

Paso 1: Base de datos

Lo primero que necesitamos es tener nuestra tabla con los datos, en este ejemplo utilizaremos postgresql.

Estructura de la tabla Imágenes:

Paso 2: Importar los Campos

Se importan los campos al diseño/

Paso 3. Definir el elemento imagen.

El objeto imagen tiene varias formas de importar la imagen.

usaremos la opción 5: Expresión Personalizada

Escenario 1: Binario

Para mostrar una imagen de un campo binario es extremadamente fácil solo hay que poner la referencia del campo en la expresión como fuente.

Propiedad del Elemento Imagen.
Ventana de selección de campos.

eso es todo, la conversion se hace automáticamente.

Escenario 2: Base64

Aunque actualmente es raro que alguna empresa use imágenes se guardadas en Base64 tambien es posbible mostrarlas.

Para este escenario se requieren unos pasos adicionales.

Paso 1. Importar las librerías

JasperReport nos permite realizar programación dentro de los reportes, sea con Java o con Groovy, para poder manipular las cadena Base64 recuperada dela base de datos se necesitará solo una librearía.

En la ventana de imports se agregan las librerías.
Importando java.util.Base64

Luego agregamos la expresión personalizada al control de la imagen.

Eso es todo.

Persistencia de datos con Sqlite implementando JDBC.

Introducción

Hablar acerca de persistencia de datos con Sqlite, es considerar que la mayoría de aplicaciones requieren almacenar información. El objetivo puede ser la toma de decisiones, como es el caso de programas empresariales; o para mantener persistente una configuración de la misma aplicación. Podríamos requerir que esta información se almacene al lado del cliente o del servidor. En este artículo veremos cómo persistir nuestro datos del lado del cliente utilizando SQLite con Java.  Ahora que sabemos qué es SQLite y hemos dado nuestros primeros pasos con Maven, vamos a crear un pequeño proyecto en Java. A este proyecto lo nombraremos “jsqlite”. En él podremos hacer persistencia de datos con Sqlite implementando o utilizando JDBC puro.

El proyecto “jsqlite” estará desarrollado en Java y tendrá las siguientes características:

  • Portable
  • Lo podremos ejecutar desde una terminal
  • Creará la base de datos en el caso que no existiera.
  • Creará una tabla en el caso que no existiera.
  • También podremos agregar, listar, borrar y actualizar registros.
  • Está basado en patrones de diseño MVC y Singleton.

¿Qué es JDBC?

Java Database Connectivity es una API de Java con una colección de interfaces y clases. Nos ayuda a que los programas Java accedan a sistemas de gestión de bases de datos.
Las aplicaciones desarrolladas en Java, mediante las interfaces y clases estándar de JDBC, pueden hacer consultas escritas en el lenguaje de consulta estructurada (SQL). ¿Quieres saber más?

Desarrollo de jsqlite (Nuestro Proyecto).

Iniciamos el proyecto de persistencia de datos con Sqlite.

Iniciamos creando la estructura de directorios base. Utilizando Maven digitamos desde la terminal la siguiente instrucción:

mvn archetype:generate  -DgroupId=ni.jug -DartifactId=jsqlite -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Nos quedará la siguiente estructura de directorios:

. 
├── pom.xml 
└── src 
   ├── main 
   │   └── java 
   │       └── ni 
   │           └── jug 
   │               └── App.java 
   └── test 
       └── java 
           └── ni 
               └── jug 
                   └── AppTest.java

Crearemos las carpetas controller, view y model dentro de la carpeta main/java/ni/jug/

mkdir main/java/ni/jug/util
mkdir main/java/ni/jug/controller
mkdir main/java/ni/jug/view
mkdir main/java/ni/jug/model

Modificación el pom.xml

Modificamos el archivo pom.xml, agregamos la dependencia para el driver de SQLite. También agregamos el plugin maven-assembly-plugin para la construcción del jar y agregamos la propiedad para que al momento de compilar Maven utilice la versión de Java 11. Nos quedará de la siguiente manera:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>ni.jug</groupId>
    <artifactId>jsqlite</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jsqlite</name>
    <url>http://maven.apache.org</url>
    <dependencies>
    <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
      <dependency>
         <groupId>org.xerial</groupId>
         <artifactId>sqlite-jdbc</artifactId>
         <version>3.28.0</version>
      </dependency>
    </dependencies>
   <build>
     <plugins>
        <!-- Configuracion del plugin para contruccion del jar -->
        <plugin>
           <artifactId>maven-assembly-plugin</artifactId>
           <version>3.1.1</version>
           <configuration>
              <!-- Le decimos a maven que agrege en el manifiesto cual es la clase que contiene el metodo main -->
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                     <mainClass>ni.jug.App</mainClass>
                  </manifest>
                </archive>
                <!-- Le decimos a maven que incluya las dependencia en el jar -->
                <descriptorRefs>
                     <descriptorRef>jar-with-dependencies</descriptorRef>
                 </descriptorRefs>
              </configuration>
             <executions>
                <execution>
                   <id>assemble-all</id>
                    <phase>package</phase> 
                    <goals>
                        <goal>single</goal>
                    </goals>
                 </execution>
              </executions>
            </plugin>
        </plugins>
   </build>
   <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>

Conexión con la base de datos.

Vamos a crear un clase con el nombre ConnectionBD y estará ubicada en main/java/ni/jug. Esta clase hará la conexión a la base de datos. Aunque SQLite soporta que múltiples procesos lean la base de datos, solo puede haber un proceso de escritura, por lo utilizaremos el patrón de diseño Singleton para evitar múltiples conexiones a la base de datos.

package ni.jug;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
 * Establece la conexion con la base de datos y nos devuelve la conexion por el metodo
 * @author gacs
 */
public class ConnectionBD {
    private static ConnectionBD connectionBD=null;    //inilicializamos la clase de conexion 
    private final String strUrl;                      //Almacena la direccion de la base de datos
    private final String strDriver ;                  //Almacena el driver de conexion
    private static Connection conn=null;
    private ConnectionBD() {
    	this.strDriver="org.sqlite.JDBC"; //Driver de conexion
    	strUrl="jdbc:sqlite:var/db/mybd.db"; //Parametros de conexion de conexion
    	setConnection(); //Establece la conexion y la almacena en la propiedad conn
	}
    public static Connection getConnection(){
           if(connectionBD==null)
        	   connectionBD=new ConnectionBD(); 
           return conn;
    }
    /**
     * metodo de conexion con la base de datos
     * Almacena la conexion en la propiedad conn
     */
    private void setConnection() {
        try {
            Class.forName(strDriver);
            conn=DriverManager.getConnection(strUrl);
        }catch(ClassNotFoundException | SQLException e){
               System.err.println(e);
        }		   
  }
}

Crearemos la carpeta ../var/db dentro la carpeta donde hemos creado el proyecto, al mismo nivel de la carpeta src y del archivo pom.xml. La estructura de directorio se verá así:

.
├── src
│   ├── main
│   │   └── java
│   │       └── ni
│   │           └── jug
│   │               ├── controller
│   │               ├── model
│   │               ├── util
│   │               └── view
│   └── test
│       └── java
│           └── ni
│               └── jug
└── var
    └── db

Persistiendo datos con el patrón de diseño MVC.

Modelo

Crearemos una clase que nos servirá de modelo. Esta clase tendrá el nombre de Persona y estará ubicada en la carpeta main/java/ni/jug/model. A continuación el código que contiene la clase Persona:

package ni.jug.model;
/**
 * Clase modelo Persona 
 * @author gacs
 *
 */
public class Persona {
	private int id;
	private String nombre;
	private String apellido;
	private String sexo;
    public Persona() {
	}
	public Persona(int id, String nombre, String apellido, String sexo) {
		this.id = id;
		this.nombre = nombre;
		this.apellido = apellido;
		this.sexo = sexo;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNombre() {
		return nombre;
	}
	public String getNombreCompleto() {
		return nombre+" "+apellido;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public String getApellido() {
		return apellido;
	}
	public void setApellido(String apellido) {
		this.apellido = apellido;
	}
	public String getSexo() {
		return sexo;
	}
	public void setSexo(String sexo) {
		this.sexo = sexo;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Persona other = (Persona) obj;
		if (id != other.id)
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "[ "+id+"  "+  nombre +"  "+apellido + "  "+  sexo + "]";
	}
}

 

Vistas

Crearemos ahora las clases que nos darán la vista de los datos, como también las vistas que interactúan con el usuario. Estas clases estarán ubicadas en la carpeta main/java/ni/jug/view. Las clases que utilizaremos como vista son:

  • NewPersona: Esta clase se utiliza para que el usuario agregue un nuevo registro en la tabla persona.
  • ListPersona: Lista los registro de la tabla persona.
  • EditPersona: Con esta clase podremos editar y modificar un registro de la tabla persona.
  • DeletePersona: Con esta clase podremos borrar un registro almacenado en la tabla persona.

NewPersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
/**
 * Agrega registro a la tabla
 * @author gacs
 *
 */
public class NewPersona {
	private Persona pm; 
	public NewPersona(Persona pm) {
            this.pm = pm;
	}
	/**
	 * Este metodo es llamado por el controlador
	 */ 
    public void setRegister() {
        Scanner reg = Input.getInstance();
        System.out.println( "Nombre:" );
        pm.setNombre(reg.next());
        System.out.println( "Apellido:" );
        pm.setApellido(reg.next());
        System.out.println( "Sexo:" );
        pm.setSexo(reg.next());
    }
}

ListPersona.

package ni.jug.view;
import java.io.IOException;
import java.util.List;
import ni.jug.model.Persona;
/**
 * Lista los registro de la tabla
 * @author gacs
 *
 */
public class ListPersona {
    public void sayRegisters(List a) 
    {
        System.out.println(" Id "+"  -- Nombre --   "+" -- Apellido -- "+" - Sexo - ");
	a.forEach(p ->{
            System.out.println(p.toString());	
	});
        try
        {
            System.out.println("\n\n  Presione ENTER..");
            System.in.read();
	}
	catch(IOException exe)
	{
            System.out.printf("Error?");
	}
    }
	public void sayRegister(Persona a) 
	{
		System.out.println(" Id "+"  -- Nombre --   "+" -- Apellido -- "+" - Sexo - ");
		System.out.println(a.toString());	
	}
}

EditPersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
public class EditPersona {
	
    public int getRegister() {
        int register;
        Scanner r= Input.getInstance();
        System.out.print("Digite el Id del registro a modificar :");
        register=r.nextInt();
        return register;
    }	
    public void postRegister(Persona a) {
        Scanner r= Input.getInstance();
        System.out.println("Registro a modificar");
        System.out.println("Id      :"+a.getId());
        System.out.println("Apellido  :"+a.getApellido());
        a.setApellido(r.next());
        System.out.println("Nombre  :"+a.getNombre());
            a.setNombre(r.next());
        System.out.println("Sexo    :"+a.getSexo());
           a.setSexo(r.next());
    }
}

DeletePersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
public class DeletePersona {
	
	public int getRegister() {
		int register;
		Scanner r= Input.getInstance();
		System.out.println("Digite el registro a eliminar");
		register=r.nextInt();
		return register;
	}
	public boolean postRegister(Persona a) {
		Scanner r= Input.getInstance();
		System.out.println("Se borra el registro");
		System.out.println("Id      :"+a.getId());
		System.out.println("Nombre  :"+a.getNombreCompleto());
		System.out.println("Sexo    :"+a.getSexo());
		System.out.print("Desear eliminar este registro ?[S/N]");
		String bln=r.next();
		return bln.equalsIgnoreCase("S");
	}
}

Clase util Input.

Vamos a crear una clase llama Input, la cual estará en la carpeta main/java/ni/jug/util . Esta clase devuelve la clase Scanner, la cual es utilizada por las vistas para interactuar con el usuario. El contenido de la clase es el siguiente:

package ni.jug.util;
import java.util.Scanner;
/**
 * Clase de utilidad para las vistas
 * aplicado bajo el modelo singleton
 * @author gacs
 */
public class Input {
	private static Scanner input=null;
	private Input() {
		input=new Scanner(System.in);
	}
	
	public static Scanner getInstance() {
        if(input==null)
     	   		new Input(); 
        return input;
	}
}

Controlador.

Ya tenemos el modelo y las vistas de de nuestro proyecto. Ahora vamos a crear nuestra clase controladora, la que nos hará la persistencia de los datos que suministró el usuario en las vista a través del modelo.  Esta clase tendrá el nombre de PersonaController y estará ubicado en la carpeta main/java/ni/jug/controller, y tendrá el siguiente código:

package ni.jug.controller;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import ni.jug.ConnectionBD;
import ni.jug.model.Persona;
import ni.jug.view.DeletePersona;
import ni.jug.view.EditPersona;
import ni.jug.view.ListPersona;
import ni.jug.view.NewPersona;
/**
 * Clase de control para la tabla persona
 * @author gacs
 */
 public class PersonaController {
	private final String SQLINSERT="INSERT INTO PERSONA VALUES(NULL,?,?,?)";
	private final String SQLQUERY="SELECT Id,Nombre,Apellido,Sexo FROM PERSONA";
	private final String SQLQUERYONE=SQLQUERY+" WHERE Id=?";
	private final String SQLDELETE="DELETE FROM PERSONA WHERE Id=?";
	private final String SQLUPDATE="UPDATE PERSONA SET Nombre=?, Apellido=?,Sexo=? WHERE Id=?";
	private Connection conn;
	public PersonaController() {
		initController();
	}
	/**
	 * Instancia la conexion, crea la tabla si esta no existe
	 */
	
	private void initController() {
		conn=ConnectionBD.getConnection();
		PreparedStatement pst=null;
		String sqlCreate="CREATE TABLE IF NOT EXISTS PERSONA (" + 
				"	Id	integer PRIMARY KEY autoincrement," + 
				"	Nombre	 varchar ( 25 ) NOT NULL," + 
				"	Apellido varchar ( 25 ) NOT NULL," + 
				"	Sexo char  NOT NULL" + 
				"	)";
		try {
			pst=conn.prepareStatement(sqlCreate);
			pst.execute();
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
	}
	
	/**
	 * llama a la vista NewPersona para que el usuario agregue los valores a los campos
	 * luego estos valores se agregan a la tabla
	 */
	public void addRegister() {
		Persona persona=new Persona();
		NewPersona view =new NewPersona(persona);
		view.setRegister();
		PreparedStatement pst=null;
		try {
			pst=conn.prepareStatement(SQLINSERT);
			pst.setString(1, persona.getNombre());
			pst.setString(2, persona.getApellido());
			pst.setString(3, persona.getSexo());
			pst.executeUpdate();
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
	}
/**
 * Extrae los registro de la tabla y los visualiza en pantalla por medio de la vista
 * ListPersona
 */
	public void getRegisters() {
		List personas=new ArrayList<>();
		PreparedStatement pst=null;
		ResultSet rs=null;
		try {
			pst=conn.prepareStatement(SQLQUERY);
			rs=pst.executeQuery();
			while(rs.next()) {
				personas.add(new Persona( rs.getInt("Id"),
						rs.getString("Nombre"),
						rs.getString("Apellido"),
						rs.getString("Sexo")
						));
			}
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
	    		if(rs!=null)
	    			rs.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
		ListPersona view =new ListPersona();
		view.sayRegisters(personas);
	}	
	
	private Persona getRegister(int a) {
		PreparedStatement pst=null;
		ResultSet rs=null;
		Persona persona=null;
		try {
			pst=conn.prepareStatement(SQLQUERYONE);
			pst.setInt(1, a);
			rs=pst.executeQuery();
			if(rs.next()) {
				persona=new Persona( rs.getInt("Id"),
					rs.getString("Nombre"),
					rs.getString("Apellido"),
					rs.getString("Sexo")
				);
			}
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {		//Nos aseguramos que se cierre el PreparedStatement y ResultSet al finalizar el metodo
	    	try {
	    		if(pst!=null)
	    			pst.close();
	    		if(rs!=null)
	    			rs.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
		
		return persona;
	}
	
	/**
	 * Borra un registro
	 * llama a la vista DeletePersona 
	 * con el metodo get determina el registro que quiere eliminar de la tabla
	 * con el metodo post visualiza el registro a eliminar y pide confirmacion para borrar
	 * 
	 */
    public void deleteRegister() {
	DeletePersona dp=new DeletePersona();
	Persona persona=null;
        int reg;
	PreparedStatement pst=null;
        do {
                reg=dp.getRegister();
                persona=getRegister(reg);
                if(persona.getId()!=reg) {
                        System.out.println("Registro no encontrado!!!");
                }
        }while(persona.getId()!=reg);
	if(dp.postRegister(persona)) {
            try {
		pst=conn.prepareStatement(SQLDELETE);
		pst.setInt(1, persona.getId());
		pst.execute();
            }catch( SQLException e){
	        System.err.println(e);
	    }
            finally {		//Nos aseguramos que se cierre el PreparedStatement al finalizar el metodo
           	try {
                    if(pst!=null)
		    	pst.close();
		}catch( SQLException e){
		        System.err.println(e);
                }
            }		   
	}
    }
	
    public void editRegister() {
        EditPersona edit =new EditPersona();
        Persona persona=null;
        int reg;
        PreparedStatement pst=null;
        do {
                reg=edit.getRegister();
                persona=getRegister(reg);
                if(persona.getId()!=reg) {
                        System.out.println("Registro no encontrado!!!");
                }
        }while(persona.getId()!=reg);
        //Envia el registro para ser modificado 
        edit.postRegister(persona);
        try {
                pst=conn.prepareStatement(SQLUPDATE);
                pst.setString(1, persona.getNombre());
                pst.setString(2, persona.getApellido());
                pst.setString(3, persona.getSexo());
                pst.setInt(4, persona.getId());
                pst.executeUpdate();
        }catch( SQLException e){
            System.err.println(e);
        }
            //Nos aseguramos que se cierre el PreparedStatement al finalizar el metodo
        finally {
            try {
                   if(pst!=null)
                            pst.close();
                }catch( SQLException e){
                    System.err.println(e);
            }
        }		   
    }
}

Clase principal (main class)

Para terminar el desarrollo de nuestro proyecto vamos a modificar la clase App.java, la cual se encuentra en la carpeta main/java/ni/jug. Esta es la primera clase que se iniciará en nuestra aplicación, por lo que en su contenido tendrá el método main. El código de esta clase sea el siguiente:

package ni.jug;
import java.sql.SQLException;
import java.util.Scanner;
import ni.jug.controller.PersonaController;
import ni.jug.util.Input;
/**
 * class main
  * @author gacs
 */
public class App 
{
    public App() {
    	menus();
    }
    public static void main( String[] args )
    {
            App app = new App();
    	System.out.println("Termino");	
    }
	/**
	 * Metodo que proporciona el menu de la aplicacion
	 */
    private void menus() {
    	Scanner menu;
    	menu=Input.getInstance();
    	PersonaController pc = new PersonaController();
        byte op=0;
        do {
            System.out.println( "-- Persistencia con SQLite --" );
            System.out.println( "1. Ingresar" );
            System.out.println( "2. Listar" );
            System.out.println( "3. Borrar" );
            System.out.println( "4. Editar" );
            System.out.println( "0. Salir de la aplicacion" );
            op=menu.nextByte();
            switch (op) {
                case 0:
                    System.out.println( "Saliendo del sistema" );
                    break;
                case 1:
                    pc.addRegister();
                    break;
                case 2:
                    pc.getRegisters();	
                    break;
                case 3:
                    pc.deleteRegister();	
                    break;
                case 4:
                    pc.editRegister();	
                    break;
                default:
                   System.out.println( "Solo puede eligir una de las opciones del menu" );
                    break;
            }
        }while(op!=0);
        menu.close();
   	try {
            ConnectionBD.getConnection().close(); 
	 }catch( SQLException e){
	    System.err.println(e);
	 }		   
    }
}

Hasta aquí ya tenemos finalizado nuestro proyecto, cumpliendo lo básico para persistir nuestros datos en base de datos SQLite por medio de JDBC.

Compilación y ejecución.

Compilando el proyecto.

Para terminar con el desarrollo de nuestro pequeño proyecto de persistencia de datos en  SQLite, procederemos con la compilación. Para ello, nos trasladamos a la carpeta que contiene nuestro proyecto y ejecutamos la siguiente sentencia.

 mvn clean install

Una vez que finalice de compilar, Maven nos habrá creado una carpeta target y en ella unos archivos jar.

target/jsqlite-1.0-SNAPSHOT.jar
target/jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar

Ejecución de jsqlite.

Para ejecutar nuestro aplicación, desde una terminal correremos el archivo jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar, con el siguiente sentencia

java -jar target/jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar

Nos presentará las siguientes pantallas:

 

Ingresamos datos con la opción 1.

Listamos los datos que ingresamos con la opción 2.

Salimos de la aplicación y volvemos a ingresar y listamos los registro que contiene la base de datos para comprobar si está persistiendo nuestra jsqlite.

Buenos, finalizamos y hemos alcanzado nuestro objetivo.

¿Qué les parece? ¿Dejé por fuera algo? ¿No entendiste algo? Déjamelo en los comentarios..

Reportes en Java con Jaspersoft Studio. Post 1 de 5

Por qué Jaspersoft Studio? En mi experiencia, los reportes son fundamentales en toda aplicación o software empresarial que creamos. Ellos nos muestran la información ya procesada que es el objetivo de nuestros desarrollo, presentar informes precisos, entendibles que permitan tomar decisiones asertivamente. Es por eso que en esta ocasión les queremos compartir acerca de jasperReports con Jaspersoft Studio, la herramienta de referencia en el mundo java para los reportes.

JasperReports es una biblioteca de creación de informes que tiene la habilidad de entregar contenido enriquecido al monitor, a la impresora o a ficheros PDFHTMLXLSCSV y XML.

Escrito completamente en java, por y para java,  puede ser usado en gran variedad de aplicaciones tanto EE (Enterprise), como SE(Standart), o de escritorio, puede servir reportes fijos y dinámicos.

Requerimientos previos a la instalación en ambiente Windows

Funciona en Windows XP / 7/8 con 32 o 64 bits, Linux (32 o 64 bits), MacOS X en 64 bits.

Procesador de 3GHZ o superior  (32/64). La cantidad de la memoria RAM es completamente  dependiente de la complejidad de los informes, se recomienda un valor de 1 GB dedicado a Jaspersoft de estudio, ya dado que en el sistema hay muchas otras cosas que requieren RAM se sugiere que el equipo tenga un mínimo de 4GB de RAM.


Se requiere una distribución completa de Java. Necesitas descargar el Java Development Kit (JDK) 1.6 o la ultima versión jdk-12.0.1_windows-x64_bin.exe

 

** No olvides el JDBC con el que te conectaras al motor de Base de datos, de otro modo no podrás acceder a los datos. 

 

Instalando Jaspersoft Studio 6.9.0 (windows)

1. Visitar la página https://community.jaspersoft.com/project/jaspersoft-studio/releases para bajarnos la última versión de JasperSoft Studio, en este caso la 6.9.0.

Pagina de descarga de Jaspersoft Studio

la primera vez nos mostrará una página para iniciar sesión o inscribirnos al sitio de la comunidad de Jaspersoft.

 

2. Después de finalizar la descarga del instalador, dar doble clic en el archivo TIB_js-studiocomm_6.9.0_windows_x86_64.exe

3. Clic en “I Agree” (estoy de acuerdo)

4. Seleccionar la carpeta donde deseamos instalar el programa, podemos dejar la ubicación por defecto y hacer clic en instalar.

5. Esperar que se instale el programa

6. Clic en Finish (Finalizar)

 

Descripción de la pantalla de JasperStudio

En este Lote de artículos vamos a utilizar MS SQL Server para nuestro ejercicios, por tanto, el paso posterior a la instalación de JasperSoft Studio 6.9.0  seria  Instalar el driver para Microsoft SQL Server: 

 

El driver que bajemos dependerá de la versión de SQL Server que tengamos instalada en nuestra computadora. Para eso visitaremos la página https://docs.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-2017  

Copiar el archivo sqljdbc4.jar a la carpeta: C:Program FilesTIBCOJaspersoft Studio-6.9.0featuresjre.win32.win32.x86_64.feature_1.8.0.u151jrelibext

 

NOTA

Opcionalmente, si queremos usar la autenticación con el usuario de Windows o seguridad integrada con SQL Server: 

Copiar el archivo sqljdbc_auth.dll a la carpeta: C:Program FilesTIBCOJaspersoft Studio-6.9.0featuresjre.win32.win32.x86_64.feature_1.8.0.u151jrebin

Para que surtan efectos los cambios debemos cerrar y volver a abrir la aplicación de JasperSoft Studio.

Format Style

Las normas de estilo son fundamentales tanto para crear una identidad como para transmitir correctamente. Desde el momento en que decidimos crear un proyecto, la coherencia visual es importante, UX y UI están de la mano, y en los reportes no puede ser la exception.

Por esta razón aconsejamos crear un estilo propio que se aplique a todos los estilos de los reportes que vamos a crear.

 

Propuesta de normativa para formateo de reportes

 

Elemento de sección

Norma a aplicar

No.

Descripción

Fuente/estilo/bordes

Tamaño

Alineación

Encabezado del reporte

 

1

Nombre de Entidad

Arial + Mayúscula + Negrita

12

Centrado

2

Unidad administrativa de la entidad

Arial + Mayúscula + Negrita

10

Centrado

3

Código del reporte

Arial + Negrita

9

Derecha

4

Título

Arial + Mayúscula + Negrita

11

Centrado

5

Subtítulo

Arial + Mayúscula + Negrita

10

Centrado

6

Periodo de Fecha

Arial + Mayúscula + Negrita

9

Centrado

Agrupaciones

 

7

Encabezado y subtitulo de la agrupación

Arial Narrow + Negrita con borde superior e inferior

8

Izquierda

8

Detalle de la agrupación

Arial Narrow

8

 

9

Sub-total por agrupación

Arial Narrow + Negrita con borde superior

9

 

Detalle

 

10

Etiquetas o nombres de columna

Arial Narrow + Negrita con borde superior e inferior

8

Izquierda

11

Detalle

Arial Narrow

8

 

Pie del reporte

 

12

Versión, módulo, página # de ## páginas, usuario, fecha

Arial Narrow con línea fina (0.6 px) parte superior de las etiquetas

8

 

Sumatoria o suma a nivel de reporte

 

13

Suma

Arial Narrow con borde superior

9

 

Ejemplo de estilos.

 

Elemento

Ejemplo

Ejemplo de Negrita, Cursiva y Subrayado en texto estático

 

Hola Mundo

 

 

Ejemplo de Negrita  y Cursiva y fondo amarillo en texto estático

 

Hola Mundo

 

 

Ejemplo de Negrita y Cursiva y color azul en texto estático

 

Hola Mundo

 
 

textElement

 

 

Ejemplo de Negrita, fondo verde, color de la fuente rojo en campo de texto

+ $F{Campo} +

 

Aplicando formato a números y fechas

Elemento

Ejemplo

Formato para campos numéricos

” (C$ ” + new java.text.DecimalFormat(“#,##0.00”).format($F{Total}) + “)”

Formato para campos de fechas

(new SimpleDateFormat(“dd/MM/yyyy”).format($F{Fecha}))

Mostrar un elemento si el número de página es par

new Boolean($V{PAGE_NUMBER}%2==0 )

Mostrar un elemento si el número de página es 1

new Boolean($V{PAGE_NUMBER}.intValue()==1)

Mostrar un elemento si el valor de un campo de la consulta es 1

new Boolean($F{Campo}.equals(new Integer(1)) ? true  : false)

Condicionales en campos de texto

( $F{Referencia}.intValue() == 1

? “Referencia tiene valor 1”

:    (  $F{Referencia}.intValue() == 3

      ? “Referencia tiene valor 3”

    :”Referencia tiene un valor distinto a 1 ó 3“    )

)

 

 

Nosotros y terceros seleccionados utilizamos cookies o tecnologías similares con fines técnicos y, con su consentimiento, para otras finalidades (“interacciones y funcionalidades básicas”, “mejora de la experiencia”, “medición” y “segmentación y publicidad”) según se especifica en la política de cookies. Usted es libre de otorgar, denegar o revocar su consentimiento en cualquier momento.    Configurar y más información
Privacidad