Phase 15
Core APIs, Reflection, JPMS & JDBC Persistence
📂 Module 6_ Database and ORM \\ 01_JDBC_Demos
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
• 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
• For 200, it's outside the cache, so Java does
Senior Rule: Always compare wrapper objects using
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 writenew 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 immediateArithmeticException. Always specify explicit decimal scale (2 digits) and aRoundingMode(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
• 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!
' 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
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
PreparedStatementand 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 (
• 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 thejava.lang.Classinstance 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.