Translate

Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas

miércoles, 3 de junio de 2026

Jakarta Persistence 4.0: El mayor salto de JPA en años



Durante mucho tiempo, cuando hablábamos de persistencia en Java, hablábamos de JPA. Con la migración de Java EE a Jakarta EE, JPA pasó a llamarse Jakarta Persistence, pero durante varias versiones los cambios fueron relativamente modestos.

Con Jakarta Persistence 4.0 eso cambia.

Esta nueva versión incorpora características largamente esperadas, mejora la seguridad de tipos y prepara el terreno para trabajar junto a Jakarta Data. Veamos qué trae de nuevo.


¿Qué es Jakarta Persistence?

Jakarta Persistence es la especificación estándar para el mapeo objeto-relacional (ORM) en Java.

Permite representar tablas como objetos Java y realizar operaciones CRUD sin escribir SQL para cada interacción.


@Entity

public class Cliente {


    @Id

    private Long id;

    private String nombre;

}


Implementaciones populares incluyen:

  • Hibernate ORM
  • EclipseLink


Novedades más importantes de Jakarta Persistence 4.0:

1. EntityAgent: trabajar sin Persistence Context

Una de las novedades más llamativas es la incorporación de EntityAgent.

Hasta ahora, casi todas las operaciones pasaban por un EntityManager y un Persistence Context.


entityManager.persist(cliente);


Con EntityAgent se pueden realizar operaciones sobre entidades desacopladas del contexto de persistencia, simplificando ciertos escenarios y reduciendo overhead. 


Conceptualmente:

entityAgent.insert(cliente);


Esto acerca la API a modelos más ligeros y modernos.


2. Carga masiva por ID

Un problema clásico:


for(Long id : ids) {

    entityManager.find(Cliente.class, id);

}


Esto puede generar el famoso problema N+1.

Jakarta Persistence 4.0 incorpora soporte para obtener múltiples entidades por identificador en una sola operación. 


La ventaja:

  • Menos viajes a la base de datos.
  • Mejor rendimiento.
  • Código más simple.


3. Consultas más seguras con Static Query

Una crítica frecuente a JPA era que las consultas JPQL eran simples Strings.


@Query("select c from Cliente c")


Si el nombre de una propiedad cambiaba, el error aparecía recién en tiempo de ejecución.

Persistence 4.0 incorpora una API de Static Queries que permite mayor verificación en compilación. 


Beneficios:

  • Más seguridad de tipos.
  • Menos errores en producción.
  • Mejor soporte para herramientas y refactorización.


4. Result Set Mapping programático

Hasta ahora era habitual definir mapeos complejos mediante anotaciones.


@SqlResultSetMapping(...)


La nueva versión agrega una API programática para definir estos mappings. 


Ventajas:

  • Más flexible.
  • Más reutilizable.
  • Menos anotaciones gigantes.


5. Nuevas capacidades para Entity Graphs

Los Entity Graphs fueron introducidos para controlar qué relaciones se cargan.


@EntityGraph(attributePaths = {

    "pedidos",

    "direccion"

})


Persistence 4.0 mejora significativamente esta funcionalidad:

Nuevas anotaciones.

Mejor integración con operaciones existentes.

Uso de Entity Graphs en refresh(). 


6. Soporte para entidades Read-Only

En muchos escenarios solo queremos leer datos.

Antes:


Cliente cliente =

    entityManager.find(Cliente.class, id);


La entidad quedaba administrada por el contexto.

Persistence 4.0 agrega soporte explícito para carga en modo solo lectura. 


Beneficios:

  • Menor consumo de memoria.
  • Menor costo de dirty checking.
  • Mejor rendimiento.


7. @PreMerge

Hasta ahora existían callbacks como:


@PrePersist

@PostPersist

@PreUpdate

@PostUpdate



Ahora aparece:

@PreMerge

public void beforeMerge() {

    System.out.println("Fusionando entidad");

}


Esto permite interceptar el proceso de merge antes de que ocurra. 


8. Excluir campos del Optimistic Locking

Hasta ahora cualquier modificación podía afectar el control de versiones.

Persistence 4.0 incorpora una anotación para excluir ciertos atributos del mecanismo de optimistic locking. 


Ideal para:

  • Campos calculados.
  • Contadores.
  • Metadatos auxiliares.


9. select new implícito

Actualmente:


select new ClienteDTO(

    c.id,

    c.nombre

)

from Cliente c


Persistence 4.0 simplifica este escenario permitiendo inferir automáticamente el DTO cuando se especifica el tipo de resultado. 


10. Preparándose para Jakarta Data

Quizás el cambio más importante no sea una característica puntual.

Muchas de las nuevas APIs fueron diseñadas para trabajar mejor con Jakarta Data

La idea es ofrecer una experiencia similar a:

  • Spring Data
  • Micronaut Data
  • Quarkus Panache


pero de forma estandarizada dentro del ecosistema Jakarta EE. 


¿Vale la pena actualizar?

  • Si estás usando:
  • Hibernate moderno
  • Jakarta EE 11 o futuro Jakarta EE 12
  • Nuevos proyectos


la respuesta es sí.


Las mejoras apuntan a problemas reales que los desarrolladores vienen sufriendo desde hace años:

  • Más type safety
  • Menos Strings mágicos
  • Mejor rendimiento
  • Mejor integración con Jakarta Data
  • APIs más modernas


Jakarta Persistence 4.0 representa probablemente la evolución más importante de JPA desde la llegada de los Entity Graphs y los Stored Procedures.

La incorporación de EntityAgent, Static Queries, Result Set Mapping programático, mejoras en Entity Graphs y nuevas capacidades de carga hacen que la especificación se vea mucho más moderna y preparada para competir con las soluciones de persistencia actuales. 

Para quienes pensaban que JPA había quedado estancado, Jakarta Persistence 4.0 demuestra exactamente lo contrario.


Dejo link: 

https://jakarta.ee/specifications/persistence/4.0/



lunes, 1 de junio de 2026

Problema: Java consumiendo mucho CPU (sin GUI ni puertos)


Si no podés usar VisualVM ni JMX remoto, la estrategia correcta es:

CLI + correlación de datos


1. Encontrar el proceso

jps -l

o directamente:

ps aux | grep java

guardate el PID


2. Confirmar que el problema es CPU

