Java Notes
Deep dive into JDBC architecture — two-tier and three-tier models, driver manager, connection pooling, DataSource, and internal execution flow.
Architecture Overview
JDBC follows a layered architecture that separates the application from the database-specific implementation:
| MySQL | PostgreSQL | Oracle | SQL Server |
|---|---|---|---|
| Driver | Driver | Driver | Driver |
| MySQL DB | PostgreSQL | Oracle DB | SQL Server |
Three-Tier Architecture (Web Applications)
| Client | ────▶ | Application | ────▶ | Database |
|---|---|---|---|---|
| (Browser) | Server (Tomcat/ | Server | ||
| ◀──── | Spring Boot) | ◀──── |
- Client communicates with app server via HTTP/REST
- App server manages database connections (pooling)
- Database not exposed to internet
- Scalable, secure, standard for production
DriverManager vs DataSource
DriverManager (Simple, Non-Pooled)
import java.sql.DriverManager;
import java.sql.Connection;
public class DriverManagerDemo {
public static Connection getConnection() throws SQLException {
// Creates a NEW connection every time (expensive!)
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC",
"root",
"password"
);
}
// With properties
public static Connection getConnectionWithProps() throws SQLException {
Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "password");
props.setProperty("useSSL", "false");
props.setProperty("serverTimezone", "UTC");
props.setProperty("connectTimeout", "5000");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", props);
}
}DataSource (Production, Connection Pooling)
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class DataSourceDemo {
private static DataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
// Pool configuration
config.setMaximumPoolSize(10); // Max connections in pool
config.setMinimumIdle(5); // Min idle connections
config.setIdleTimeout(300000); // 5 minutes idle timeout
config.setConnectionTimeout(20000); // 20 seconds to get connection
config.setMaxLifetime(1200000); // 20 minutes max connection life
// Performance settings
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
// Returns a connection FROM THE POOL (fast!)
return dataSource.getConnection();
}
}Why Connection Pooling?
Without pooling
Request 1: Create connection (500ms) → Execute (10ms) → Close → Destroy
Request 2: Create connection (500ms) → Execute (10ms) → Close → Destroy
Total: 1020ms
With pooling
Startup: Create 5 connections (500ms each, done once)
Request 1: Get from pool (1ms) → Execute (10ms) → Return to pool
Request 2: Get from pool (1ms) → Execute (10ms) → Return to pool
Total: 22ms
Internal Execution Flow
When you execute a query, here's what happens internally:
// This single line triggers many internal steps:
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE id = 1");Step-by-Step Internal Flow:
Connection Lifecycle
public class ConnectionLifecycleDemo {
public static void main(String[] args) {
Connection conn = null;
try {
// 1. OPENING: TCP handshake + authentication
conn = DriverManager.getConnection(url, user, pass);
// Connection is now in AUTO_COMMIT mode
// 2. USING: Execute queries
conn.setAutoCommit(false); // Start transaction
Statement stmt = conn.createStatement();
stmt.executeUpdate("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
stmt.executeUpdate("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
conn.commit(); // 3. COMMIT: Make changes permanent
} catch (SQLException e) {
if (conn != null) {
try {
conn.rollback(); // 4. ROLLBACK: Undo on error
} catch (SQLException ex) {
ex.printStackTrace();
}
}
} finally {
if (conn != null) {
try {
conn.close(); // 5. CLOSING: Release resources, TCP teardown
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}Connection Properties and Optimization
public class OptimizedConnection {
public static Connection getOptimizedConnection() throws SQLException {
Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "password");
// Performance tuning
props.setProperty("useSSL", "false"); // Disable in dev
props.setProperty("serverTimezone", "UTC");
props.setProperty("rewriteBatchedStatements", "true"); // Batch optimization
props.setProperty("cachePrepStmts", "true"); // Cache prepared statements
props.setProperty("prepStmtCacheSize", "250");
props.setProperty("useServerPrepStmts", "true"); // Server-side prep
props.setProperty("connectTimeout", "5000"); // 5s connect timeout
props.setProperty("socketTimeout", "30000"); // 30s socket timeout
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", props);
}
}Complete Connection Utility Class
import java.sql.*;
import java.util.Properties;
public class DBUtil {
private static final String URL = "jdbc:mysql://localhost:3306/student_db";
private static final String USER = "root";
private static final String PASSWORD = "password";
private DBUtil() {} // Prevent instantiation
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException("MySQL driver not found", e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void close(AutoCloseable... resources) {
for (AutoCloseable resource : resources) {
if (resource != null) {
try {
resource.close();
} catch (Exception e) {
System.err.println("Error closing resource: " + e.getMessage());
}
}
}
}
public static void rollback(Connection conn) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
System.err.println("Rollback failed: " + e.getMessage());
}
}
}
}Interview Questions
Q1: Explain the two-tier and three-tier JDBC architecture.
Answer: Two-tier: Java app connects directly to database (client-server). Three-tier: client talks to app server via HTTP, app server talks to database via JDBC. Three-tier is standard for web apps — provides connection pooling, security, and scalability.
Q2: What is the difference between DriverManager and DataSource?
Answer: DriverManager creates new connections each time (slow, no pooling). DataSource supports connection pooling, distributed transactions, and is configured by the application server. Always use DataSource in production.
Q3: What is connection pooling and why is it important?
Answer: Connection pooling maintains a cache of database connections that are reused across requests. Creating a connection is expensive (TCP handshake, authentication). Pooling reduces this to ~1ms from ~500ms, dramatically improving throughput.
Q4: What happens internally when you call DriverManager.getConnection()?
Answer: DriverManager iterates through registered drivers, each tries to connect using the URL. The matching driver creates a TCP socket to the database, performs protocol handshake and authentication, and returns a Connection object.
Q5: Name popular connection pool libraries.
Answer: HikariCP (fastest, default in Spring Boot), Apache DBCP2, C3P0, and Tomcat JDBC Pool. HikariCP is the industry standard due to its speed and reliability.
Q6: What is the role of META-INF/services/java.sql.Driver?
Answer: Since JDBC 4.0, drivers are auto-loaded via Java ServiceLoader. The driver JAR contains this file listing the driver class name, eliminating the need for Class.forName().
Summary
In this chapter, we learned about JDBC Architecture in detail. Here are the key points:
- Basic introduction to the concept and why it is needed
- Syntax and structure with complete examples
- Internal working and best practices
- Real-world applications and use cases
- Common mistakes and how to avoid them
- How this topic is asked in interviews
To practice these concepts, write and run the code in your IDE and verify the output. Modify each example and experiment so that you deeply understand the concept.
Key Takeaways for Jdbc Architecture
- This topic is fundamental in Java development and is frequently asked in interviews
- Make sure to do hands-on practice for practical implementation
- Understanding its use in real-world projects is important for writing production-ready code
- Keep referring to documentation and official Java docs for the latest updates
- Also develop testing and debugging skills alongside this topic
- Follow industry best practices and actively participate in code reviews
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for JDBC Architecture.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, jdbc, architecture, jdbc architecture
Related Java Master Course Topics