Skip to content

Commit 535c27a

Browse files
authored
Merge pull request #52 from javaevolved/copilot/add-new-slugs-for-ideas
Add 8 new enterprise category patterns
2 parents ea702fb + cff2ea6 commit 535c27a

9 files changed

+433
-1
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 102,
3+
"slug": "ejb-timer-vs-jakarta-scheduler",
4+
"title": "EJB Timer vs Jakarta Scheduler",
5+
"category": "enterprise",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "Jakarta EE 10+",
10+
"oldApproach": "EJB TimerService",
11+
"modernApproach": "ManagedScheduledExecutorService",
12+
"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}",
13+
"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}",
14+
"summary": "Replace heavyweight EJB timers with Jakarta Concurrency's ManagedScheduledExecutorService for simpler scheduling.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🪶",
19+
"title": "Reduced boilerplate",
20+
"desc": "No @Timeout callback or ScheduleExpression — use the standard ScheduledExecutorService API."
21+
},
22+
{
23+
"icon": "🧪",
24+
"title": "Better testability",
25+
"desc": "Plain methods and executor mocks make unit testing straightforward without EJB container."
26+
},
27+
{
28+
"icon": "☁️",
29+
"title": "Cloud-native friendly",
30+
"desc": "Managed executors integrate with container lifecycle and work in lightweight runtimes."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Available since Jakarta EE 10 / Concurrency 3.0"
36+
},
37+
"prev": "enterprise/jpa-vs-jakarta-data",
38+
"next": "enterprise/jndi-lookup-vs-cdi-injection",
39+
"related": [
40+
"enterprise/ejb-vs-cdi",
41+
"concurrency/virtual-threads",
42+
"concurrency/executor-try-with-resources"
43+
],
44+
"docs": [
45+
{
46+
"title": "Jakarta Concurrency Specification",
47+
"href": "https://jakarta.ee/specifications/concurrency/"
48+
},
49+
{
50+
"title": "Jakarta Concurrency 3.0 API",
51+
"href": "https://jakarta.ee/specifications/concurrency/3.0/apidocs/"
52+
}
53+
]
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 109,
3+
"slug": "jdbc-resultset-vs-jpa-criteria",
4+
"title": "JDBC ResultSet Mapping vs JPA Criteria API",
5+
"category": "enterprise",
6+
"difficulty": "advanced",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "Jakarta EE 8+",
10+
"oldApproach": "JDBC ResultSet",
11+
"modernApproach": "JPA Criteria API",
12+
"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}",
13+
"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}",
14+
"summary": "Replace manual JDBC ResultSet mapping with JPA's type-safe Criteria API for dynamic queries.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🔒",
19+
"title": "Type-safe queries",
20+
"desc": "The Criteria builder catches field name and type mismatches at compile time."
21+
},
22+
{
23+
"icon": "🗺️",
24+
"title": "Automatic mapping",
25+
"desc": "JPA maps result rows to entity objects — no manual column-by-column extraction."
26+
},
27+
{
28+
"icon": "🧩",
29+
"title": "Composable predicates",
30+
"desc": "Dynamic where-clauses build cleanly with and(), or(), and reusable Predicate objects."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Widely available since Jakarta EE 8 / Java 11"
36+
},
37+
"prev": "enterprise/singleton-ejb-vs-cdi-application-scoped",
38+
"next": null,
39+
"related": [
40+
"enterprise/jdbc-vs-jpa",
41+
"enterprise/jpa-vs-jakarta-data",
42+
"enterprise/manual-transaction-vs-declarative"
43+
],
44+
"docs": [
45+
{
46+
"title": "Jakarta Persistence Specification",
47+
"href": "https://jakarta.ee/specifications/persistence/"
48+
},
49+
{
50+
"title": "Jakarta Persistence 3.1 — Criteria API",
51+
"href": "https://jakarta.ee/specifications/persistence/3.1/apidocs/"
52+
}
53+
]
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 103,
3+
"slug": "jndi-lookup-vs-cdi-injection",
4+
"title": "JNDI Lookup vs CDI Injection",
5+
"category": "enterprise",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "Jakarta EE 8+",
10+
"oldApproach": "JNDI Lookup",
11+
"modernApproach": "CDI @Inject",
12+
"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}",
13+
"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}",
14+
"summary": "Replace fragile JNDI string lookups with type-safe CDI injection for container-managed resources.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🔒",
19+
"title": "Type-safe wiring",
20+
"desc": "Injection errors are caught at deployment time, not at runtime via string lookups."
21+
},
22+
{
23+
"icon": "🗑️",
24+
"title": "No boilerplate",
25+
"desc": "Eliminates InitialContext creation, JNDI name strings, and NamingException handling."
26+
},
27+
{
28+
"icon": "🧪",
29+
"title": "Testable",
30+
"desc": "Dependencies are injected fields, easily replaced with mocks in unit tests."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Widely available since Jakarta EE 8 / Java 11"
36+
},
37+
"prev": "enterprise/ejb-timer-vs-jakarta-scheduler",
38+
"next": "enterprise/manual-transaction-vs-declarative",
39+
"related": [
40+
"enterprise/ejb-vs-cdi",
41+
"enterprise/jdbc-vs-jpa",
42+
"enterprise/singleton-ejb-vs-cdi-application-scoped"
43+
],
44+
"docs": [
45+
{
46+
"title": "Jakarta CDI Specification",
47+
"href": "https://jakarta.ee/specifications/cdi/"
48+
},
49+
{
50+
"title": "Jakarta Annotations — @Resource",
51+
"href": "https://jakarta.ee/specifications/annotations/"
52+
}
53+
]
54+
}

