☕
Core Java Roadmap Phase 15
0 / 40 (0%)
☰ Syllabus Index Next: P16 →

Enterprise Persistence, JDBC & Database Architecture

📂 Repository Module: Module 6_ Database and ORM \ 01_JDBC_Demos
📖 Textbook Alignment: Volume II — Chapter 5: Database Programming (JDBC, DataSources, Transactions, and Metadata)
Phase 15

Core APIs, Reflection, JPMS & JDBC Persistence

📂 Module 6_ Database and ORM \\ 01_JDBC_Demos
📖 Core Java Textbook Alignment: Volume II — Chapter 5: Database Programming (JDBC) • Chapter 8: Annotations • Volume I — Chapter 4: Reflection

14.1 Access Modifiers, Wrapper IntegerCache & BigDecimal Financial Precision

📖 Cay S. Horstmann • Core Java Volume II — Chapter 5, Section 5.1 & 5.3 ("Database Programming: JDBC")
“Always use PreparedStatement instead of raw Statement whenever queries contain user parameters. PreparedStatement treats inputs strictly as literal data, completely neutralizing SQL injection attacks.”
  • ACID TransactionsCall connection.setAutoCommit(false), commit() on success, and rollback() in catch.
⚡ Pro-Tip: PreparedStatement Security
Never concatenate strings in SQL! PreparedStatement precompiles the query template, rendering injection mathematically impossible.
💡 Real-World Analogy: The IntegerCache (-128 to 127 Trap)
Why does Integer a = 100, b = 100; (a == b) return true, but Integer x = 200, y = 200; (x == y) return false?
• Java pre-allocates an array of small Integer objects from -128 to 127 in memory during JVM bootup (like keeping coins in a cash register).
• For 100, Java hands both a and b the exact same cached object reference!
• For 200, it's outside the cache, so Java does new Integer(200) twice on the Heap. Two different objects → two different addresses → == returns false!
Senior Rule: Always compare wrapper objects using .equals(), NEVER ==!
📂 BigDecimalFinancialDemo.java — Solving the Double Precision Trap
import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalFinancialDemo {
    public static void main(String[] args) {
        // THE TRAP: Binary floating-point IEEE 754 precision error
        double doublePrice = 0.1 + 0.2;
        System.out.println("Double calculation: " + doublePrice); // Prints 0.30000000000000004! Unacceptable in banking!

        // THE SOLUTION: BigDecimal instantiated with STRING (Never pass raw double to BigDecimal constructor!)
        BigDecimal price1 = new BigDecimal("0.10");
        BigDecimal price2 = new BigDecimal("0.20");

        BigDecimal sum = price1.add(price2);
        System.out.println("BigDecimal exact sum: " + sum); // Prints exact 0.30

        // Financial Division with explicit scale and rounding mode
        BigDecimal total = new BigDecimal("100.00");
        BigDecimal splits = new BigDecimal("3");
        BigDecimal perPerson = total.divide(splits, 2, RoundingMode.HALF_EVEN); // Banker's rounding
        System.out.println("Per person: $" + perPerson); // $33.33
    }
}
👁 BigDecimal Financial Arithmetic & The String Constructor Rule
  • The IEEE 754 Floating-Point Trap Numbers like 0.1 cannot be represented precisely in binary base-2 floating-point arithmetic (just as 1/3 cannot be represented finite in base-10). Over millions of transactions, rounding errors cost millions of dollars.
  • Always Use String Constructor Line 11: new BigDecimal("0.10") — Passing a string guarantees exact precision. If you mistakenly write new BigDecimal(0.10) with a double literal, you bake in the double rounding inaccuracy immediately!
  • Scale and RoundingMode Line 20: When dividing, if the result has non-terminating decimals (e.g. 100 / 3), calling divide(splits) without scale throws an immediate ArithmeticException. Always specify explicit decimal scale (2 digits) and a RoundingMode (e.g. HALF_EVEN — Banker's rounding).

14.2 JDBC Architecture, PreparedStatement & SQL Injection Immunity

💡 Real-World Analogy: Why PreparedStatement is Immune to SQL Injection
• Regular Statement (Vulnerable): Like a boss telling an assistant: "Execute whatever is written on this paper." If an attacker sneaks in the text ' OR 1=1; DROP TABLE users; --, the database interprets those words as actual SQL commands!
• PreparedStatement (Safe): Like an official government passport application with strict blank boxes: [First Name] [Last Name]. The SQL command template is compiled and locked by the database engine FIRST. Whatever the user enters in the box is treated strictly as harmless literal text data, never as executable SQL code!
🖊 Architectural Flow: PreparedStatement SQL Injection Prevention Mermaid Diagram
graph LR SQLQuery["SQL Template with Placeholders:
SELECT * FROM users WHERE user = ? AND pass = ?"] DBEngine["Database Optimizer Engine
Compiles Query Tree & Execution Plan ONCE"] Params["User Input Parameter:
' OR '1'='1
Bound strictly as literal string DATA!"] Exec["Safe Execution:
Malicious input cannot alter query structure!"] SQLQuery --> DBEngine Params --> Exec DBEngine --> Exec
📂 Module 6.../EmployeeDAOImpl.java — Production JDBC PreparedStatement
package com.accenture.ltt.dao;

import java.sql.*;

public class EmployeeDAOImpl {
    private static final String INSERT_SQL = 
        "INSERT INTO employees (id, name, department, salary) VALUES (?, ?, ?, ?)";

    public void saveEmployee(Connection conn, int id, String name, String dept, double salary) throws SQLException {
        // Try-with-resources closes PreparedStatement automatically
        try (PreparedStatement pstmt = conn.prepareStatement(INSERT_SQL)) {
            pstmt.setInt(1, id);
            pstmt.setString(2, name);
            pstmt.setString(3, dept);
            pstmt.setDouble(4, salary);
            pstmt.executeUpdate();
        }
    }
}
👁 Production JDBC Architecture & PreparedStatement Mechanics
  • Pre-Compiled Execution Plan Line 10: conn.prepareStatement(INSERT_SQL) sends the SQL skeleton to the database engine. The DB compiles the query and caches its execution plan. Repeated queries simply reuse the cached plan with different parameters, improving throughput.
  • 1-Indexed Parameter Binding Lines 11-14: Notice JDBC parameters are 1-indexed (pstmt.setInt(1, id)). The driver escapes and typesets all parameters into literal binary tokens, making SQL injection attacks impossible.
  • Automatic Connection Hygiene Using try-with-resources ensures the PreparedStatement and underlying database cursor are closed immediately, preventing database connection pool exhaustion in high-volume microservices.
📂 Module 6.../01_JDBC_Demos/.../DBUtility.java
package com.accenture.ltt.utility;

import java.sql.*;

public class DBUtility {
    private static final String URL = "jdbc:mysql://localhost:3306/jdbcdemos";
    private static final String USER = "root";
    private static final String PASS = "root";
    private static Connection connection = null;

    public static Connection getDBConnection() throws ClassNotFoundException, SQLException {
        // Explicit driver loading (legacy/guaranteed registration)
        Class.forName("com.mysql.cj.jdbc.Driver");
        connection = DriverManager.getConnection(URL, USER, PASS);

        // Inspecting database metadata
        DatabaseMetaData meta = connection.getMetaData();
        System.out.println("Connected to: " + meta.getDatabaseProductName() + " v" + meta.getDatabaseProductVersion());
        return connection;
    }

    public static void closeConnection() throws SQLException {
        if (connection != null && !connection.isClosed()) {
            connection.close();
        }
    }
}
👁 JDBC Connection Management & Metadata Inspection
  • DriverManager.getConnection() Loads JDBC URL, username, and password credentials and opens an active TCP socket to the database server. In enterprise production, raw DriverManager is replaced by high-performance HikariCP connection pooling.
  • DatabaseMetaData Provides diagnostic reflection over the database engine (version, capabilities, table schemas) without issuing SQL queries.

14.3 Java Reflection API & Dynamic Inspection

💡 Real-World Analogy: The X-Ray Machine for Java Objects
Normally, a class is like an opaque black safe: private fields are locked inside, accessible only to the class itself.
• Reflection is the X-Ray Machine: It allows frameworks like Spring Boot, Hibernate, and JUnit to peer inside any class at runtime, inspect its fields and methods, read its annotations (@Autowired, @Test), and even invoke private methods dynamically!
📂 ReflectionApiDemo.java — Inspecting Private Fields & Dynamic Method Invocation
import java.lang.reflect.*;

public class ReflectionApiDemo {
    static class Account {
        private double balance = 5000.0;

        private void secretAudit(String auditor) {
            System.out.println("Audit executed by: " + auditor + " | Balance: $" + balance);
        }
    }

    public static void main(String[] args) throws Exception {
        Account acc = new Account();
        Class<?> clazz = acc.getClass();

        // 1. Inspect and read private field at runtime
        Field balanceField = clazz.getDeclaredField("balance");
        balanceField.setAccessible(true); // Bypasses Java language access control checks!
        System.out.println("Reflected private field value: $" + balanceField.get(acc));

        // 2. Mutate private field directly
        balanceField.set(acc, 9999.0);

        // 3. Invoke private method dynamically
        Method auditMethod = clazz.getDeclaredMethod("secretAudit", String.class);
        auditMethod.setAccessible(true);
        auditMethod.invoke(acc, "ExternalComplianceOfficer");
    }
}
👁 Reflection API Breakdown & Framework Mechanics
  • Class<?> Metadata Mirror Line 14: acc.getClass() retrieves the java.lang.Class instance loaded in the JVM Metaspace. Contains full architectural blueprints of constructors, fields, methods, and annotations.
  • setAccessible(true) Lines 18, 26: Suppresses Java language access control checks. This is the exact mechanism Spring Dependency Injection uses to inject beans into @Autowired private MyService service; without needing public setter methods!
  • Performance Tradeoff Reflective lookups bypass JVM JIT compiler inlining and escape analysis optimizations. Frameworks typically inspect and cache reflection metadata during bootup to avoid runtime penalties.