top -p <pid>

o mejor:

top -H -p <pid>

Esto muestra threads individuales


Acá está la clave:

  • Vas a ver un thread con CPU alto (ej: 200%)
  • Anotá el TID (thread id)


3. Convertir TID a HEX (clave para Java)


Java muestra threads en hexadecimal, Linux en decimal.

printf "%x\n" <tid>


Ejemplo:

12345 → 3039


4.Sacar thread dump

jstack <pid> > dump.txt


5. Buscar el thread problemático


Dentro del dump:

grep -i 3039 dump.txt


Ese es el thread que está consumiendo CPU

¿Qué vas a encontrar? Generalmente algo así:

"id=123 nid=0x3039 runnable"


Y abajo el stack:

at com.miapp.CalculoPesado.procesar(CalculoPesado.java:42)

at com.miapp.Service.loop(Service.java:88)


¡encontraste el problema!


¿Dónde entra jstat en todo esto?

Acá es donde sumás contexto.


Ejecutá:

jstat -gcutil <pid> 1000


 Qué mirar para CPU alto


Caso 1: GC excesivo

  • YGC sube MUY rápido
  • GCT crece constantemente


CPU alto por Garbage Collector


Caso 2: Full GC frecuentes

  • FGC aumenta
  • FGCT alto


pausas pesadas → CPU + latencia


Caso 3: GC normal

  • GC estable
  • memoria OK


Entonces el problema es código, no GC

domingo, 31 de mayo de 2026

jstat en Java: cómo entender el Garbage Collector desde la consola


Cuando hablamos de monitoreo en Java, muchos van directo a VisualVM.

Pero en entornos reales (servidores, contenedores, CI), necesitás herramientas sin GUI. Ahí aparece jstat.

Una herramienta liviana, incluida en la JVM, que te permite ver qué está pasando con la memoria y el Garbage Collector en tiempo real.

jstat (Java Virtual Machine Statistics Monitoring Tool) es una utilidad de consola que permite consultar métricas internas de la JVM.

  • Uso de memoria
  • Actividad del GC
  • Cantidad de recolecciones
  • Tiempos acumulados


Primero necesitás el PID del proceso:


jps -l


Después ejecutás:


jstat -gc <pid> 1000


Esto imprime estadísticas cada 1 segundo.


 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU     YGC   YGCT   FGC    FGCT     GCT

1024.0 1024.0  0.0   512.0   8192.0   4096.0   16384.0   8000.0   5120.0 4800.0 1024.0  900.0     10    0.25    2     0.50     0.75


Ahora lo importante: entender esto 👇

Generación joven (Young Gen)


S0C / S1C → tamaño de Survivor 0 y 1

S0U / S1U → uso actual

EC → tamaño de Eden

EU → uso de Eden

Acá viven los objetos nuevos.


Generación vieja (Old Gen)

OC → capacidad

OU → uso


👉 Si esto crece mucho y no baja → posible problema de memoria.


Metaspace

MC / MU → capacidad y uso

CCSC / CCSU → Compressed Class Space


👉 Relacionado con clases cargadas.


YGC → cantidad de GC jóvenes

YGCT → tiempo total en GC joven

FGC → cantidad de Full GC

FGCT → tiempo total en Full GC

GCT → tiempo total de GC


Veamos un caso saludable

  • Eden sube y baja constantemente
  • YGC aumenta progresivamente
  • Old Gen estable


👉 Todo OK.


Posible memory leak

  • OU (Old Gen) sube y nunca baja
  • Full GC frecuentes


👉 Algo está reteniendo objetos.


🟠 Problema de GC

  • YGC muy alto en poco tiempo
  • GCT creciendo rápido


👉 La app está gastando demasiado tiempo en GC.


⚙️ Otros modos útiles

-gcutil (más simple y legible)


jstat -gcutil <pid> 1000


Salida en porcentajes:

 S0     S1     E      O      M     CCS    YGC   YGCT   FGC   FGCT     GCT

 0.00  50.00  60.00  48.00  93.75 87.89   10    0.25    2     0.50     0.75


👉 Ideal para monitoreo rápido.


Ejecutar N veces

jstat -gc <pid> 1000 10


👉 Ejecuta 10 veces y termina.


jstat es una herramienta simple pero extremadamente poderosa.


✔ Ideal para servidores sin GUI

✔ Perfecta para debugging en vivo

✔ Fundamental para entender el GC

lunes, 25 de mayo de 2026

¿Cómo monitorear la JVM desde la consola?


Cuando trabajamos con Java, una de las herramientas más conocidas para monitoreo es VisualVM.

Nos permite analizar memoria, CPU, hilos, GC y mucho más… todo desde una interfaz gráfica muy cómoda.

Pero hay un problema: ¿Qué pasa cuando estás en un servidor sin entorno gráfico?

Ahí es donde VisualVM ya no alcanza.


Java ya trae todo lo necesario… solo que más low level.


1. jps – listar procesos Java


jps -l


Salida típica:

1234 com.miapp.Main

5678 org.apache.catalina.startup.Bootstrap


Es el primer paso: obtener el PID.


2. jstat – métricas del GC


jstat -gc 1234 1000


Muestra:

  • uso de memoria
  • comportamiento del Garbage Collector
  • generaciones (Eden, Survivor, Old)


Ideal para detectar problemas de memoria en vivo.


3. jstack – dump de hilos


jstack 1234


Te da:

  • todos los threads
  • estados (RUNNABLE, BLOCKED, etc.)
  • posibles deadlocks


Clave para debugging de concurrencia.


4. jmap – análisis de memoria


jmap -heap 1234


O histogramas:


jmap -histo 1234


Podés ver qué objetos están ocupando memoria.


5. jcmd – el reemplazo moderno

Esta es la más importante.


jcmd 1234 help


Ejemplos:

jcmd 1234 GC.heap_info

jcmd 1234 Thread.print

jcmd 1234 GC.run


Es como un “super comando” que reemplaza a varios anteriores.


Veamos un ejemplo: 

En entornos reales se suele hacer esto:


1. Servidor (Linux sin GUI)

  • Aplicación Java corriendo
  • JMX habilitado


2. Diagnóstico:

  • CLI (jcmd, jstat, etc.)
  • o conexión remota desde VisualVM


Este enfoque es estándar en sistemas productivos.


