Java EE arrancó en Sun Microsystems a finales de los noventa como J2EE. Oracle lo heredó en 2010 cuando compró Sun y lo mantuvo durante años sin demasiado movimiento. En 2017 decidió donarlo a la Eclipse Foundation, que lo renombró Jakarta EE y lo abrió a una gobernanza mucho más activa.
El cambio más visible para cualquier desarrollador fue el de los paquetes: todo lo que antes empezaba por javax.* pasó a jakarta.* a partir de Jakarta EE 9 (2020). No fue una refactorización trivial, pero era necesaria para liberar el nombre de marca y permitir evolucionar la API sin depender de Oracle.
Los servidores de aplicaciones más usados hoy son WildFly (Red Hat), GlassFish (Eclipse Foundation), Payara, TomEE y Open Liberty (IBM). Todos ellos implementan el estándar y publican su certificación de conformidad.
Jakarta EE frente a Spring Boot: cuándo usar cada uno
La diferencia fundamental es que Jakarta EE es un estándar con varias implementaciones independientes, mientras que Spring Boot es un framework con una única implementación. Si escribes código que solo usa especificaciones Jakarta EE, puedes desplegarlo en WildFly, GlassFish o Payara sin tocar una línea. Con Spring Boot no tienes esa portabilidad, aunque tampoco suele ser necesaria en la mayoría de proyectos.
Spring Boot tiene más popularidad y sus starters acortan mucho el arranque de un proyecto nuevo. Jakarta EE, en cambio, sigue siendo la referencia en administraciones públicas europeas y en banca, donde el soporte certificado de IBM o de Oracle es un requisito contractual, no una preferencia técnica.
Para la mayoría de proyectos nuevos sin esas restricciones, Spring Boot es más productivo desde el primer día. Pero conocer Jakarta EE te abre puertas a entornos enterprise donde el estándar manda.
Jakarta Persistence 3.2: JPA con soporte real de records y UUID
JPA es la especificación de persistencia del estándar y en la versión 3.2 hay varias mejoras concretas que se notarán en el día a día.
UUID como tipo nativo
Hasta ahora, usar un UUID como clave primaria requería un convertidor manual o depender de extensiones propietarias del proveedor. En 3.2 ya viene de serie:
@Id
@GeneratedValue
@Column(columnDefinition = "UUID")
private UUID id;
El proveedor de persistencia (Hibernate, EclipseLink) se encarga de mapear el tipo al dialecto SQL correspondiente sin necesidad de nada más.
Records como proyecciones y embeddables
Los records de Java se pueden usar ahora como embeddables y como proyecciones de consulta. Esto encaja bien con el estilo funcional que muchos equipos están adoptando desde Java 16:
public record NombreCompleto(String nombre, String apellido) {}
@Embeddable
public record DireccionDTO(String calle, String ciudad) {}
Fechas nativas en JPQL
JPQL incorpora LOCAL DATE, LOCAL TIME y LOCAL DATETIME, que devuelven tipos de java.time directamente. Nada de new java.util.Date() en las consultas.
Jakarta Data 1.0: la novedad más importante de EE 11
Si hay algo que vale la pena destacar de esta versión, es Jakarta Data. Por primera vez, el patrón Repository llega al estándar oficial, sin necesidad de Spring Data ni de ninguna dependencia externa.
La idea es familiar si has usado Spring Data JPA:
@Repository
public interface UsuarioRepository extends CrudRepository<Usuario, Long> {
List<Usuario> findByActivoTrue();
@Query("WHERE this.nombre LIKE :patron")
List<Usuario> buscarPorNombre(String patron);
}
Hibernate Data Repositories y EclipseLink ya implementan la especificación. Para proyectos que quieran el patrón Repository sin introducir Spring en el classpath, Jakarta Data junto con Jakarta Persistence cubre el mismo caso de uso con código portátil entre proveedores.
Jakarta REST 4.0: HTTP moderno en el estándar
Jakarta REST (el antiguo JAX-RS) llega a la versión 4.0 con mejoras en el cliente HTTP y en la gestión de Server-Sent Events.
Recurso REST básico
@Path("/api/usuarios")
@Produces(MediaType.APPLICATION_JSON)
public class UsuarioResource {
@GET
@Path("/{id}")
public Usuario buscar(@PathParam("id") Long id) {
return usuarioService.buscar(id);
}
}
Client API mejorada
Client client = ClientBuilder.newClient();
Response respuesta = client
.target("https://api.ejemplo.com")
.path("/datos")
.request()
.get();
Server-Sent Events
@GET
@Path("/eventos")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void eventos(@Context SseEventSink sink, @Context Sse sse) {
try (sink) {
sink.send(sse.newEvent("primer-evento", "datos iniciales"));
}
}
La mejora en SSE es especialmente útil para notificaciones en tiempo real sin tener que tirar de WebSockets cuando no se necesita comunicación bidireccional.
CDI 4.1: inyección de dependencias en el estándar
CDI (Contexts and Dependency Injection) es el sistema de inyección de dependencias de Jakarta EE. La versión 4.1 refina el contenedor y mejora la interoperabilidad con el resto de especificaciones.
El uso básico sigue siendo el mismo:
@Inject
private UsuarioService servicio;
Los scopes definen cuánto vive un bean. @ApplicationScoped lo mantiene vivo durante toda la aplicación, @RequestScoped lo crea y destruye por petición HTTP, y @SessionScoped lo liga a la sesión del usuario.
Para producir beans de forma programática:
@Produces
@ApplicationScoped
public ConexionDB producirConexion() {
return new ConexionDB(config.getUrl());
}
Los @Qualifier sirven para distinguir entre dos implementaciones del mismo tipo, algo habitual cuando tienes varias fuentes de datos o varias estrategias de procesamiento.
Jakarta Security 4.0: autenticación declarativa con OpenID Connect
La novedad más llamativa de Security 4.0 es la integración nativa con OpenID Connect. Configurar un proveedor OIDC es tan sencillo como añadir una anotación a la clase de configuración:
@OpenIdAuthenticationMechanismDefinition(
providerURI = "https://accounts.google.com"
)
public class AppConfig {}
El control de acceso declarativo funciona en cualquier bean CDI o método REST:
@RolesAllowed("ADMIN")
public void accionRestringida() {
// solo ADMIN llega aquí
}
Y si necesitas comprobar el contexto de seguridad en tiempo de ejecución:
@Inject
SecurityContext ctx;
public void verificar() {
Principal usuario = ctx.getCallerPrincipal();
boolean esAdmin = ctx.isCallerInRole("ADMIN");
}
Para más detalle sobre cómo combinar estos mecanismos con JWT y autenticación multifactor, puedes consultar el artículo sobre seguridad en Java enterprise: de Spring Security a Jakarta Security.
Cuándo elegir Jakarta EE en 2026
Si tu organización tiene contratos de soporte con IBM (Open Liberty) u Oracle (WebLogic), Jakarta EE es la opción natural porque el estándar garantiza que el proveedor certifica cada versión. Lo mismo aplica a proyectos en banca o administración pública donde la portabilidad entre servidores certificados es un requisito legal o contractual.
Para proyectos nuevos sin esas restricciones, Spring Boot sigue siendo más rápido de arrancar y tiene un ecosistema de integraciones más amplio. Pero con Jakarta Data 1.0 en EE 11, la brecha en lo que respecta al acceso a datos se ha cerrado bastante.
La elección real no es técnica sino organizativa: ¿quién da soporte, qué certificaciones se exigen y qué conocimiento tiene el equipo? Si las respuestas apuntan a un application server certificado, Jakarta EE es el camino. Si apuntan a velocidad de desarrollo y libertad de elección de infraestructura, Spring Boot sigue mandando.
Para gestionar los cambios de esquema de base de datos en cualquiera de los dos stacks, el artículo sobre Flyway con Jakarta EE: migraciones de base de datos en el stack enterprise detalla cómo integrar Flyway con un proyecto Jakarta EE.
Imagen: Pexels / Bibek ghosh
