Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions content/enterprise/ejb-timer-vs-jakarta-scheduler.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 102,
"slug": "ejb-timer-vs-jakarta-scheduler",
"title": "EJB Timer vs Jakarta Scheduler",
"category": "enterprise",
"difficulty": "intermediate",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "Jakarta EE 10+",
"oldApproach": "EJB TimerService",
"modernApproach": "ManagedScheduledExecutorService",
"oldCode": "@Stateless\npublic class ReportGenerator {\n @Resource\n TimerService timerService;\n\n @PostConstruct\n public void init() {\n timerService.createCalendarTimer(\n new ScheduleExpression()\n .hour(\"2\").minute(\"0\"));\n }\n\n @Timeout\n public void generateReport(Timer timer) {\n // runs every day at 02:00\n buildDailyReport();\n }\n}",
"modernCode": "@ApplicationScoped\npublic class ReportGenerator {\n @Resource\n ManagedScheduledExecutorService scheduler;\n\n @PostConstruct\n public void init() {\n scheduler.scheduleAtFixedRate(\n this::generateReport,\n 0, 24, TimeUnit.HOURS);\n }\n\n public void generateReport() {\n buildDailyReport();\n }\n}",
"summary": "Replace heavyweight EJB timers with Jakarta Concurrency's ManagedScheduledExecutorService for simpler scheduling.",
"explanation": "EJB timers require a @Stateless or @Singleton bean with a @Timeout callback and XML or annotation-based schedule expressions. Jakarta Concurrency provides ManagedScheduledExecutorService, which uses the familiar java.util.concurrent scheduling API. The result is less boilerplate, easier unit testing, and no EJB container dependency.",
"whyModernWins": [
{
"icon": "🪶",
"title": "Reduced boilerplate",
"desc": "No @Timeout callback or ScheduleExpression — use the standard ScheduledExecutorService API."
},
{
"icon": "🧪",
"title": "Better testability",
"desc": "Plain methods and executor mocks make unit testing straightforward without EJB container."
},
{
"icon": "☁️",
"title": "Cloud-native friendly",
"desc": "Managed executors integrate with container lifecycle and work in lightweight runtimes."
}
],
"support": {
"state": "available",
"description": "Available since Jakarta EE 10 / Concurrency 3.0"
},
"prev": "enterprise/jpa-vs-jakarta-data",
"next": "enterprise/jndi-lookup-vs-cdi-injection",
"related": [
"enterprise/ejb-vs-cdi",
"concurrency/virtual-threads",
"concurrency/executor-try-with-resources"
],
"docs": [
{
"title": "Jakarta Concurrency Specification",
"href": "https://jakarta.ee/specifications/concurrency/"
},
{
"title": "Jakarta Concurrency 3.0 API",
"href": "https://jakarta.ee/specifications/concurrency/3.0/apidocs/"
}
]
}
54 changes: 54 additions & 0 deletions content/enterprise/jdbc-resultset-vs-jpa-criteria.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 109,
"slug": "jdbc-resultset-vs-jpa-criteria",
"title": "JDBC ResultSet Mapping vs JPA Criteria API",
"category": "enterprise",
"difficulty": "advanced",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "Jakarta EE 8+",
"oldApproach": "JDBC ResultSet",
"modernApproach": "JPA Criteria API",
"oldCode": "String sql = \"SELECT * FROM users\"\n + \" WHERE status = ? AND age > ?\";\ntry (Connection con = ds.getConnection();\n PreparedStatement ps =\n con.prepareStatement(sql)) {\n ps.setString(1, status);\n ps.setInt(2, minAge);\n ResultSet rs = ps.executeQuery();\n List<User> users = new ArrayList<>();\n while (rs.next()) {\n User u = new User();\n u.setId(rs.getLong(\"id\"));\n u.setName(rs.getString(\"name\"));\n u.setAge(rs.getInt(\"age\"));\n users.add(u);\n }\n}",
"modernCode": "@PersistenceContext\nEntityManager em;\n\npublic List<User> findActiveAboveAge(\n String status, int minAge) {\n CriteriaBuilder cb = em.getCriteriaBuilder();\n CriteriaQuery<User> cq =\n cb.createQuery(User.class);\n Root<User> root = cq.from(User.class);\n cq.select(root).where(\n cb.equal(root.get(\"status\"), status),\n cb.greaterThan(root.get(\"age\"), minAge));\n return em.createQuery(cq).getResultList();\n}",
"summary": "Replace manual JDBC ResultSet mapping with JPA's type-safe Criteria API for dynamic queries.",
"explanation": "Raw JDBC requires building SQL strings, setting parameters by index, and mapping each ResultSet column manually — a process that is error-prone and breaks silently when columns change. The JPA Criteria API builds queries programmatically using a type-safe builder pattern. Column names are validated against the entity model, result mapping is automatic, and complex dynamic queries compose cleanly without string concatenation.",
"whyModernWins": [
{
"icon": "🔒",
"title": "Type-safe queries",
"desc": "The Criteria builder catches field name and type mismatches at compile time."
},
{
"icon": "🗺️",
"title": "Automatic mapping",
"desc": "JPA maps result rows to entity objects — no manual column-by-column extraction."
},
{
"icon": "🧩",
"title": "Composable predicates",
"desc": "Dynamic where-clauses build cleanly with and(), or(), and reusable Predicate objects."
}
],
"support": {
"state": "available",
"description": "Widely available since Jakarta EE 8 / Java 11"
},
"prev": "enterprise/singleton-ejb-vs-cdi-application-scoped",
"next": null,
"related": [
"enterprise/jdbc-vs-jpa",
"enterprise/jpa-vs-jakarta-data",
"enterprise/manual-transaction-vs-declarative"
],
"docs": [
{
"title": "Jakarta Persistence Specification",
"href": "https://jakarta.ee/specifications/persistence/"
},
{
"title": "Jakarta Persistence 3.1 — Criteria API",
"href": "https://jakarta.ee/specifications/persistence/3.1/apidocs/"
}
]
}
54 changes: 54 additions & 0 deletions content/enterprise/jndi-lookup-vs-cdi-injection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 103,
"slug": "jndi-lookup-vs-cdi-injection",
"title": "JNDI Lookup vs CDI Injection",
"category": "enterprise",
"difficulty": "intermediate",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "Jakarta EE 8+",
"oldApproach": "JNDI Lookup",
"modernApproach": "CDI @Inject",
"oldCode": "public class OrderService {\n private DataSource ds;\n\n public void init() throws NamingException {\n InitialContext ctx = new InitialContext();\n ds = (DataSource) ctx.lookup(\n \"java:comp/env/jdbc/OrderDB\");\n }\n\n public List<Order> findAll()\n throws SQLException {\n try (Connection con = ds.getConnection()) {\n // query orders\n }\n }\n}",
"modernCode": "@ApplicationScoped\npublic class OrderService {\n @Inject\n @Resource(name = \"jdbc/OrderDB\")\n DataSource ds;\n\n public List<Order> findAll()\n throws SQLException {\n try (Connection con = ds.getConnection()) {\n // query orders\n }\n }\n}",
"summary": "Replace fragile JNDI string lookups with type-safe CDI injection for container-managed resources.",
"explanation": "The traditional JNDI pattern forces you to use string-based resource names, handle NamingException, and manage an InitialContext. CDI injection with @Inject (or @Resource for container resources) lets the container wire dependencies automatically. Typos become compile-time errors, and classes are easier to test because dependencies can be injected directly.",
"whyModernWins": [
{
"icon": "🔒",
"title": "Type-safe wiring",
"desc": "Injection errors are caught at deployment time, not at runtime via string lookups."
},
{
"icon": "🗑️",
"title": "No boilerplate",
"desc": "Eliminates InitialContext creation, JNDI name strings, and NamingException handling."
},
{
"icon": "🧪",
"title": "Testable",
"desc": "Dependencies are injected fields, easily replaced with mocks in unit tests."
}
],
"support": {
"state": "available",
"description": "Widely available since Jakarta EE 8 / Java 11"
},
"prev": "enterprise/ejb-timer-vs-jakarta-scheduler",
"next": "enterprise/manual-transaction-vs-declarative",
"related": [
"enterprise/ejb-vs-cdi",
"enterprise/jdbc-vs-jpa",
"enterprise/singleton-ejb-vs-cdi-application-scoped"
],
"docs": [
{
"title": "Jakarta CDI Specification",
"href": "https://jakarta.ee/specifications/cdi/"
},
{
"title": "Jakarta Annotations — @Resource",
"href": "https://jakarta.ee/specifications/annotations/"
}
]
}
2 changes: 1 addition & 1 deletion content/enterprise/jpa-vs-jakarta-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"description": "Available since Jakarta EE 11 / Java 21 (2024)"
},
"prev": "enterprise/jdbc-vs-jpa",
"next": null,
"next": "enterprise/ejb-timer-vs-jakarta-scheduler",
"related": [
"enterprise/jdbc-vs-jpa",
"enterprise/ejb-vs-cdi",
Expand Down
54 changes: 54 additions & 0 deletions content/enterprise/jsf-managed-bean-vs-cdi-named.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 107,
"slug": "jsf-managed-bean-vs-cdi-named",
"title": "JSF Managed Bean vs CDI Named Bean",
"category": "enterprise",
"difficulty": "intermediate",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "Jakarta EE 10+",
"oldApproach": "@ManagedBean",
"modernApproach": "@Named + CDI",
"oldCode": "@ManagedBean\n@SessionScoped\npublic class UserBean implements Serializable {\n @ManagedProperty(\"#{userService}\")\n private UserService userService;\n\n private String name;\n\n public String getName() { return name; }\n public void setName(String name) {\n this.name = name;\n }\n\n public void setUserService(UserService svc) {\n this.userService = svc;\n }\n}",
"modernCode": "@Named\n@SessionScoped\npublic class UserBean implements Serializable {\n @Inject\n private UserService userService;\n\n private String name;\n\n public String getName() { return name; }\n public void setName(String name) {\n this.name = name;\n }\n}",
"summary": "Replace deprecated JSF @ManagedBean with CDI @Named for a unified dependency injection model.",
"explanation": "JSF's @ManagedBean and @ManagedProperty were deprecated in Jakarta Faces 2.3 and removed in Jakarta EE 10. The CDI-based replacement uses @Named to expose the bean to EL expressions and @Inject for dependency wiring. This unifies the bean model: JSF pages, JAX-RS resources, and EJBs all share the same CDI container.",
"whyModernWins": [
{
"icon": "🔗",
"title": "Unified model",
"desc": "One CDI container manages all beans — JSF, REST, and service layers share the same injection."
},
{
"icon": "🗑️",
"title": "Less boilerplate",
"desc": "@Inject replaces @ManagedProperty and its required setter method."
},
{
"icon": "🔮",
"title": "Future-proof",
"desc": "@ManagedBean is removed in Jakarta EE 10; @Named is the supported replacement."
}
],
"support": {
"state": "available",
"description": "CDI @Named available since Java EE 6; @ManagedBean removed in Jakarta EE 10"
},
"prev": "enterprise/mdb-vs-reactive-messaging",
"next": "enterprise/singleton-ejb-vs-cdi-application-scoped",
"related": [
"enterprise/ejb-vs-cdi",
"enterprise/jndi-lookup-vs-cdi-injection",
"enterprise/servlet-vs-jaxrs"
],
"docs": [
{
"title": "Jakarta Faces Specification",
"href": "https://jakarta.ee/specifications/faces/"
},
{
"title": "Jakarta CDI Specification",
"href": "https://jakarta.ee/specifications/cdi/"
}
]
}
54 changes: 54 additions & 0 deletions content/enterprise/manual-transaction-vs-declarative.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 104,
"slug": "manual-transaction-vs-declarative",
"title": "Manual JPA Transaction vs Declarative @Transactional",
"category": "enterprise",
"difficulty": "intermediate",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "Jakarta EE 8+",
"oldApproach": "Manual Transaction",
"modernApproach": "@Transactional",
"oldCode": "@PersistenceContext\nEntityManager em;\n\npublic void transferFunds(Long from, Long to,\n BigDecimal amount) {\n EntityTransaction tx = em.getTransaction();\n tx.begin();\n try {\n Account src = em.find(Account.class, from);\n Account dst = em.find(Account.class, to);\n src.debit(amount);\n dst.credit(amount);\n tx.commit();\n } catch (Exception e) {\n tx.rollback();\n throw e;\n }\n}",
"modernCode": "@ApplicationScoped\npublic class AccountService {\n @PersistenceContext\n EntityManager em;\n\n @Transactional\n public void transferFunds(Long from, Long to,\n BigDecimal amount) {\n Account src = em.find(Account.class, from);\n Account dst = em.find(Account.class, to);\n src.debit(amount);\n dst.credit(amount);\n }\n}",
"summary": "Replace verbose begin/commit/rollback blocks with a single @Transactional annotation.",
"explanation": "Manual transaction management requires explicit begin(), commit(), and rollback() calls wrapped in try-catch blocks — every service method repeats this boilerplate. The @Transactional annotation delegates lifecycle management to the container: it begins a transaction before the method, commits on success, and rolls back on RuntimeException automatically.",
"whyModernWins": [
{
"icon": "🗑️",
"title": "No boilerplate",
"desc": "One annotation replaces repetitive begin/commit/rollback try-catch blocks."
},
{
"icon": "🛡️",
"title": "Safer rollback",
"desc": "The container guarantees rollback on unchecked exceptions — no risk of forgetting the catch block."
},
{
"icon": "📐",
"title": "Declarative control",
"desc": "Propagation, isolation, and rollback rules are expressed as annotation attributes."
}
],
"support": {
"state": "available",
"description": "Widely available since Jakarta EE 8 / Java 11"
},
"prev": "enterprise/jndi-lookup-vs-cdi-injection",
"next": "enterprise/soap-vs-jakarta-rest",
"related": [
"enterprise/ejb-vs-cdi",
"enterprise/jdbc-vs-jpa",
"enterprise/jpa-vs-jakarta-data"
],
"docs": [
{
"title": "Jakarta Transactions Specification",
"href": "https://jakarta.ee/specifications/transactions/"
},
{
"title": "Jakarta Transactions 2.0 API",
"href": "https://jakarta.ee/specifications/transactions/2.0/apidocs/"
}
]
}
54 changes: 54 additions & 0 deletions content/enterprise/mdb-vs-reactive-messaging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": 106,
"slug": "mdb-vs-reactive-messaging",
"title": "Message-Driven Bean vs Reactive Messaging",
"category": "enterprise",
"difficulty": "advanced",
"jdkVersion": "11",
"oldLabel": "Java EE",
"modernLabel": "MicroProfile 4+",
"oldApproach": "Message-Driven Bean",
"modernApproach": "Reactive Messaging",
"oldCode": "@MessageDriven(activationConfig = {\n @ActivationConfigProperty(\n propertyName = \"destinationType\",\n propertyValue = \"jakarta.jms.Queue\"),\n @ActivationConfigProperty(\n propertyName = \"destination\",\n propertyValue = \"java:/jms/OrderQueue\")\n})\npublic class OrderMDB implements MessageListener {\n @Override\n public void onMessage(Message message) {\n TextMessage txt = (TextMessage) message;\n processOrder(txt.getText());\n }\n}",
"modernCode": "@ApplicationScoped\npublic class OrderProcessor {\n @Incoming(\"orders\")\n public void process(Order order) {\n // automatically deserialized from\n // the \"orders\" channel\n fulfillOrder(order);\n }\n}",
"summary": "Replace JMS Message-Driven Beans with MicroProfile Reactive Messaging for simpler event processing.",
"explanation": "Message-Driven Beans require implementing MessageListener, configuring activation properties, and manually deserializing JMS messages. MicroProfile Reactive Messaging uses a simple @Incoming annotation on a method that receives typed objects directly. The channel configuration is externalised, making the code broker-agnostic and far easier to test.",
"whyModernWins": [
{
"icon": "🪶",
"title": "Minimal code",
"desc": "A single @Incoming method replaces the MDB class, MessageListener interface, and activation config."
},
{
"icon": "🔌",
"title": "Broker-agnostic",
"desc": "Swap Kafka, AMQP, or JMS connectors via configuration without changing application code."
},
{
"icon": "☁️",
"title": "Cloud-native fit",
"desc": "Reactive streams backpressure and lightweight runtime make it ideal for containerised deployments."
}
],
"support": {
"state": "available",
"description": "Available since MicroProfile 4.0 / SmallRye Reactive Messaging"
},
"prev": "enterprise/soap-vs-jakarta-rest",
"next": "enterprise/jsf-managed-bean-vs-cdi-named",
"related": [
"enterprise/ejb-vs-cdi",
"concurrency/structured-concurrency",
"concurrency/virtual-threads"
],
"docs": [
{
"title": "MicroProfile Reactive Messaging Specification",
"href": "https://download.eclipse.org/microprofile/microprofile-reactive-messaging-3.0/microprofile-reactive-messaging-spec-3.0.html"
},
{
"title": "SmallRye Reactive Messaging Documentation",
"href": "https://smallrye.io/smallrye-reactive-messaging/"
}
]
}
Loading