content/enterprise/jpa-vs-jakarta-data.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Available since Jakarta EE 11 / Java 21 (2024)"
3636
},
3737
"prev": "enterprise/jdbc-vs-jpa",
38-
"next": null,
38+
"next": "enterprise/ejb-timer-vs-jakarta-scheduler",
3939
"related": [
4040
"enterprise/jdbc-vs-jpa",
4141
"enterprise/ejb-vs-cdi",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 107,
3+
"slug": "jsf-managed-bean-vs-cdi-named",
4+
"title": "JSF Managed Bean vs CDI Named Bean",
5+
"category": "enterprise",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "Jakarta EE 10+",
10+
"oldApproach": "@ManagedBean",
11+
"modernApproach": "@Named + CDI",
12+
"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}",
13+
"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}",
14+
"summary": "Replace deprecated JSF @ManagedBean with CDI @Named for a unified dependency injection model.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🔗",
19+
"title": "Unified model",
20+
"desc": "One CDI container manages all beans — JSF, REST, and service layers share the same injection."
21+
},
22+
{
23+
"icon": "🗑️",
24+
"title": "Less boilerplate",
25+
"desc": "@Inject replaces @ManagedProperty and its required setter method."
26+
},
27+
{
28+
"icon": "🔮",
29+
"title": "Future-proof",
30+
"desc": "@ManagedBean is removed in Jakarta EE 10; @Named is the supported replacement."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "CDI @Named available since Java EE 6; @ManagedBean removed in Jakarta EE 10"
36+
},
37+
"prev": "enterprise/mdb-vs-reactive-messaging",
38+
"next": "enterprise/singleton-ejb-vs-cdi-application-scoped",
39+
"related": [
40+
"enterprise/ejb-vs-cdi",
41+
"enterprise/jndi-lookup-vs-cdi-injection",
42+
"enterprise/servlet-vs-jaxrs"
43+
],
44+
"docs": [
45+
{
46+
"title": "Jakarta Faces Specification",
47+
"href": "https://jakarta.ee/specifications/faces/"
48+
},
49+
{
50+
"title": "Jakarta CDI Specification",
51+
"href": "https://jakarta.ee/specifications/cdi/"
52+
}
53+
]
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 104,
3+
"slug": "manual-transaction-vs-declarative",
4+
"title": "Manual JPA Transaction vs Declarative @Transactional",
5+
"category": "enterprise",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "Jakarta EE 8+",
10+
"oldApproach": "Manual Transaction",
11+
"modernApproach": "@Transactional",
12+
"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}",
13+
"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}",
14+
"summary": "Replace verbose begin/commit/rollback blocks with a single @Transactional annotation.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🗑️",
19+
"title": "No boilerplate",
20+
"desc": "One annotation replaces repetitive begin/commit/rollback try-catch blocks."
21+
},
22+
{
23+
"icon": "🛡️",
24+
"title": "Safer rollback",
25+
"desc": "The container guarantees rollback on unchecked exceptions — no risk of forgetting the catch block."
26+
},
27+
{
28+
"icon": "📐",
29+
"title": "Declarative control",
30+
"desc": "Propagation, isolation, and rollback rules are expressed as annotation attributes."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Widely available since Jakarta EE 8 / Java 11"
36+
},
37+
"prev": "enterprise/jndi-lookup-vs-cdi-injection",
38+
"next": "enterprise/soap-vs-jakarta-rest",
39+
"related": [
40+
"enterprise/ejb-vs-cdi",
41+
"enterprise/jdbc-vs-jpa",
42+
"enterprise/jpa-vs-jakarta-data"
43+
],
44+
"docs": [
45+
{
46+
"title": "Jakarta Transactions Specification",
47+
"href": "https://jakarta.ee/specifications/transactions/"
48+
},
49+
{
50+
"title": "Jakarta Transactions 2.0 API",
51+
"href": "https://jakarta.ee/specifications/transactions/2.0/apidocs/"
52+
}
53+
]
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 106,
3+
"slug": "mdb-vs-reactive-messaging",
4+
"title": "Message-Driven Bean vs Reactive Messaging",
5+
"category": "enterprise",
6+
"difficulty": "advanced",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java EE",
9+
"modernLabel": "MicroProfile 4+",
10+
"oldApproach": "Message-Driven Bean",
11+
"modernApproach": "Reactive Messaging",
12+
"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}",
13+
"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}",
14+
"summary": "Replace JMS Message-Driven Beans with MicroProfile Reactive Messaging for simpler event processing.",
15+
"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.",
16+
"whyModernWins": [
17+
{
18+
"icon": "🪶",
19+
"title": "Minimal code",
20+
"desc": "A single @Incoming method replaces the MDB class, MessageListener interface, and activation config."
21+
},
22+
{
23+
"icon": "🔌",
24+
"title": "Broker-agnostic",
25+
"desc": "Swap Kafka, AMQP, or JMS connectors via configuration without changing application code."
26+
},
27+
{
28+
"icon": "☁️",
29+
"title": "Cloud-native fit",
30+
"desc": "Reactive streams backpressure and lightweight runtime make it ideal for containerised deployments."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Available since MicroProfile 4.0 / SmallRye Reactive Messaging"
36+
},
37+
"prev": "enterprise/soap-vs-jakarta-rest",
38+
"next": "enterprise/jsf-managed-bean-vs-cdi-named",
39+
"related": [
40+
"enterprise/ejb-vs-cdi",
41+
"concurrency/structured-concurrency",
42+
"concurrency/virtual-threads"
43+
],
44+
"docs": [
45+
{
46+
"title": "MicroProfile Reactive Messaging Specification",
47+
"href": "https://download.eclipse.org/microprofile/microprofile-reactive-messaging-3.0/microprofile-reactive-messaging-spec-3.0.html"
48+
},
49+
{
50+
"title": "SmallRye Reactive Messaging Documentation",
51+
"href": "https://smallrye.io/smallrye-reactive-messaging/"
52+
}
53+
]
54+
}

0 commit comments

Comments
 (0)