VisualVM no es “la herramienta”, es solo un cliente visual.

Las verdaderas herramientas son:

  • APIs internas de la JVM
  • JMX
  • comandos como jcmd


VisualVM simplemente las consume y las muestra bonito.


martes, 5 de mayo de 2026

VisualVM vs .NET Tooling


¿Por qué en Java hay “una herramienta” y en .NET hay muchas?

Cuando venís del mundo Java, es muy común preguntarte: ¿Dónde está el VisualVM de .NET?


La respuesta corta es: no existe uno solo.

La respuesta interesante es: no existe por diseño.


En el ecosistema Java, herramientas como VisualVM se volvieron estándar porque la JVM expone un modelo bastante unificado:

  • JMX (Java Management Extensions)
  • Heap dumps
  • Thread dumps
  • GC metrics


Todo eso se puede consumir desde una sola herramienta.

¿Qué ofrece VisualVM?

  • Profiling de CPU
  • Análisis de memoria (heap)
  • Monitoreo de threads
  • Plugins

Resultado: una experiencia “tipo panel de control” donde ves todo.


En .NET moderno, el enfoque es distinto. En lugar de una herramienta central, tenés un ecosistema de herramientas especializadas.


Visual Studio (Profiler integrado)


Es lo más parecido a VisualVM:

  • CPU profiling
  • Memory profiling
  • Timeline
  • UI completa


Es la opción más “todo en uno”, pero:

❌ Es pesada

❌ No siempre usable en producción


CLI tools (el corazón real de .NET)


.NET adopta un enfoque más tipo Unix:

  • dotnet-counters → métricas en vivo
  • dotnet-trace → trazas de ejecución
  • dotnet-dump → dumps
  • dotnet-gcdump → GC
  • dotnet-stack → threads


Cada herramienta hace una cosa… pero la hace muy bien.

Esto permite:

  • Usarlas en producción
  • Automatizarlas
  • Integrarlas con pipelines


PerfView

  • Muy potente
  • Usado internamente por Microsoft
  • Basado en ETW (Event Tracing for Windows)


❌ Curva de aprendizaje alta

❌ UX poco amigable


Herramientas comerciales

  • dotTrace
  • ANTS Profiler
  • .NET Memory Profiler


Estas sí se sienten más como VisualVM:

✔️ UI amigable

✔️ Experiencia integrada

❌ Son pagas



sábado, 2 de mayo de 2026

Records vs Project Valhalla en Java


Java viene evolucionando fuerte en dos direcciones:

  • Java Records → escribir menos código
  • Project Valhalla → ejecutar más rápido


A primera vista parecen competir… pero en realidad se complementan.

  • Usá Records → claridad, simplicidad, APIs
  • Usá Valhalla (Value Types) → rendimiento, memoria
  • Usá ambos → lo ideal en sistemas complejos


Records: el rey de la simplicidad

record User(String name, int age) {}


✔ Inmutables

✔ Menos boilerplate

✔ equals, hashCode, toString automáticos


¿Cuándo usar Records?

DTOs / APIs


record ProductDTO(String name, double price) {}


Perfecto para:

  • REST APIs
  • serialización JSON
  • comunicación entre capas


Modelos simples

record Money(double amount, String currency) {}


Cuando te importa más la legibilidad que el rendimiento


Lógica de negocio

  • servicios
  • respuestas intermedias


El overhead de objetos no suele ser un problema


Limitación clave, un record:

  • vive en el heap
  • tiene identidad
  • usa referencias


Valhalla: el rey del rendimiento

Ejemplo conceptual:


value class Point {

    int x, y;

}



✔ Sin identidad

✔ Sin overhead de objeto

✔ Datos contiguos en memoria


¿Cuándo usar Value Types?

Grandes volúmenes de datos


value class Point {

    int x, y;

}


List<Point>


Mucho más eficiente que objetos tradicionales


Simulaciones

  • físicas
  • partículas
  • coordenadas


Miles o millones de instancias


Alto rendimiento

  • trading
  • analytics
  • cálculos intensivos


Menos GC = mejor performance


¿Qué cambia realmente?

Sin Valhalla:

List<Point> // lista de referencias


Con Valhalla:

[x1, y1, x2, y2, x3, y3]


Datos planos → mejor cache → más velocidad


Cuándo NO usar Valhalla

  • Entidades (`User`, `Order`)
  • Objetos con identidad
  • Integración con frameworks clásicos


Si necesitás identidad → no es value type

Lo mejor: combinarlos

Acá está la clave real 👇


Caso 1: Record + Value Type


value class Point {

    int x, y;

}


record Circle(Point center, double radius) {}


Tenés:

API clara (record)

datos eficientes (value)


Caso 2: separación por capas


// API

record PointDTO(int x, int y) {}


// Core

value class Point {

    int x, y;

}


Separás:

  • interfaz → legible
  • core → performante


Caso 3: colecciones grandes


value class Price {

    double amount;

}


List<Price>


Sin boxing → sin referencias → 🔥


Futuro: value records

Se viene algo así:

value record Point(int x, int y) {}


Combina:

  • simplicidad (record)
  • eficiencia (value type)


Conclusión, no es una pelea, es una estrategia:

  • Records → hacen feliz al desarrollador
  • Valhalla → hace feliz a la JVM



Java vs C#: Records y Value Types vs Records y Structs


Durante años, C# llevó ventaja en expresividad y control de memoria.

Pero con Java Records y Project Valhalla, Java está alcanzando… y en algunos aspectos, intentando superarlo.


👉 La pregunta es:

 ¿Java está copiando a C#… o lo está mejorando?


Empecemos por records: empate técnico

En C#

record Person(string Name);


En Java

record Person(String name) {}


✔ Inmutables

✔ Comparación por valor

✔ Menos boilerplate


Conclusión: Empate total — ambos lenguajes convergieron al mismo concepto


Value Types vs Structs

En C#: struct


struct Point {

    public int X;

    public int Y;

}


✔ Tipo por valor

✔ Generalmente en stack

✔ Muy eficiente


Problemas:

Boxing (se convierte en objeto)

Copias implícitas

Comportamiento a veces confuso


 En Java: Value Types (Valhalla)


value class Point {

    int x, y;

}


✔ Sin identidad

✔ Sin overhead de objeto

