Java Backend Engineering
Connect Java applications to HTTP services and databases, understand JDBC, transactions, connection pools, REST API design, and backend resilience.
HTTP Client and REST Fundamentals
Modern Java includes an HTTP client API for synchronous and asynchronous HTTP calls.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/users"))
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
A production HTTP client needs timeouts, connection reuse, retries where safe, observability, response validation, and protection against unbounded concurrency.
- HTTP clients need timeouts and resilience policies.
- Retries are only safe when operation and server semantics allow them.
- API contracts should make validation and error behavior explicit.
JDBC and Connection Pools
JDBC provides a standard API for relational database access. A connection pool reuses database connections rather than creating a new connection for every request.
try (Connection connection = dataSource.getConnection();
PreparedStatement statement =
connection.prepareStatement(
"select id, name from users where id = ?")) {
statement.setString(1, userId);
try (ResultSet rs = statement.executeQuery()) {
if (rs.next()) {
return rs.getString("name");
}
}
}
Always use parameterized queries. Pool size should account for database capacity and query latency; making it huge can overload the database.
Never concatenate untrusted input into SQL. Use prepared statements or a safe data-access abstraction.
- Use prepared statements for values.
- Connection pools are shared scarce resources.
- Database pool sizing must account for database capacity.
Transactions and Isolation
A transaction groups database operations into an atomic unit. Isolation controls how concurrent transactions observe each other's changes.
connection.setAutoCommit(false);
try {
debitAccount(connection, fromId, amount);
creditAccount(connection, toId, amount);
connection.commit();
} catch (Exception e) {
connection.rollback();
throw e;
}
Transaction boundaries should usually live at a service/use-case layer rather than being scattered across repository methods.
- Transactions define atomicity boundaries.
- Isolation is a concurrency trade-off.
- Keep transaction boundaries aligned with business operations.
Timeouts, Retries, Bulkheads, and Backpressure
A service is only as reliable as its dependencies allow. A slow database or API can consume all request resources if there are no timeouts or concurrency limits.
Resilience techniques include deadlines, bounded retries with jitter, circuit breakers, bulkheads, rate limits, queues, and backpressure.
Request
|
v
Timeout --> Retry policy --> Downstream
| |
| +--> bounded attempts
v
Fallback / error
Observability should include latency, error rate, saturation, dependency timings, and retry counts.
- Timeouts are mandatory for remote dependencies.
- Retries can amplify outages if uncontrolled.
- Backpressure and concurrency limits protect downstream systems.