✔ Puede ser “flattened”


Y lo más importante: No cambia su naturaleza según el contexto


Diferencia clave

C# → tipo híbrido (a veces valor, a veces objeto)

Java → tipo consistente (siempre value type)


Punto para Java (a nivel diseño)


Memoria y layout

  • Stack vs Heap
  • Depende del contexto
  • Puede generar copias innecesarias


 Java (Valhalla)

  • Flattening automático
  • Mejor locality
  • Optimización por la JVM


Ejemplo mental:

En java esto: 

List<Point>


Podría ser:

[x1, y1, x2, y2, x3, y3]


Mucho más eficiente que referencias



Generics (el golpe fuerte de Java)


C#

List<int> 


Soportado desde hace años


Java (hoy)

List<Integer>  (boxing)


Java (con Valhalla)

List<int> ✔

List<Point> ✔


Sin boxing y sin referencias


Este es uno de los mayores avances de Java


Complejidad del modelo mental


En C#

  • class vs struct
  • boxing/unboxing
  • copias implícitas
  • Puede ser confuso


En Java

  • class vs value class
  • sin identidad vs con identidad
  • Más simple conceptualmente


Ecosistema y madurez

C#

✔ Ya está en producción

✔ Frameworks adaptados

✔ Casos reales


 Java (Valhalla)

⚠️ En desarrollo

⚠️ APIs en evolución

⚠️ Falta adopción


Hoy no podés usarlo en producción estándar



¿Quién lo hizo mejor?


Hoy: C#

Porque:

  • ya funciona
  • es estable
  • está probado


 A futuro: Java (posiblemente)

Porque:

  • evita errores históricos (boxing, copias)
  • diseño más consistente
  • mejor integración con la JVM



Proyecto Valhalla en Java: Tipos de Valor y el Futuro del Rendimiento


Cuando hablamos de la evolución de Java, uno de los proyectos más interesantes (y menos comprendidos) es Project Valhalla.


Si alguna vez sentiste que Java es “pesado” por culpa de los objetos… este proyecto apunta directamente a eso.

Project Valhalla es una iniciativa dentro de OpenJDK cuyo objetivo principal es introducir:

Tipos de valor (Value Types / Inline Classes)

Esto permite representar datos sin el costo de los objetos tradicionales.

En Java, casi todo es un objeto… y eso tiene consecuencias:


Integer x = 10;


Esto implica:

  • Heap allocation
  • Indirección (punteros)
  • Presión sobre el GC
  • Mala localización en memoria (cache-unfriendly)


Incluso algo simple como una lista de enteros:


List<Integer>


Es en realidad:

List -> referencias -> objetos Integer


¿Qué propone Valhalla?


Valhalla introduce un nuevo tipo de clases: Clases inline (value classes)


Ejemplo conceptual:


value class Point {

    int x;

    int y;

}


Esto permite:

  • Sin identidad (no == por referencia)
  • Sin overhead de objeto
  • Layout compacto en memoria


¿Por qué esto es importante?


Mejora de rendimiento

  • Menos uso de heap
  • Mejor uso de CPU cache
  • Menos GC


Estructuras más eficientes


Ejemplo:

List<Point>


Con Valhalla podría ser:

[ x1, y1, x2, y2, x3, y3 ]


👉 En lugar de:

[ ref -> Point, ref -> Point, ref -> Point ]


Conceptos clave de Valhalla


Value Objects

  • No tienen identidad, la identidad esta dada por su propio valor
  • Son inmutables (conceptualmente)
  • Se comparan por contenido


Flattening

El JVM puede “aplanar” estructuras:


class Line {

    Point p1;

    Point p2;

}


En memoria:

[x1, y1, x2, y2]


Specialized Generics (futuro)

Uno de los grandes problemas actuales:

List<int> ❌

List<Integer> ✅ (pero lento)


Valhalla busca permitir:

List<int> ✅


Sin boxing


Cambios importantes a tener en cuenta

  • No todo será automáticamente value type
  • Cambia el modelo mental (menos identidad, más datos)
  • Impacta librerías y frameworks


Project Valhalla aún está en desarrollo activo.

No es parte estable de Java todavía, pero ya hay:

  • Prototipos en OpenJDK
  • Features en incubación


Veamos un ejemplo: 


 Antes (Java actual)


class Complex {

    double re;

    double im;

}


  • Objeto en heap
  • Referencias


Con Valhalla (ideal)


value class Complex {

    double re;

    double im;

}


  • Datos contiguos
  • Sin overhead




Project Valhalla busca algo muy ambicioso:

Combinar lo mejor de OOP con la eficiencia de lenguajes de bajo nivel


En otras palabras:

  • Mantener la simplicidad de Java
  • Reducir el costo de abstracción


En C# tenemos el struct que es muy similar a lo que busca Valhalla.


martes, 21 de abril de 2026

Records vs Clases vs Lombok vs Kotlin vs Scala


¿Cuál es la mejor forma de modelar datos? Desde los Struct de c++ nos venimos preguntando esto. Vamos a ver algunas opciones modernas que nos provee la plataforma java. 

Cuando trabajamos con objetos que representan datos (DTOs, Value Objects, etc.), distintos lenguajes ofrecen soluciones para evitar el boilerplate.


En este post comparamos:

  • Records en Java
  • Clases tradicionales
  • Lombok
  • Data classes en Kotlin
  • Case classes en Scala


1. Clase tradicional (Java)


public class Persona {

    private final String nombre;

    private final int edad;


    public Persona(String nombre, int edad) {

        this.nombre = nombre;

        this.edad = edad;

    }


    public String getNombre() { return nombre; }

    public int getEdad() { return edad; }


    @Override public boolean equals(Object o) { ... }

    @Override public int hashCode() { ... }

    @Override public String toString() { ... }

}

Ventajas

  • Total control
  • Compatible con todo (JPA, frameworks)


Desventajas

  • Mucho boilerplate
  • Propenso a errores


2. Records (Java)


public record Persona(String nombre, int edad) {}


Ventajas

  • Ultra conciso
  • Inmutabilidad garantizada
  • Sin dependencias


Desventajas

  • Menos flexible
  • No sirve bien con JPA
  • No herencia


3. Lombok (@Data)


import lombok.Data;


@Data

public class Persona {

    private String nombre;

    private int edad;

}


Ventajas

  • Reduce mucho código
  • Mutable o inmutable (configurable)


Desventajas

  • Dependencia externa
  • "Magia" en compilación (puede confundir)
  •  Problemas en tooling/debug


4. Data Classes (Kotlin)


data class Persona(val nombre: String, val edad: Int)


Ventajas

  • Muy conciso
  • Inmutable por defecto
  • copy() incluido
  • Destructuring


val (nombre, edad) = persona


Desventajas

  • Requiere usar Kotlin
  • Interoperabilidad Java no siempre perfecta


5. Case Classes (Scala)


case class Persona(nombre: String, edad: Int)


Ventajas

  • Inmutables
  • Pattern matching nativo
  • copy() automático
  • Muy expresivas


persona match {

  case Persona(nombre, edad) => println(nombre)

}


Desventajas

  • Curva de aprendizaje
  • Ecosistema más complejo


Y entonces? Y ninguno es super mejor, pero podemos tener estas reglas: 


Java Records

Ideal para:

  • DTOs simples
  • APIs REST
  • Código moderno sin dependencias


Son el "mínimo viable elegante" en Java.


Lombok

Ideal para:

  • Proyectos legacy
  • Equipos que ya lo usan


 Soluciona el problema… pero no es parte del lenguaje.


Kotlin

La mejor experiencia general para modelado de datos.

  • copy()
  • destructuring
  • null-safety


Es claramente superior en ergonomía.


Scala

El más poderoso conceptualmente.

  • Pattern matching real
  • Inmutabilidad fuerte
  • Integración con FP


Pero más complejo.


 Clases Java

 Siguen siendo necesarias cuando:

  • Usás JPA
  • Necesitás mutabilidad
  • Requerís control total


Si estás en Java moderno → Records

Si querés máxima productividad → Kotlin

Si buscás poder expresivo → Scala

Si estás en legacy → Lombok o clases



lunes, 20 de abril de 2026

Records en Java: Datos Inmutables de Forma Elegante


Desde Java 14, el lenguaje incorporó una nueva forma de definir clases de datos: los records.

Su objetivo es simple: reducir el boilerplate cuando solo queremos representar datos inmutables.


¿Qué problema vienen a resolver?

Antes de records, una clase típica para representar datos se veía así:


public class Persona {

    private final String nombre;

    private final int edad;


    public Persona(String nombre, int edad) {

        this.nombre = nombre;

        this.edad = edad;

    }


    public String getNombre() {

        return nombre;

    }


    public int getEdad() {

        return edad;

    }


    @Override

    public boolean equals(Object o) { ... }


    @Override

    public int hashCode() { ... }


    @Override

    public String toString() { ... }

}


Mucho código repetitivo, ¿no?

Con records, lo mismo se define así:


public record Persona(String nombre, int edad) {}


Y listo.


Java automáticamente genera:

  • Constructor
  • Getters (sin prefijo get)
  • equals()
  • hashCode()
  • toString()


Un record es una clase especial que:

  • Es inmutable
  • Es final
  • Extiende implícitamente de java.lang.Record


Persona p = new Persona("Juan", 30);

System.out.println(p.nombre()); // Juan

System.out.println(p.edad());   // 30


Podemos hacer un constructor personalizado por ejemplo para validar datos:


public record Persona(String nombre, int edad) {

    public Persona {

        if (edad < 0) {

            throw new IllegalArgumentException("Edad inválida");

        }

    }

}


Este es un constructor compacto.

Los records no son solo datos, también pueden tener lógica:


public record Persona(String nombre, int edad) {

    public boolean esMayorDeEdad() {

        return edad >= 18;

    }

}


Restricciones importantes:

  • No podés extender otras clases
  • Los atributos son implícitamente final
  • No hay setters
  • No están pensados para entidades mutables (ej: JPA)


¿Cuándo usar Records?

Usalos cuando:

✔ Tenés objetos de solo datos (DTOs)

✔ Querés inmutabilidad

✔ Buscás claridad y menos boilerplate


Evitarlos cuando:

❌ Necesitás mutabilidad

❌ Estás modelando entidades complejas

❌ Usás frameworks que requieren setters (como JPA tradicional)


Con features modernas (Java 21+), los records se integran muy bien con pattern matching:


if (obj instanceof Persona(String nombre, int edad)) {

    System.out.println(nombre);

}


Esto hace el código más declarativo y expresivo.


Los records son una de las mejoras más importantes en Java moderno:

  • Reducen código repetitivo
  • Fomentan inmutabilidad
  • Mejoran la legibilidad


Son ideales para modelar datos de forma clara y segura.

domingo, 19 de abril de 2026

¿Por qué Java usa Streams?


Cuando Java introdujo Streams en Java 8, no fue solo para “hacer código más lindo”, sino para cambiar la forma en que procesamos colecciones.


Pero otros lenguajes de la JVM ya resolvían esto de forma distinta.

Entonces, la pregunta es: ¿por qué Java eligió Streams y no otro modelo?


Antes de Java 8:

List<String> result = new ArrayList<>();

for (String s : lista) {

    if (s.length() > 3) {

        result.add(s.toUpperCase());

    }

}


Mucho boilerplate

  • No es declarativo
  • Difícil de paralelizar
  • Mezcla lógica con control de flujo


La solución: Streams en Java


Con Streams:


List<String> result = lista.stream()

    .filter(s -> s.length() > 3)

    .map(String::toUpperCase)

    .toList();


Claves del diseño:

  • Evaluación lazy
  • Pipeline de operaciones
  • Separación entre datos y procesamiento
  • Fácil paralelización (parallelStream())


Java construyó un modelo nuevo, no solo métodos en Collection ¿y por qué? Otros lenguajes los hacían bien. 

Scala

lista.filter(_.length > 3).map(_.toUpperCase)


Características

  • Colecciones inmutables por defecto
  • Operaciones directamente sobre la colección
  • Lazy solo si usás View o LazyList

Scala no necesita Streams porque su API de colecciones ya es funcional


Kotlin

lista.filter { it.length > 3 }

     .map { it.uppercase() }


Características:

  • API funcional sobre colecciones
  • Operaciones eager por defecto


Para lazy:

lista.asSequence()

    .filter { it.length > 3 }

    .map { it.uppercase() }


Diferencia clave

Kotlin separa:

  • List (eager)
  • Sequence (lazy)


Java unificó esto en Streams.


Groovy

lista.findAll { it.length() > 3 }

     .collect { it.toUpperCase() }


Características

  • Muy expresivo
  • Dinámico
  • API funcional desde hace años


Diferencia clave

Más simple, pero:

  • menos eficiente
  • no lazy por defecto
  • sin optimización tipo pipeline


Entonces ¿Por qué Java eligió Streams?


Java tenía restricciones fuertes:

1. Compatibilidad hacia atrás, no podía romper Collection

2. Necesidad de lazy evaluation para evitar:

  • listas intermedias
  • consumo extra de memoria


3. Paralelismo

Streams permiten: lista.parallelStream() sin cambiar el código lógico


4. Pipeline optimizable


La JVM puede optimizar: filter → map → reduce, como una sola operación


Java no eligió Streams por casualidad.

Fue una decisión para:

  • mantener compatibilidad
  • introducir programación funcional
  • mejorar performance
  • habilitar paralelismo


Mientras otros lenguajes:

  • ya eran funcionales (Scala)
  • o tomaron caminos más simples (Kotlin, Groovy)



sábado, 18 de abril de 2026

Java 26: Hacia dónde evoluciona la plataforma


Java 26 representa una versión en evolución dentro del ciclo de releases de Java, donde se profundizan cambios importantes en concurrencia, rendimiento y modelo de datos.

Tipo de release: No LTS

Estado: 🧪 En desarrollo / Early Access

Enfoque: innovación + maduración de features clave


Concurrencia (Project Loom)

Java continúa consolidando un nuevo modelo de concurrencia.


Structured Concurrency (posible estabilización)

Permite agrupar tareas relacionadas como una unidad lógica

Simplifica:

  • manejo de errores
  • cancelación
  • sincronización


Hace que el código concurrente sea más legible y seguro


Virtual Threads (optimización continua)

  • Mejor integración con APIs existentes
  • Ajustes de rendimiento
  • Mayor adopción en frameworks


Lenguaje: menos boilerplate


Pattern Matching (más evolución)

  • Código más declarativo
  • Reducción de casts explícitos
  • Mejor integración con `switch`


Java sigue acercándose a un estilo más expresivo sin perder claridad.

Interoperabilidad nativa

Foreign Function & Memory API (FFM)

  • Más cerca de ser estándar definitivo
  • Alternativa real a JNI


Permite:

  • acceso eficiente a memoria off-heap
  • llamadas a librerías nativas


Clave para aplicaciones de alto rendimiento


Proyecto Valhalla (avance gradual)

Uno de los cambios más importantes a largo plazo.


Value Objects (en progreso)

  • Objetos sin identidad
  • Mejor uso de memoria
  • Mayor eficiencia en estructuras de datos


Impacto esperado:

  • colecciones más rápidas
  • menos overhead de objetos


JVM y rendimiento


  • Mejoras continuas en:
    • G1
    • ZGC
  • Optimización del JIT
  • Mejor uso de CPU moderna


Java 26 no es una versión de adopción masiva, sino una versión que:

  • empuja nuevas ideas
  • estabiliza features incubadas
  • prepara cambios grandes a futuro


martes, 14 de abril de 2026

Alias en imports en Java: lo que no existe (y cómo resolverlo)


Cuando venís de otros lenguajes, es común esperar algo como esto:

import com.ejemplo.A as A1; // ❌


Pero en Java esto simplemente no existe.


 ¿Qué son los alias en imports?


Un alias permite:

  • Importar dos clases con el mismo nombre
  • Y diferenciarlas con nombres alternativos


Ejemplo típico en otros lenguajes:


Kotlin

import com.ejemplo.a.Clase as ClaseA

import com.ejemplo.b.Clase as ClaseB


Scala

import com.ejemplo.a.{Clase => ClaseA}

import com.ejemplo.b.{Clase => ClaseB}


C#

using ClaseA = Ejemplo.A.Clase;

using ClaseB = Ejemplo.B.Clase;


¿Qué pasa en Java?


Si tenés dos clases con el mismo nombre:


import com.ejemplo.a.Clase;

import com.ejemplo.b.Clase; // ❌ conflicto


Java no sabe cuál usar.

La solución es tenés que usar el nombre completo en al menos uno:


import com.ejemplo.a.Clase;


public class Main {

    public static void main(String[] args) {

        Clase a = new Clase();

        com.ejemplo.b.Clase b = new com.ejemplo.b.Clase();

    }

}


¿Por qué Java no tiene alias?


El lenguaje prioriza:

  • Simplicidad
  • Legibilidad explícita
  • Evitar ambigüedades en compilación

No hay transformación de nombres en imports


Problemas reales que genera:

  • Código más verboso
  • Menor ergonomía
  • Conflictos frecuentes en proyectos grandes
  • Difícil integración entre librerías con naming similar

Java no soporta alias en imports y la solución es usar nombres completos. Pero otros lenguajes modernos sí lo resuelven mejor


viernes, 10 de abril de 2026

Java 25: Evolución y consolidación de la plataforma


Java SE 25 continúa el camino de modernización de Java, con foco en concurrencia, rendimiento y maduración de features introducidas en versiones anteriores.

Ojo es No LTS


Concurrencia (Project Loom)


Java sigue apostando fuerte por la concurrencia moderna.

Virtual Threads (madurez)

  • Totalmente integrados en el ecosistema
  • Mejoras en estabilidad y rendimiento
  • Transparencia casi total respecto a threads tradicionales


Beneficio clave: Escalar a miles de tareas concurrentes sin complejidad extra


Scoped Values (evolución)

Alternativa moderna a ThreadLocal:


ScopedValue<String> user = ScopedValue.newInstance();


ScopedValue.where(user, "Emanuel").run(() -> {

    System.out.println(user.get());

});


  • Inmutables
  • Seguros en entornos concurrentes
  • Ideales para usar con Virtual Threads


Structured Concurrency (avance)

  • Mejor organización de tareas relacionadas
  • Manejo más claro de errores y cancelaciones
  • Código más predecible


Lenguaje: más simple y expresivo


Pattern Matching (refinamientos)

  • Mejoras en switch
  • Menos casting manual
  • Código más limpio


switch (obj) {

    case String s -> System.out.println(s);

    case null -> System.out.println("null");

    default -> {}

}


Interoperabilidad


Foreign Function & Memory API

  • Más estable y usable
  • Reemplazo progresivo de JNI


Permite:

  • llamar a código nativo
  • manejar memoria off-heap de forma segura


JVM y rendimiento

Garbage Collectors


Mejoras continuas en:

  • G1
  • ZGC


Optimizaciones generales

  • Menor latencia
  • Mejor throughput
  • Ajustes en el JIT


Ecosistema

  • Mejor integración con frameworks modernos
  • Tooling más alineado con Virtual Threads
  • Preparación para proyectos futuros como Valhalla


Java 25 no busca revolucionar, sino consolidar lo que ya empezó a cambiar Java profundamente:

  • Concurrencia moderna ya usable en producción
  • Lenguaje más expresivo
  • Mejor performance


martes, 7 de abril de 2026

Java 24: Novedades y características principales


Java SE 24 continúa la evolución de la plataforma con foco en rendimiento, concurrencia y simplificación del lenguaje.

Ojo es no LTS!


Concurrencia moderna (Project Loom)

Uno de los ejes más importantes sigue siendo la evolución de la concurrencia.


Virtual Threads (mejoras)

  • Threads livianos gestionados por la JVM
  • Permiten manejar miles o millones de tareas concurrentes
  • Menor costo que los threads tradicionales


Structured Concurrency (incubating)

  • Permite tratar múltiples tareas como una sola unidad lógica
  • Mejora el manejo de errores y cancelaciones


Ejemplo conceptual:


try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

    scope.fork(() -> servicioA());

    scope.fork(() -> servicioB());

    scope.join();

    scope.throwIfFailed();

}


Lenguaje: más expresividad


Pattern Matching (evolución)

  • Mejora continua en switch
  • Código más declarativo y menos verboso


switch (obj) {

    case String s -> System.out.println(s.length());

    case Integer i -> System.out.println(i * 2);

    default -> {}

}


Interoperabilidad nativa

Foreign Function & Memory API (evolución)

  • Reemplazo moderno de JNI
  • Acceso seguro a memoria fuera del heap
  • Llamadas a código nativo (C/C++)


Beneficios:

  • más performance
  •  menos complejidad que JNI


Rendimiento y JVM

Garbage Collectors

Mejoras en:

  • G1
  • ZGC


Optimizaciones generales

  • Mejor uso de CPU y memoria
  • Reducción de pausas


Otras mejoras

  • Refinamientos en APIs estándar
  • Mejoras internas en la JVM
  • Preparación para futuros proyectos como Valhalla


Java 24 no introduce cambios “revolucionarios”, pero sí consolida tendencias clave:

  • Concurrencia moderna (Loom)
  • Código más expresivo (Pattern Matching)
  • Mejor interoperabilidad (FFM API)
  • Performance constante


Es una versión que prepara el terreno para cambios más grandes en futuras releases.

sábado, 4 de abril de 2026

¿Cual es el estado de los lenguajes que corren sobre la plataforma Java?



La plataforma Java no es solo el lenguaje Java. Gracias a la JVM (Java Virtual Machine), es posible ejecutar múltiples lenguajes con distintos paradigmas y objetivos.

A continuación, un resumen breve de los más relevantes:


Java

Objetivo: Lenguaje generalista, orientado a objetos.

Uso típico: Backend, enterprise, Android (históricamente).

Estado: Activo y en constante evolución (LTS recientes, mejoras funcionales).


Kotlin

Objetivo: Alternativa moderna a Java, más concisa y segura.

Uso típico: Android, backend, multiplataforma.

Estado:  Muy activo, impulsado por JetBrains y adoptado oficialmente por Google.


Scala

Objetivo: Mezclar programación funcional y orientada a objetos.

Uso típico: Big Data, sistemas distribuidos.

Estado: Activo, pero con menor adopción reciente frente a Kotlin.


Groovy

Objetivo: Lenguaje dinámico para simplificar Java.

Uso típico: Scripts, testing, herramientas como Gradle.

Estado:  Estable, pero en segundo plano.


Clojure

Objetivo: Programación funcional pura (Lisp en la JVM).

Uso típico: Sistemas concurrentes, data processing.

Estado: Activo en nichos específicos.


Jython

Objetivo: Implementación de Python sobre la JVM.

Uso típico: Integración con ecosistema Java.

Estado: Limitado (sin soporte moderno de Python 3 completo).


JRuby

Objetivo: Ejecutar Ruby en la JVM.

Uso típico: Integración con sistemas Java.

Estado: Activo, pero nicho.


Frege

Objetivo: Lenguaje funcional inspirado en Haskell.

Uso típico: Académico / experimental.

Estado: Poco activo.


Eta

Objetivo: Llevar Haskell a la JVM.

Uso típico: Funcional puro sobre JVM.

Estado: Proyecto prácticamente detenido.


 JavaScript (GraalVM)

Objetivo: Ejecutar JavaScript en la JVM mediante GraalVM.

Uso típico: Polyglot, microservicios, scripting.

Estado:  Activo y en crecimiento.


Python (GraalVM)

Objetivo: Ejecutar Python sobre la JVM con GraalVM.

Uso típico: Integración polyglot.

Estado:  Experimental.


La JVM es en una plataforma polyglot, donde distintos lenguajes conviven según la necesidad.

Cursos Gugler: Inscripciones para el primer cuatrimestre del año 2026

Se encuentran abiertas las inscripciones para el primer cuatrimestre del año 2026, para todas las capacitaciones dictadas por el Laboratorio de Investigación Gugler. Podés asegurar tu lugar en el curso y comisión que desees !!!.

Las clases inician:

  • Lunes 06/04 , Jueves 09/04 o Sábado 11/04, según el curso que elegiste.

Podés inscribirte utilizando el siguiente enlace: INSCRIBIRME

Cursos, horarios y comisiones disponiblesINFO CURSOS






miércoles, 1 de abril de 2026

Rendimiento de Consola: Linux vs Windows (¿realmente uno es más rápido?)


Hace poco me encontré con un comportamiento curioso ejecutando el siguiente código en Java:


var lista = new LinkedList<Long>();

for (var i = 0L; i < Long.MAX_VALUE; i++) {

    System.out.println(i);

    lista.add(i);

}


En Linux corría muchísimo más rápido que en Windows.


La primera reacción fue: ¿La JVM es más rápida en Linux?

Pero la respuesta es: no necesariamente.


El problema no está en Java ni en la lógica del programa. Está en: System.out.println(i);


Cada println implica:

  • Sincronización interna (thread-safe)
  • Conversión a string
  • Llamada al sistema operativo (I/O)
  • Escritura en la consola


Windows (cmd / PowerShell)

  • Consola históricamente más lenta
  • Mayor overhead en escritura
  • Menor eficiencia en buffering
  • Renderizado de texto menos optimizado


🐧 Linux (terminal)

  • Mejor manejo de buffers
  • I/O más eficiente
  • Consolas más livianas (bash, zsh, tty)
  • Mejor throughput de escritura


Si sacamos el println anda muy rápido :


var lista = new LinkedList<Long>();

for (var i = 0L; i < 10_000_000; i++) {

    lista.add(i);

}


El cuello de botella era la consola, no el sistema operativo.


Como lecciones importantes tengo :

  1. Nunca midas performance con println
  2. El I/O domina el rendimiento
  3. No culpes al lenguaje o al OS sin aislar variables
  4. Linux suele tener mejor rendimiento de consola



sábado, 28 de marzo de 2026

GraalOS ¿qué es y en qué estado está?


Si venís siguiendo GraalVM, probablemente escuchaste hablar de GraalOS.


La idea suena ambiciosa. Un sistema operativo optimizado para correr aplicaciones compiladas con GraalVM.

Pero… ¿es algo real hoy? ¿o sigue siendo experimental?


GraalOS es un concepto/proyecto que busca ejecutar aplicaciones native image directamente sobre un sistema mínimo.


Sin necesidad de:

  • JVM tradicional
  • Sistema operativo generalista


En esencia:

  • Llevar la idea de GraalVM al extremo:
  • una app = un runtime + un OS mínimo

GraalOS está muy relacionado con el concepto de Unikernel


Un unikernel:

  • Incluye solo lo necesario para una app
  • Corre directamente sobre el hypervisor
  • Reduce overhead y superficie de ataque


Pero, pero, GraalOS no es un producto maduro ni ampliamente disponible


En los últimos años:

  • No tuvo adopción masiva
  • No hay releases estables mainstream
  • No forma parte del flujo típico de GraalVM


En la práctica es más un concepto exploratorio / investigación que una herramienta de uso diario.

Mientras GraalVM avanzó mucho, GraalOS perdió protagonismo :(


En lugar de GraalOS, crecieron:

  • Containers (Docker)
  • Orquestación (Kubernetes)
  • Serverless

La industria resolvió el problema con otra abstracción


Hoy usarías antes:

  • Un container liviano
  • Un binary con GraalVM Native Image
  • Kubernetes o serverless


Antes que meterte en algo como GraalOS. GraalOS es más una idea interesante que una herramienta práctica.

¿En que anda GraalVM?


GraalVM es uno de los proyectos más interesantes del ecosistema Java. Permite ejecutar múltiples lenguajes (Java, JavaScript, Python, etc.) y, sobre todo, compilar aplicaciones a binarios nativos mediante Native Image.

En los últimos años, el proyecto evolucionó bastante — no tanto en hype, sino en madurez real. Además, hubo cambios importantes en cómo se distribuye y licencia.


Uno de los cambios más importantes: Gran parte de GraalVM se integró más profundamente con OpenJDK

  • El compilador Graal JIT ya no es algo “externo raro”
  • Se volvió más estándar dentro del ecosistema Java
  • Mejor compatibilidad con versiones recientes del JDK (17, 21, 23)


En otras palabras:

  • Antes GraalVM era “algo aparte”.
  • Hoy está mucho más alineado con el stack oficial de Java.


La feature estrella sigue siendo:

  • Ahead-of-Time compilation (AOT)
  • Genera binarios nativos sin JVM
  • Arranque ultra rápido
  • Bajo consumo de memoria


En estos años mejoró muchísimo:

  • Mejor soporte para frameworks (Spring, Micronaut, Quarkus)
  • Menos configuración manual
  • Mejor manejo de reflection
  • Debugging menos doloroso


Especialmente con:

  • Spring Boot + AOT
  • Quarkus (que sigue siendo el más “native-first”)


Cambio clave: licencias y distribución. Acá está el punto más importante del post 

Antes existían dos ediciones:

  •   Community Edition (open source)
  •   Enterprise Edition (mejoras pagas)


Ahora (últimos años) oracle reorganizó todo:

GraalVM Community

  • Basado en OpenJDK
  • Licencia: GPL (con classpath exception)
  • Sigue siendo gratis y open source


GraalVM Oracle (antes Enterprise)

  • Parte del ecosistema comercial de Oracle
  • Incluye optimizaciones avanzadas
  • Integrado con Oracle JDK


Y esto está más alineado con el modelo de licencias de Oracle Java.


Bueno pero, ¿GraalVM ahora es pago?”

La respuesta corta:

  • No, la versión Community sigue siendo gratis
  • Pero algunas features avanzadas están en el ecosistema comercial


Y algo que no me gusta es que Polyglot tiene menos hype y es más de nicho. 

GraalVM nació con la idea de: “Un runtime para múltiples lenguajes”


Soporta:

  • Java
  • JavaScript
  • Python
  • Ruby
  • WebAssembly


Pero, no se usa mucho, creo que es una tendencia general en la industria. 


En estos años se vio algo interesante que hubo menos marketing, más uso concreto


Casos típicos:

  • Microservicios con arranque rápido
  • Serverless
  • Apps en contenedores (menos RAM)


La pregunta del millon ¿Vale la pena hoy? Sí, si:

  • Querés arranque rápido
  • Buscás bajo consumo de memoria
  • Usás frameworks compatibles
  • Estás en cloud / Kubernetes


No tanto si:

  • Usás mucha reflexión dinámica
  • Dependés de librerías complejas
  • Querés “zero config”

Mi opinión es que GraalVM es un producto real, que todos tendríamos que conocer y usar. Si tenés una aplicación en Java y en el cloud, no cuesta nada probar migrar a GraalVM y medir su performance, ya sea usando Graal Jit o binarios nativos.