0% found this document useful (0 votes)
4 views

Advanced-Java-Notes

Java notes

Uploaded by

ayushbagde780
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Advanced-Java-Notes

Java notes

Uploaded by

ayushbagde780
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 53

Advanced Java Notes: JDBC

1. JDBC (Java Database Connectivity)

1.1 Introduction

●​ What is JDBC?
○​ JDBC stands for Java Database Connectivity.
○​ It is a standard Java API (Application Programming Interface) specified by
Sun Microsystems (now Oracle) for connecting Java applications to
various types of databases.
○​ It provides methods for querying and updating data in a database,
regardless of the specific database management system (DBMS) being
used.
●​ Purpose:
○​ To enable Java applications to interact with relational databases (like
MySQL, Oracle, PostgreSQL, SQL Server, etc.).
○​ To provide a database-independent connectivity layer. This means you
can write your Java code once, and it can connect to different databases
with minimal changes (often just changing the driver and connection
string).
●​ Key Components:
○​ JDBC API: A set of interfaces and classes within the java.sql and
javax.sql packages. Examples include Connection, Statement,
PreparedStatement, ResultSet, DriverManager.
○​ JDBC Driver: A specific implementation of the JDBC API that acts as a
bridge between the Java application and a particular database system.
Each database vendor typically provides its own JDBC driver.

1.2 JDBC Architecture

The JDBC architecture consists primarily of two layers:

1.​ JDBC API: This provides the interface for the application programmer. Your Java
code interacts directly with these classes and interfaces (Connection, Statement,
etc.).
2.​ JDBC Driver Manager & Drivers: This layer connects the JDBC API to the
actual database-specific drivers.
○​ DriverManager: A class in the JDBC API responsible for managing a list
of registered database drivers. When an application requests a
connection, the DriverManager attempts to locate a suitable driver.
○​ JDBC Drivers: These are database-specific implementations that handle
the actual communication with the database server.

There are four main types of JDBC drivers:

●​ Type 1: JDBC-ODBC Bridge Driver:


○​ Translates JDBC calls into ODBC (Open Database Connectivity) calls.
○​ Requires ODBC drivers to be installed on the client machine.
○​ Pros: Allows access to almost any database for which an ODBC driver
exists.
○​ Cons: Platform-dependent (requires ODBC), performance overhead due
to the bridge, considered obsolete and removed from recent Java
versions. Not recommended for new development.

●​ Type 2: Native-API Partly-Java Driver:


○​ Converts JDBC calls into native API calls specific to the database vendor
(e.g., Oracle Call Interface - OCI).
○​ Requires vendor-specific native libraries to be installed on the client
machine.
○​ Pros: Generally better performance than Type 1.
○​ Cons: Platform-dependent due to native code, requires client-side library
installation.
●​ Type 3: Network-Protocol All-Java Driver (Middleware Driver):
○​ Uses a middleware server that translates JDBC calls into a
database-independent network protocol. This middleware then
communicates with the specific database.
○​ The client-side driver is written purely in Java.
○​ Pros: Flexible, no client-side native code, can provide additional features
like caching, load balancing.
○​ Cons: Requires a separate middleware server, adds another layer of
complexity and potential point of failure.
●​ Type 4: Native-Protocol All-Java Driver (Thin Driver):
○​ Converts JDBC calls directly into the network protocol used by the
database vendor.
○​ Written entirely in Java. Does not require any native code or middleware.
○​ Pros: Platform-independent (pure Java), excellent performance, no
client-side software installation (other than the driver JAR), simple
deployment.
○​ Cons: Driver is database-specific.
○​ This is the most commonly used type of driver today.

1.3 JDBC Process

Connecting to a database and executing SQL commands using JDBC typically involves
the following steps:

1.​ Load and Register the Driver:


○​ This step tells the DriverManager about the specific JDBC driver you want
to use.
○​ Typically done using Class.forName("com.database.jdbc.Driver");
○​ Example (MySQL): Class.forName("com.mysql.cj.jdbc.Driver");
○​ Example (Oracle): Class.forName("oracle.jdbc.driver.OracleDriver");
○​ Note: In modern JDBC (4.0 and later), drivers can be automatically
discovered if they are in the classpath (using the Service Provider
Interface mechanism), making explicit Class.forName() often unnecessary,
but it's good practice to understand it.
2.​ Establish the Connection:
○​ Use the DriverManager.getConnection() method to create a Connection
object.
○​ This requires a JDBC URL (specifies the database location, name, etc.),
username, and password.
○​ String url = "jdbc:mysql://localhost:3306/mydatabase";
○​ String user = "root";
○​ String password = "password";
○​ Connection con = DriverManager.getConnection(url, user, password);
○​ The Connection object represents the active session with the database.
3.​ Create a Statement Object:
○​ The Connection object is used to create a Statement object, which is used
to execute SQL queries.
○​ Statement stmt = con.createStatement();
○​ Alternatively, use PreparedStatement for precompiled queries, especially
those with parameters. This is generally preferred for security (prevents
SQL injection) and performance.
○​ PreparedStatement pstmt = con.prepareStatement("SELECT * FROM
users WHERE id = ?");
○​ pstmt.setInt(1, userId); // Set the parameter
4.​ Execute the SQL Query:
○​ Use methods of the Statement or PreparedStatement object.
○​ executeQuery(String sql): Used for SQL SELECT statements. Returns a
ResultSet object.
■​ ResultSet rs = stmt.executeQuery("SELECT name, age FROM
students");
■​ ResultSet rs = pstmt.executeQuery(); // For PreparedStatement
○​ executeUpdate(String sql): Used for SQL DML (INSERT, UPDATE,
DELETE) or DDL (CREATE TABLE, DROP TABLE) statements. Returns
an int representing the number of rows affected (for DML) or 0 (for DDL).
■​ int rowsAffected = stmt.executeUpdate("INSERT INTO products
VALUES (...)");
■​ int rowsAffected = pstmt.executeUpdate(); // For
PreparedStatement
5.​ Process the Results (if applicable):
○​ If executeQuery() was used, process the data contained in the returned
ResultSet object (see Section 1.4).
6.​ Close the Resources:
○​ It is crucial to release database resources (Connection, Statement,
ResultSet) as soon as they are no longer needed to avoid resource leaks.
○​ Close them in the reverse order of their creation.
○​ Best practice is to use a finally block (or try-with-resources statement in
Java 7+) to ensure closure even if exceptions occur.
○​ finally {
○​ try { if (rs != null) rs.close(); } catch (SQLException e) { /* handle */ }
○​ try { if (stmt != null) stmt.close(); } catch (SQLException e) { /* handle */ }
○​ try { if (con != null) con.close(); } catch (SQLException e) { /* handle */ }
○​ }

1.4 Working with ResultSet Interface

●​ Purpose: A ResultSet object holds the data retrieved from a database after
executing a query using Statement.executeQuery() or
PreparedStatement.executeQuery().
●​ Cursor: Conceptually, a ResultSet maintains a cursor pointing to its current row
of data. Initially, the cursor is positioned before the first row.
●​ Key Methods:
○​ boolean next():
■​ Moves the cursor one row forward from its current position.
■​ Returns true if the new current row is valid (i.e., there is another
row).
■​ Returns false if there are no more rows.
■​ This method must be called first to move to the first row. It's
typically used in a while loop to iterate through all rows.
○​ getXXX(int columnIndex):
■​ Retrieves the value of the designated column in the current row as
the specified Java type (XXX).
■​ Column indices start from 1.
■​ Examples: getInt(1), getString(2), getDouble(3), getDate(4).
○​ getXXX(String columnLabel):
■​ Retrieves the value of the designated column in the current row as
the specified Java type (XXX).
■​ Uses the column name (or alias specified in the SQL query) instead
of the index. Case-insensitivity depends on the driver/database.
■​ Examples: getInt("student_id"), getString("student_name"),
getDouble("average_mark"). Using column labels is generally more
readable and less prone to errors if the query changes.
○​ close():
■​ Immediately releases the ResultSet object's database and JDBC
resources. It's automatically closed when the Statement that
generated it is closed or re-executed, but explicit closure in a finally
block is recommended.
●​ Example Usage:

Statement stmt = null;


ResultSet rs = null;
try {
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT id, name, email FROM users WHERE city =
'Mumbai'");

// Iterate through the results


while (rs.next()) {
int id = rs.getInt("id"); // Using column label
String name = rs.getString("name");
String email = rs.getString(3); // Using column index (email is the 3rd column)

System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email);
}
} catch (SQLException e) {
e.printStackTrace(); // Handle exceptions appropriately
} finally {
// Close resources in reverse order
try { if (rs != null) rs.close(); } catch (SQLException e) { /* ignore */ }
try { if (stmt != null) stmt.close(); } catch (SQLException e) { /* ignore */ }
// Connection 'con' should be closed elsewhere, typically outside this specific query
block
}

●​ ResultSet Types (Brief Mention):


○​ By default, ResultSet objects are forward-only (can only iterate forward
using next()) and read-only.
○​ JDBC allows creating scrollable (TYPE_SCROLL_INSENSITIVE,
TYPE_SCROLL_SENSITIVE) and updatable (CONCUR_UPDATABLE)
result sets, but these are less commonly used and require specific driver
support.

2. Multithreading

2.1 Introduction to Multithreading

●​ What is a Thread?
○​ A thread is the smallest unit of execution within a process. It's a
lightweight subprocess.
○​ A process can have multiple threads running concurrently, each
performing a different task.
●​ What is Multithreading?
○​ Multithreading is a programming concept where multiple threads execute
concurrently within a single program (process).
○​ Each thread has its own program counter, stack, and local variables, but
threads within the same process share the same memory space (heap),
including objects and static variables.
●​ Purpose & Benefits:
○​ Improved Responsiveness: Allows applications (especially GUIs) to
remain responsive to user input while performing long-running tasks in the
background.
○​ Increased Resource Utilization: Keeps the CPU busy by executing
another thread when one thread is blocked (e.g., waiting for I/O).
○​ Parallelism: On multi-core processors, multithreading allows different
threads to run simultaneously on different cores, speeding up
computation-intensive tasks.
○​ Simplified Modeling: Some problems are naturally modeled using
multiple concurrent activities.
●​ Process vs. Thread:
○​ Process: An independent program running in its own memory space.
Processes are heavyweight and communication between them
(Inter-Process Communication - IPC) is complex. Context switching
between processes is expensive.
○​ Thread: Runs within the context of a process, sharing its memory space.
Threads are lightweight. Communication between threads is easier
(shared objects). Context switching between threads is generally faster
than between processes.

2.2 Thread Creation

There are two primary ways to create threads in Java:

1. Extending the Thread Class:

●​ Steps:
1.​ Create a new class that extends java.lang.Thread.
2.​ Override the run() method in your class. The code inside run() is what the
thread will execute.
3.​ Create an instance (object) of your custom thread class.
4.​ Call the start() method on the object. This allocates system resources,
schedules the thread to run, and invokes the run() method (indirectly).
Never call run() directly.
●​ Example:

class MyThread extends Thread {


private String threadName;

MyThread(String name) {
threadName = name;
System.out.println("Creating " + threadName );
}

@Override
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50); // Sleep for 50 milliseconds
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
}

// To run this thread:


// MyThread t1 = new MyThread("Thread-1");
// t1.start();
// MyThread t2 = new MyThread("Thread-2");
// t2.start();

IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END

2. Implementing the Runnable Interface:

●​ Steps:
1.​ Create a new class that implements the java.lang.Runnable interface.
2.​ Implement the run() method in your class. This contains the code the
thread will execute.
3.​ Create an instance (object) of your custom Runnable class.
4.​ Create an instance of the Thread class, passing your Runnable object to
the Thread constructor.
5.​ Call the start() method on the Thread object.
●​ Example:

class MyRunnable implements Runnable {


private String taskName;
MyRunnable(String name) {
taskName = name;
System.out.println("Creating task " + taskName );
}

@Override
public void run() {
System.out.println("Executing task " + taskName );
try {
for(int i = 0; i < 3; i++) {
System.out.println("Task: " + taskName + ", Step " + i);
Thread.sleep(100); // Sleep for 100 milliseconds
}
} catch (InterruptedException e) {
System.out.println("Task " + taskName + " interrupted.");
}
System.out.println("Task " + taskName + " complete.");
}
}

// To run this task in a thread:


// MyRunnable task1 = new MyRunnable("Data Processing");
// Thread thread1 = new Thread(task1);
// thread1.start();
//
// MyRunnable task2 = new MyRunnable("File Download");
// Thread thread2 = new Thread(task2);
// thread2.start();

IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END

●​ Which method to prefer?


○​ Implementing Runnable is generally preferred because:
■​ Java does not support multiple inheritance of classes. If your class
needs to extend another class (e.g., JFrame), it can still implement
Runnable.
■​ It promotes better object-oriented design by separating the task
(Runnable) from the execution mechanism (Thread).
2.3 Life Cycle of a Thread

A thread goes through various states during its lifetime:

1.​ New:
○​ The thread object has been created (e.g., new Thread(...)) but the start()
method has not yet been called.
○​ The thread is not yet alive.
2.​ Runnable:
○​ The thread enters this state after start() is called.
○​ It is considered "alive" and is eligible to be run by the thread scheduler.
○​ It might be actually running or waiting for its turn on the CPU. The OS
thread scheduler decides which runnable thread gets the CPU time.
3.​ Running:
○​ The thread scheduler has selected the thread, and it is currently executing
its run() method on the CPU.
○​ A thread transitions from Runnable to Running when it gets CPU time.
○​ It can transition back to Runnable if its time slice expires or if it yields
control (Thread.yield()).
4.​ Blocked / Waiting / Timed Waiting:
○​ The thread is alive but currently not eligible to run for some reason. It is
waiting for some event to occur. It enters this state due to:
■​ Blocked: Waiting to acquire a monitor lock (entering a
synchronized block/method already held by another thread).
■​ Waiting: Waiting indefinitely for another thread to perform a
particular action, typically via object.wait(), thread.join().
■​ Timed Waiting: Waiting for a specified amount of time, via
Thread.sleep(millis), object.wait(millis), thread.join(millis).
○​ A thread returns to the Runnable state when the condition it was waiting
for is met (e.g., lock acquired, notify()/notifyAll() called, sleep duration
expires, joined thread terminates).
5.​ Terminated (Dead):
○​ The thread has completed its execution because:
■​ Its run() method has finished normally.
■​ An uncaught exception terminated the run() method.
○​ Once a thread is terminated, it cannot be restarted. Calling start() again
will throw an IllegalThreadStateException.

2.4 Thread Priority


●​ Concept: Java assigns each thread a priority, which acts as a suggestion to the
thread scheduler about which thread should be favored for execution.
●​ Priority Range: Priorities are integers ranging from Thread.MIN_PRIORITY (1)
to Thread.MAX_PRIORITY (10). The default priority is Thread.NORM_PRIORITY
(5).
●​ Scheduler Behavior: Threads with higher priority are generally expected to get
more CPU time than threads with lower priority. However, the exact behavior is
highly dependent on the underlying Operating System and JVM implementation.
Priorities do not guarantee execution order.
●​ Methods:
○​ setPriority(int newPriority): Sets the thread's priority. Throws
IllegalArgumentException if the priority is outside the valid range.
○​ getPriority(): Returns the thread's current priority.
●​ Use Cases: Can be used to give preference to critical threads, but rely on it
cautiously. Over-reliance on priorities can lead to "thread starvation" where
lower-priority threads never get a chance to run. Synchronization is a more
reliable way to manage critical execution order.

2.5 Execution of Thread Application

●​ A Java application starts with a single main thread (the one executing the main()
method).
●​ New threads are created and started using the mechanisms described in section
2.2 (Thread class or Runnable interface and start() method).
●​ Once start() is called, the new thread enters the Runnable state, and its run()
method will eventually be executed concurrently with the other threads in the
application.
●​ The Java Virtual Machine (JVM) continues to run as long as at least one
non-daemon thread is alive.
1.​ Daemon Threads: These are low-priority threads that run in the
background (e.g., garbage collector). The JVM will exit even if daemon
threads are still running, once all non-daemon threads have terminated.
You can mark a thread as a daemon using setDaemon(true) before
starting it.
●​ Example Flow:
1.​ JVM starts, executes main() in the main thread.
2.​ main creates and starts Thread-A.
3.​ main creates and starts Thread-B.
4.​ Now, the main thread, Thread-A, and Thread-B are potentially running
concurrently (interleaved execution or parallel on multi-core).
5.​ The main method might finish, but the JVM will not exit until both Thread-A
and Thread-B (assuming they are non-daemon) also finish their run()
methods.

2.6 Synchronization and Inter-thread Communication

Synchronization:

●​ Problem: When multiple threads access and modify shared mutable data
(variables, objects) concurrently, it can lead to race conditions and data
inconsistency.
○​ Race Condition: Outcome depends on the unpredictable timing or
interleaving of thread execution.
●​ Solution: Synchronization controls access to shared resources, ensuring that
only one thread can execute a critical section of code at a time.
●​ Mechanism: Java uses monitors (also known as locks). Every Java object has
an associated monitor.
●​ synchronized Keyword:

Synchronized Methods: When a method is declared synchronized, a thread must


acquire the lock (monitor) of the object (this for instance methods, the .class object for
static methods) before executing the method. The lock is released when the method
finishes (normally or via exception).​
public synchronized void incrementCounter() {
// Critical section: only one thread at a time can execute this
count++;
}

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Synchronized Blocks: Allows synchronization on any object's monitor for only a part of
a method, providing finer-grained control.​
public void updateData() {
// Non-critical code here...
synchronized(sharedResourceObject) {
// Critical section: access/modify sharedResourceObject
}
// More non-critical code...
}

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Inter-thread Communication:

●​ Need: Sometimes threads need to coordinate their actions. For example, a


producer thread adding items to a buffer needs to signal a consumer thread
when items are available, and the consumer needs to wait if the buffer is empty.
●​ Mechanism: Java provides methods in the Object class for this: wait(), notify(),
and notifyAll().
○​ These methods must be called from within a synchronized block or
method on the object whose lock is held (the object being used for
coordination).
●​ Methods:
○​ wait(): Causes the current thread to release the lock and enter the Waiting
state until another thread invokes notify() or notifyAll() on the same object,
or the waiting thread is interrupted.
○​ wait(long timeout) / wait(long timeout, int nanos): Similar to wait(), but the
thread enters the Timed Waiting state and can also return to Runnable if
the timeout expires.
○​ notify(): Wakes up a single thread that is waiting on this object's monitor. If
multiple threads are waiting, the choice of which thread to wake is
arbitrary (depends on JVM implementation). The awakened thread doesn't
run immediately; it must re-acquire the object's lock first (when the thread
that called notify() releases it).
○​ notifyAll(): Wakes up all threads waiting on this object's monitor. Each
awakened thread will compete to re-acquire the lock.
●​ Classic Example: Producer-Consumer problem using a shared buffer.
○​ Producer waits if the buffer is full (buffer.wait()).
○​ Consumer waits if the buffer is empty (buffer.wait()).
○​ Producer adds an item and calls buffer.notify() (or notifyAll()) to potentially
wake a waiting consumer.
○​ Consumer removes an item and calls buffer.notify() (or notifyAll()) to
potentially wake a waiting producer.
3. Networking

3.1 Overview of Networking

●​ What is Networking? Computer networking refers to connecting two or more


computing devices (computers, servers, mobiles, etc.) together for the purpose of
sharing resources (like files, printers), exchanging information, and enabling
communication.
●​ Basic Models:
○​ Client-Server Model: A central server provides resources or services,
and multiple clients request those services (e.g., web server and web
browsers).
○​ Peer-to-Peer (P2P) Model: Devices (peers) connect directly with each
other without a central server, sharing resources and responsibilities (e.g.,
file-sharing systems like BitTorrent).
●​ Java and Networking: Java provides a rich set of APIs in the java.net package
that allows developers to easily create network-aware applications. Java
networking abstracts away many low-level details, making it relatively
straightforward to implement both client and server applications.

3.2 Networking Basics: Port Number, Protocols and Classes

To understand network communication, especially in Java, key concepts include:

●​ IP Address:
○​ An Internet Protocol (IP) address is a unique numerical label assigned to
each device connected to a computer network that uses the Internet
Protocol for communication.
○​ It serves as the logical address of the device on the network, similar to a
postal address for a house.
○​ Examples: 192.168.1.10 (IPv4),
2001:0db8:85a3:0000:0000:8a2e:0370:7334 (IPv6).
○​ Java class: java.net.InetAddress represents IP addresses.
●​ Port Number:
○​ While an IP address identifies a specific device (host) on the network, a
port number identifies a specific application or service running on that
device.
○​ It's like an apartment number within a building (the building's address
being the IP address).
○​ Ports are represented by 16-bit unsigned integers (0-65535).
○​ Well-Known Ports (0-1023): Reserved for standard services (e.g., 80 for
HTTP, 443 for HTTPS, 21 for FTP, 25 for SMTP). Usually require special
privileges to use.
○​ Registered Ports (1024-49151): Can be registered for specific
applications.
○​ Dynamic/Private Ports (49152-65535): Used for temporary client-side
connections or private services.
●​ Protocols:
○​ A protocol is a set of rules and conventions that govern how data is
transmitted and received over a network. Protocols define message
formats, ordering, error handling, etc.
○​ IP (Internet Protocol): Responsible for addressing hosts and routing data
packets from source to destination across networks. It's a connectionless
protocol (doesn't guarantee delivery).
○​ TCP (Transmission Control Protocol):
■​ Provides reliable, ordered, and error-checked delivery of a stream
of bytes between applications.
■​ It is connection-oriented: A connection must be established
between client and server before data transfer begins (like a phone
call).
■​ Used by HTTP, HTTPS, FTP, SMTP, etc.
■​ Java classes: java.net.Socket (for clients) and
java.net.ServerSocket (for servers).
○​ UDP (User Datagram Protocol):
■​ Provides a simpler, connectionless communication model.
■​ It sends independent packets (datagrams) without establishing a
connection.
■​ Faster than TCP (less overhead) but unreliable: Delivery, order,
and duplication are not guaranteed.
■​ Used for DNS, DHCP, streaming media, online games where speed
is critical and occasional packet loss is acceptable.
■​ Java classes: java.net.DatagramSocket and
java.net.DatagramPacket.
●​ Key java.net Classes:
○​ InetAddress: Represents an IP address (both IPv4 and IPv6). Used to find
the IP address of a host by name or vice versa.
○​ URL: Represents a Uniform Resource Locator, pointing to a resource on
the World Wide Web. Provides methods to open connections and retrieve
content.
○​ URLConnection / HttpURLConnection: Used for interacting with resources
specified by a URL, particularly for HTTP communication.
○​ Socket: Implements one endpoint of a two-way TCP connection (client
side).
○​ ServerSocket: Implements a server endpoint that listens for connection
requests from clients over TCP.
○​ DatagramSocket: Used for sending and receiving UDP datagram packets.
○​ DatagramPacket: Represents a UDP datagram packet, containing the
data, destination/source address, and port.

3.3 Sockets, Reading from and Writing to a Socket

●​ What is a Socket?
○​ In Java networking, a socket represents one endpoint of a two-way
communication link between two programs running on the network.
○​ Most commonly refers to TCP sockets (java.net.Socket and
java.net.ServerSocket).
○​ A connection between a client and server involves a pair of sockets: one
on the client machine and one on the server machine.
○​ A socket is bound to a specific IP address and port number.
●​ TCP Client-Server Interaction using Sockets:
○​ Server:
■​ Creates a ServerSocket object, binding it to a specific port number
on the server's IP address.
■​ Calls the accept() method on the ServerSocket. This method blocks
(waits) until a client attempts to connect on that port.
■​ When a client connects, accept() returns a Socket object
representing the connection to that specific client.
■​ The server then uses this client-specific Socket to communicate
(read/write) with the client, typically in a separate thread to handle
multiple clients concurrently.
○​ Client:
■​ Creates a Socket object, specifying the server's IP address and the
port number the server is listening on.
■​ If the connection is successful, the Socket object is created, and
the client can use it to communicate with the server.
■​ If the server is not listening or refuses the connection, an exception
(e.g., ConnectException) is thrown.
●​ Reading from and Writing to a Socket:
○​ Once a Socket connection is established (either on the client side or
returned by serverSocket.accept() on the server side), you can get its
input and output streams to send and receive data.
○​ InputStream getInputStream(): Returns an InputStream to read data sent
by the other end of the socket.
○​ OutputStream getOutputStream(): Returns an OutputStream to write data
to the other end of the socket.
●​ Using Streams for I/O:
○​ The raw InputStream and OutputStream read/write bytes. This can be
inconvenient for sending/receiving text or structured data.
○​ It's common practice to wrap these basic streams with higher-level stream
classes from the java.io package for easier handling:
■​ For Text Data:
■​ Wrap OutputStream with PrintWriter: PrintWriter out = new
PrintWriter(socket.getOutputStream(), true); (The true
enables auto-flushing on println). Use out.println("Hello
Server!");.
■​ Wrap InputStream with InputStreamReader and then
BufferedReader: BufferedReader in = new
BufferedReader(new
InputStreamReader(socket.getInputStream()));. Use String
line = in.readLine();.
■​ For Binary Data / Java Primitive Types:
■​ Wrap OutputStream with DataOutputStream:
DataOutputStream dos = new
DataOutputStream(socket.getOutputStream());. Use
dos.writeInt(123); dos.writeUTF("Data"); dos.flush();.
■​ Wrap InputStream with DataInputStream: DataInputStream
dis = new DataInputStream(socket.getInputStream());. Use
int num = dis.readInt(); String str = dis.readUTF();.
■​ For Objects (Serialization):
■​ Wrap with ObjectOutputStream and ObjectInputStream
(requires objects to implement Serializable).

Conceptual Code Snippets:​


Server Side (Simplified):​
ServerSocket serverSocket = null;
Socket clientSocket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
serverSocket = new ServerSocket(5000); // Listen on port 5000
System.out.println("Server waiting for connection...");
clientSocket = serverSocket.accept(); // Wait for a client
System.out.println("Client connected: " + clientSocket.getInetAddress());

// Get streams
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

// Read from client and write back


String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("Received from client: " + inputLine);
out.println("Server Echo: " + inputLine); // Send response
if (inputLine.equalsIgnoreCase("bye")) break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// Close resources
try { if (in != null) in.close(); } catch (IOException e) { /* ignore */ }
if (out != null) out.close();
try { if (clientSocket != null) clientSocket.close(); } catch (IOException e) { /* ignore */ }
try { if (serverSocket != null) serverSocket.close(); } catch (IOException e) { /* ignore
*/ }
}

IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END​
Client Side (Simplified):​
Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
BufferedReader stdIn = null; // To read user input from console
try {
socket = new Socket("localhost", 5000); // Connect to server on port 5000
System.out.println("Connected to server.");
// Get streams
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
stdIn = new BufferedReader(new InputStreamReader(System.in));

// Read from console, send to server, print server response


String userInput;
System.out.println("Enter message to send (type 'bye' to quit):");
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput); // Send message to server
if (userInput.equalsIgnoreCase("bye")) break;
System.out.println("Server response: " + in.readLine()); // Read response
}
} catch (UnknownHostException e) {
System.err.println("Don't know about host.");
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection.");
} finally {
// Close resources
try { if (in != null) in.close(); } catch (IOException e) { /* ignore */ }
if (out != null) out.close();
try { if (stdIn != null) stdIn.close(); } catch (IOException e) { /* ignore */ }
try { if (socket != null) socket.close(); } catch (IOException e) { /* ignore */ }
}

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
●​ Important Considerations:
○​ Error Handling: Network operations are prone to IOExceptions. Proper
exception handling is crucial.
○​ Resource Management: Always close sockets and streams in a finally
block or use try-with-resources (Java 7+) to prevent resource leaks.
○​ Concurrency: Servers typically need to handle multiple clients
simultaneously, requiring multithreading (e.g., creating a new thread for
each accepted client connection).
○​ Flushing: When using buffered writers (like PrintWriter without auto-flush
or BufferedWriter), ensure you call flush() to force the data to be sent over
the network. println with auto-flush enabled handles this.
4. Servlet and JSP (JavaServer Pages)

4.1 Introduction to Servlet

●​ What is a Servlet?
○​ A Servlet is a Java class that runs on a web server or application
server and extends the capabilities of the server.
○​ It handles client requests (typically HTTP requests from web browsers)
and generates dynamic responses (often HTML, but can be XML, JSON,
etc.).
○​ Servlets are part of the Java Platform, Enterprise Edition (Java EE)
specification (now Jakarta EE).
●​ Purpose:
○​ To create dynamic web content and web applications using Java.
○​ To process data submitted via HTML forms.
○​ To interact with databases or other backend resources based on client
requests.
○​ To manage application state (e.g., user sessions).
●​ Servlet Container (Web Container):
○​ Servlets do not run standalone; they run inside a Servlet Container (e.g.,
Apache Tomcat, Jetty, GlassFish).
○​ The container is responsible for managing the servlet's lifecycle, handling
requests and responses, managing sessions, security, etc.
○​ When a request comes for a servlet, the container loads the servlet (if not
already loaded), invokes its methods, and sends the response back to the
client.
●​ Advantages over CGI (Common Gateway Interface):
○​ Performance: Servlets are loaded once and stay in memory, handling
multiple requests using threads, whereas CGI scripts typically start a new
process for each request (which is resource-intensive).
○​ Platform Independence: Being Java, servlets are platform-independent.
○​ Robustness & Scalability: Benefit from Java's features like memory
management and security, and the container provides scalability features.
○​ Extensibility: Can leverage the full power of the Java ecosystem (JDBC,
Networking, etc.).

4.2 Types of Servlet: GenericServlet and HttpServlet


Java provides two main ways to create servlets, usually by extending base classes:

1.​ javax.servlet.GenericServlet:
○​ An abstract class that provides a basic, protocol-independent
implementation of the javax.servlet.Servlet interface.
○​ It requires subclasses to implement the service() method, which handles
all types of requests.
○​ You need to manually parse the request to determine the protocol and
specifics.
○​ Rarely used directly for web applications today, as most web
communication uses HTTP.

// Example Structure (Not commonly used for web)

import javax.servlet.*;

import java.io.IOException;

public class MyGenericServlet extends GenericServlet {

@Override

public void service(ServletRequest req, ServletResponse res)

throws ServletException, IOException {

// Handle the request (protocol independent)

res.setContentType("text/plain");

res.getWriter().println("Response from GenericServlet");

2.​
3.​ javax.servlet.http.HttpServlet:
○​ An abstract class that extends GenericServlet and is specifically designed
for handling HTTP requests.
○​ This is the most common way to write servlets for web applications.
○​ It overrides the generic service() method and dispatches requests based
on the HTTP method (GET, POST, PUT, DELETE, etc.) to specific handler
methods:
■​ doGet(HttpServletRequest req, HttpServletResponse res): Handles
HTTP GET requests.
■​ doPost(HttpServletRequest req, HttpServletResponse res):
Handles HTTP POST requests.
■​ doPut(...), doDelete(...), etc.
○​ You typically override doGet() and/or doPost() (and others as needed) in
your servlet class.
○​ Provides specialized request (HttpServletRequest) and response
(HttpServletResponse) objects with methods specific to HTTP (e.g.,
getting headers, parameters, cookies, setting status codes).

// Example Structure (Commonly used)

import javax.servlet.ServletException;

import javax.servlet.http.*;

import java.io.IOException;

import java.io.PrintWriter;

public class MyHttpServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("<html><body>");

out.println("<h1>Response from HttpServlet (GET)</h1>");

out.println("<p>Hello, World!</p>");
out.println("</body></html>");

@Override

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String name = request.getParameter("username"); // Get form data

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("<html><body>");

out.println("<h1>Response from HttpServlet (POST)</h1>");

out.println("<p>Hello, " + (name != null ? name : "Guest") + "!</p>");

out.println("</body></html>");

4.​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

4.3 Life cycle of a Servlet

The servlet container manages the lifecycle of a servlet instance. The key methods
involved are:

1.​ Loading and Instantiation:


○​ The container loads the servlet class (using a class loader) and creates an
instance of the servlet.
○​ This typically happens either when the container starts (if configured for
load-on-startup) or when the first request for the servlet arrives.
○​ Only one instance of a servlet is typically created by the container (by
default, it follows a singleton pattern within the container).
2.​ Initialization (init() method):
○​ After instantiation, the container calls the init(ServletConfig config) method
once for the servlet instance.
○​ This method is used for one-time setup activities, like loading configuration
data, initializing database connections, etc.
○​ The ServletConfig object provides access to initialization parameters
defined in the deployment descriptor (web.xml) or via annotations.
○​ A servlet is not ready to handle requests until its init() method completes
successfully.
3.​ Request Handling (service() method):
○​ Once initialized, the servlet instance is ready to handle client requests.
○​ For each incoming request targeted at the servlet, the container creates
ServletRequest and ServletResponse objects (or their HTTP-specific
subtypes) and invokes the servlet's service() method in a new thread.
○​ The service() method (or doGet/doPost in HttpServlet) performs the actual
request processing and generates the response.
○​ Because the container uses threads, a single servlet instance can handle
multiple requests concurrently. This makes synchronization crucial if the
servlet accesses shared resources or instance variables.
4.​ Destruction (destroy() method):
○​ When the container decides to shut down the servlet (e.g., when the web
application is undeployed or the server is shutting down), it calls the
destroy() method once for the servlet instance.
○​ This method is used to release any resources held by the servlet (e.g.,
close database connections, save state).
○​ After destroy() is called, the servlet instance is marked for garbage
collection. The container will not send any more requests to it.

Summary:

●​ Load & Instantiate -> init() [Once] -> service() [Called per request in a new
thread] -> destroy() [Once]

4.4 Session Tracking

●​ Problem: HTTP is a stateless protocol. Each request from a client to the server
is independent. The server doesn't inherently remember previous interactions
with the same client. Web applications often need to maintain state across
multiple requests from the same user (e.g., login status, shopping cart contents).
●​ Solution: Session tracking mechanisms allow the server to associate multiple
requests with the same user session.
●​ Common Techniques:
○​ HTTP Cookies:
■​ The server sends a small piece of data (a cookie, often containing a
unique session ID) to the client browser with the response
(response.addCookie(...)).
■​ The browser automatically sends this cookie back to the server with
subsequent requests to the same domain (request.getCookies()).
■​ The server uses the session ID from the cookie to retrieve the
user's session data stored on the server side.
■​ Most common technique.
○​ URL Rewriting:
■​ The server appends a unique session ID directly to the URLs in the
links and forms within the HTML pages it sends back
(response.encodeURL(...), response.encodeRedirectURL(...)).
■​ When the user clicks a rewritten link or submits a form, the session
ID is sent back to the server as part of the request URL.
■​ Used as a fallback when cookies are disabled by the client. Less
secure and makes URLs look messy.
○​ Hidden Form Fields:
■​ The server includes a hidden input field (<input type="hidden"
name="sessionId" value="...">) containing the session ID in HTML
forms.
■​ When the form is submitted, the session ID is sent back as a
request parameter.
■​ Only works for requests generated by form submissions.
●​ Java Servlet API Support (HttpSession):
○​ The Servlet API provides high-level support for session management
primarily using cookies (or URL rewriting as a fallback) via the
HttpSession interface.
○​ Getting a Session: HttpSession session = request.getSession(); or
request.getSession(true);
■​ This looks for a session ID cookie (or URL parameter). If found and
valid, it returns the existing HttpSession object associated with that
ID.
■​ If no valid session exists, it creates a new HttpSession object,
generates a unique session ID, sets a cookie for this ID in the
response, and returns the new session object.
■​ request.getSession(false); returns the existing session if one exists,
but returns null instead of creating a new one if not.
○​ Storing Data: session.setAttribute("attributeName", attributeValue);
(Stores any Java object).
○​ Retrieving Data: Object value = session.getAttribute("attributeName");
(Requires casting).
○​ Removing Data: session.removeAttribute("attributeName");
○​ Invalidating Session: session.invalidate(); (Ends the session, discards all
its data).
○​ Session ID: session.getId();
○​ Session Timeout: Sessions expire after a period of inactivity
(configurable in web.xml or programmatically).

4.5 Servlet with Database (JDBC)

Servlets often need to interact with databases to retrieve or store dynamic data. This is
done using the JDBC API (covered in Part 1).

●​ Typical Workflow:
1.​ Get Request: Servlet's doGet() or doPost() method is invoked.
2.​ Get Parameters: Retrieve any necessary data from the
HttpServletRequest (e.g., request.getParameter("userId")).
3.​ Establish DB Connection:
■​ Load the JDBC driver (often done once in init() or using a
connection pool).
■​ Get a Connection object using DriverManager.getConnection(...) or
from a connection pool.
4.​ Create Statement: Create a Statement or (preferably)
PreparedStatement.
5.​ Execute Query: Execute the SQL query (executeQuery for SELECT,
executeUpdate for INSERT/UPDATE/DELETE). Use PreparedStatement
parameters to prevent SQL injection.
6.​ Process ResultSet: If using executeQuery, iterate through the ResultSet
using rs.next() and retrieve data using rs.getXXX(). Store the data (e.g., in
a List of objects).
7.​ Close DB Resources: Close ResultSet, Statement, and Connection in a
finally block (or use try-with-resources). Crucial: Return connections to
the pool if using one.
8.​ Set Attributes (Forwarding): Store the retrieved data as attributes in the
request object (request.setAttribute("userData", userList)).
9.​ Forward/Generate Response:
■​ Forward: Often, the servlet forwards the request (along with the
data attached as attributes) to a JSP page for presentation:
RequestDispatcher dispatcher =
request.getRequestDispatcher("displayUsers.jsp");
dispatcher.forward(request, response);
■​ Generate Directly: Less common for complex HTML, but the
servlet can generate the full HTML response using
response.getWriter().
10.​Handle Exceptions: Wrap database code in try-catch blocks to handle
SQLException.
●​ Connection Pooling: Opening and closing database connections for every
request is inefficient. Connection Pools (like HikariCP, Apache Commons
DBCP) manage a set of pre-established connections. Servlets borrow a
connection from the pool, use it, and return it, significantly improving
performance and resource management. Configuration is usually done at the
server level or within the application framework.

4.6 Introduction to JSP (JavaServer Pages)

●​ What is JSP?
○​ JSP technology provides a simplified way to create dynamic web pages
by allowing developers to embed Java code directly into HTML/XML
pages.
○​ It focuses on the presentation layer of a web application.
●​ Purpose:
○​ To separate the presentation logic (HTML/UI design) from the business
logic (Java code). Designers can work on the HTML structure, while
developers embed the dynamic parts.
○​ To make generating dynamic HTML easier than writing lots of
out.println(...) statements in servlets.
●​ How it Works:
○​ A developer creates a .jsp file (which looks like an HTML file with special
JSP tags).
○​ When a client requests a JSP page for the first time (or after it's modified),
the Servlet Container (JSP Engine) automatically translates the JSP file
into a Java Servlet source file (.java).
○​ The container then compiles this generated servlet source file into a
servlet class file (.class).
○​ The container loads and runs this generated servlet just like any other
servlet (following the servlet lifecycle: init(), _jspService(), destroy()).
_jspService() corresponds to the main body of the JSP.
○​ For subsequent requests to the same JSP, the container reuses the
already compiled servlet instance (unless the JSP file has changed).
●​ Servlet vs. JSP:
○​ Servlet: Java code that generates HTML (or other content). Better suited
for processing requests, business logic, controlling flow (Controller in
MVC).
○​ JSP: HTML code that contains embedded Java code or special tags.
Better suited for generating the response structure, displaying data (View
in MVC).
○​ They work together: Servlets often handle the logic and then forward the
request (with data attached) to a JSP for rendering.

4.7 JSP Life Cycle

The JSP life cycle is managed by the container and mirrors the lifecycle of the servlet it
gets translated into:

1.​ Translation: The container's JSP engine parses the .jsp file and generates the
corresponding .java servlet source code. This happens on the first request or if
the JSP has been updated.
2.​ Compilation: The container compiles the generated .java source file into a .class
file (bytecode).
3.​ Loading: The container loads the compiled servlet class.
4.​ Instantiation: The container creates an instance of the servlet class.
5.​ Initialization (jspInit()): The container calls the jspInit() method (translated from
potentially a JSP declaration tag). This is called only once. Useful for initializing
resources.
6.​ Request Processing (_jspService()): For each client request, the container
invokes the _jspService(HttpServletRequest request, HttpServletResponse
response) method in a new thread. This method contains the code generated
from the main body of the JSP (HTML parts and scriptlets/expressions).
7.​ Destruction (jspDestroy()): When the container shuts down the JSP (e.g.,
undeployment), it calls the jspDestroy() method once. Used for cleanup.

4.8 Components of JSP

JSP pages use special elements (tags) mixed with standard HTML/XML ("template
text").
1.​ Directives: Provide instructions to the JSP container during the translation
phase.
○​ Syntax: <%@ directive attribute="value" ... %>
○​ Common Directives:

page: Defines page-dependent attributes, like import (to import Java classes),
contentType, session (whether the page participates in sessions), errorPage (specifies
a page to handle exceptions).​
<%@ page import="java.util.*, com.example.User" %>

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ page session="true" %>

■​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Jsp​
IGNORE_WHEN_COPYING_END

include: Includes the content of another file (JSP, HTML, etc.) during the translation
phase. The content becomes part of the generated servlet.​
<%@ include file="header.html" %>

■​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Jsp​
IGNORE_WHEN_COPYING_END

taglib: Declares a custom tag library (like JSTL) used in the page.​
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

■​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Jsp​
IGNORE_WHEN_COPYING_END
2.​ Scripting Elements: Allow embedding Java code directly. (Note: Excessive
use of scripting elements is discouraged in modern JSP development in
favor of Expression Language (EL) and JSTL).
○​ Declarations: Declare variables or methods at the class level in the
generated servlet.
■​ Syntax: <%! declaration; [ declaration; ] ... %>
■​ Example: <%! int counter = 0; %> <%! public void
myHelperMethod() { ... } %>
○​ Scriptlets: Contain arbitrary Java code executed within the _jspService()
method during request processing.
■​ Syntax: <% code fragment %>

Example:​
<%

String name = request.getParameter("name");

if (name == null) {

name = "Guest";

counter++; // Accessing declared variable

%>

■​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Jsp​
IGNORE_WHEN_COPYING_END
○​ Expressions: Contain a single Java expression whose result is
evaluated, converted to a String, and inserted directly into the output
(HTML).
■​ Syntax: <%= expression %>
■​ Example: Welcome, <%= name %>! Page visits: <%= counter %>
3.​ Actions: XML-like tags that perform specific actions during the request
processing phase.
○​ Syntax: <jsp:actionName attribute="value" ... />
○​ Standard Actions:
■​ <jsp:include page="relativeURL" flush="true|false" />: Includes the
response generated by another resource (Servlet, JSP) at request
time. Different from the include directive.
■​ <jsp:forward page="relativeURL" />: Forwards the request to
another resource (Servlet, JSP). The current page stops
processing.
■​ <jsp:useBean id="beanName" class="packageName.ClassName"
scope="page|request|session|application" />: Finds or instantiates a
JavaBean object.
■​ <jsp:setProperty name="beanName" property="propertyName"
value="..." />: Sets a property value on a bean.
■​ <jsp:getProperty name="beanName" property="propertyName" />:
Gets a property value from a bean and inserts it into the output.
4.​ Expression Language (EL): A simpler language (syntax: ${expression})
primarily used to access data (especially JavaBeans and data stored in scopes
like request, session) without writing Java code in scriptlets. Often used with
JSTL.
○​ Example: Welcome, ${user.name}! Total items:
${sessionScope.cart.itemCount}
5.​ JSTL (JSP Standard Tag Library): A collection of predefined custom tags that
encapsulate common tasks like iteration, conditional logic, data formatting, and
XML manipulation, further reducing the need for scriptlets. Requires adding the
JSTL library and using the taglib directive.

Example (using Core library, prefix 'c'):​


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

...

<c:if test="${user != null}">

Welcome, ${user.name}!

</c:if>

<c:forEach var="item" items="${productList}">

<li>${item.name} - $${item.price}</li>

</c:forEach>

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Jsp​
IGNORE_WHEN_COPYING_END
4.9 JSP with Database

JSPs can interact with databases, but it's strongly discouraged to put raw JDBC
code directly into JSP files using scriptlets. This violates the principle of separation
of concerns and makes the code hard to maintain and debug.

Bad Practice (Avoid): Putting JDBC in JSP scriptlets.

<%@ page import="java.sql.*" %>

<%-- Avoid doing this directly in JSP! --%>

<%

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

try {

Class.forName("com.mysql.cj.jdbc.Driver");

con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user",


"pass");

stmt = con.createStatement();

rs = stmt.executeQuery("SELECT name FROM products");

while(rs.next()) {

out.println(rs.getString("name") + "<br>"); // Mixing logic and presentation

} catch (Exception e) {

out.println("Error: " + e.getMessage());

} finally {

// ... close resources ...

}
%>

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution.Jsp

IGNORE_WHEN_COPYING_END

Good Practice (MVC Pattern):

1.​ Servlet (Controller): Handles the incoming request, interacts with the database
(using JDBC, possibly through a separate Data Access Object - DAO layer),
retrieves the data, and stores it as request attributes.
2.​ JSP (View): The servlet forwards the request to the JSP. The JSP uses
Expression Language (EL) and JSTL (or simple expressions/scriptlets only for
display logic) to access the data from the request attributes and render the HTML
output.

Example (using MVC approach):

Servlet (ProductServlet.java):

// ... imports ...

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

List<Product> productList = new ArrayList<>();

Connection con = null; // Get connection (preferably from pool)

Statement stmt = null;

ResultSet rs = null;

try {

// ... (Get connection, create statement) ...

rs = stmt.executeQuery("SELECT id, name, price FROM products");


while (rs.next()) {

Product p = new Product();

p.setId(rs.getInt("id"));

p.setName(rs.getString("name"));

p.setPrice(rs.getDouble("price"));

productList.add(p);

// Store data in request scope

request.setAttribute("products", productList);

} catch (SQLException e) {

throw new ServletException("Database error", e);

} finally {

// ... (Close resources / return connection to pool) ...

// Forward to JSP for display

RequestDispatcher dispatcher =
request.getRequestDispatcher("/WEB-INF/views/displayProducts.jsp");

dispatcher.forward(request, response);

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution.Java

IGNORE_WHEN_COPYING_END
(Assume Product is a simple JavaBean class with id, name, price properties)

JSP (displayProducts.jsp):

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>

<html>

<head><title>Product List</title></head>

<body>

<h1>Our Products</h1>

<table>

<tr><th>ID</th><th>Name</th><th>Price</th></tr>

<c:forEach var="product" items="${products}"> <%-- Use EL and JSTL --%>

<tr>

<td>${product.id}</td>

<td>${product.name}</td>

<td>${product.price}</td>

</tr>

</c:forEach>

</table>

<c:if test="${empty products}">

<p>No products found.</p>

</c:if>

</body>
</html>

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution.Jsp

IGNORE_WHEN_COPYING_END

This approach keeps database logic out of the JSP, making the code cleaner and more
maintainable.

5. Spring & Hibernate

Spring Framework

5.1 Introduction

●​ What is Spring?
○​ The Spring Framework is a popular, open-source, comprehensive
application framework for the Java platform.
○​ It provides infrastructure support for developing robust, maintainable, and
scalable Java applications, especially enterprise-level applications.
○​ It's not just one thing; it's a modular framework composed of several
sub-projects (modules) like Spring Core, Spring MVC, Spring Data, Spring
Security, etc.
●​ Core Concept: Inversion of Control (IoC)
○​ Traditionally, components create or look up their dependencies (other
objects they need).
○​ In Spring (using IoC), the framework is responsible for creating objects
(called Beans) and injecting their dependencies. The control of object
creation and wiring is inverted from the component to the framework
(container).
●​ Core Concept: Dependency Injection (DI)
○​ DI is the primary pattern used to achieve IoC in Spring.
○​ Instead of an object creating its dependencies, the dependencies are
"injected" into the object by the Spring container, typically through:
■​ Constructor Injection: Dependencies are passed as arguments to
the constructor.
■​ Setter Injection: Dependencies are provided through setter
methods.

5.2 Applications and Benefits of Spring

●​ Applications:
○​ Web Applications (using Spring MVC)
○​ Enterprise Applications (using modules for transactions, security, data
access)
○​ RESTful Web Services
○​ Microservices
○​ Batch Processing
○​ Database interaction (simplifies JDBC, integrates with ORMs like
Hibernate)
●​ Benefits:
○​ Lightweight and Modular: Use only the modules you need.
○​ Promotes Loose Coupling: DI reduces dependencies between
components, making the system more flexible and easier to test.
○​ Simplifies Development: Provides templates and abstractions for
common tasks (JDBC, transactions, MVC).
○​ Declarative Programming: Features like transaction management and
security can often be configured declaratively (via annotations or XML)
rather than written programmatically.
○​ Easy Integration: Integrates well with many other popular Java
technologies and frameworks (Hibernate, JPA, Quartz, etc.).
○​ Testability: DI makes it easier to write unit tests by allowing mock
dependencies to be injected.
○​ Large Community and Ecosystem: Well-documented, widely adopted,
lots of resources available.

5.3 Architecture and Environment Setup

●​ Architecture (Modular):
1.​ Spring is organized into modules built on top of the Core Container.
2.​ Core Container: Provides the fundamental IoC and DI features (Beans,
Core, Context, Expression Language modules).
3.​ Data Access/Integration: Modules for JDBC, ORM (Hibernate, JPA),
Transactions, Messaging (JMS).
4.​ Web: Modules for web applications, including Spring MVC, WebFlux
(reactive web).
5.​ AOP (Aspect-Oriented Programming): Module for implementing
cross-cutting concerns (like logging, security) modularly.
6.​ Testing: Support classes for unit and integration testing Spring
applications.
7.​ (And others like Security, Batch, etc.)
●​ Environment Setup (Conceptual):
1.​ Java Development Kit (JDK): Ensure a compatible JDK is installed.
2.​ Build Tool (Maven or Gradle): These tools are standard for managing
project dependencies (including Spring modules) and building the
application. You define the required Spring modules in the project's
configuration file (pom.xml for Maven, build.gradle for Gradle).
3.​ IDE (Optional but Recommended): An IDE like Eclipse, IntelliJ IDEA, or
VS Code provides helpful features for Spring development.
4.​ Spring Dependencies: Add the necessary Spring module dependencies
(e.g., spring-context for Core, spring-webmvc for web apps) to your build
configuration file. The build tool will download the required JAR files.

5.4 Hello World Example (Conceptual)

A simple Spring application typically involves:

Define a Bean: Create a simple Java class (POJO - Plain Old Java Object).​
public class MessageService {

private String message = "Hello Spring!";

// getters and setters...

public String getMessage() { return message; }

1.​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
2.​ Configure Spring (XML or Annotations): Tell the Spring container about your
bean and how to create it.
XML Configuration (beans.xml):​
<beans xmlns="http://www.springframework.org/schema/beans" ...>

<bean id="messageServiceBean" class="com.example.MessageService"/>

</beans>

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Xml​
IGNORE_WHEN_COPYING_END

Annotation-Based Configuration (Preferred):​


import org.springframework.context.annotation.*;

@Configuration // Marks this class as a source of bean definitions

@ComponentScan(basePackages = "com.example") // Scan for components

public class AppConfig { }

// Add @Component to the MessageService class

import org.springframework.stereotype.Component;

@Component // Mark this class as a Spring bean

public class MessageService { /* ... */ }

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
Create Spring Container: Instantiate the Spring IoC container, loading the
configuration.​
import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext; // For


XML

import org.springframework.context.annotation.AnnotationConfigApplicationContext; //
For Annotations

public class MainApp {

public static void main(String[] args) {

// Using XML:

// ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

// Using Annotations:

ApplicationContext context = new


AnnotationConfigApplicationContext(AppConfig.class);

// Get the bean from the container

MessageService service = context.getBean(MessageService.class);

// Or by ID if using XML: MessageService service = (MessageService)


context.getBean("messageServiceBean");

System.out.println(service.getMessage());

// Close context if using ClassPathXmlApplicationContext in non-web app

// ((ClassPathXmlApplicationContext) context).close();
}

3.​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

5.5 Core Spring - IoC Containers, Spring Bean Definition, Scope, Lifecycle

●​ IoC Containers:
1.​ The core of Spring is the IoC container, responsible for managing the
lifecycle and configuration of application objects (beans).
2.​ Two main types:
■​ BeanFactory: The most basic container, providing core IoC/DI
support. Lazy initialization of beans by default.
■​ ApplicationContext: Extends BeanFactory, adding more
enterprise-specific features like easier integration with Spring AOP,
message resource handling (i18n), event publication, and
context-aware beans. Eagerly initializes singleton beans by default.
This is the preferred container for most applications. Common
implementations: ClassPathXmlApplicationContext,
FileSystemXmlApplicationContext,
AnnotationConfigApplicationContext, WebApplicationContext.
●​ Spring Bean Definition:
1.​ A bean is simply an object that is instantiated, assembled, and managed
by the Spring IoC container.
2.​ Bean Definitions are the configuration metadata that tells the container
how to create a bean:
■​ The fully qualified class name.
■​ Bean behavioral configuration elements (scope, lifecycle callbacks,
etc.).
■​ Dependencies on other beans.
■​ Other configuration settings (e.g., constructor arguments, property
values).
3.​ This metadata can be provided via:
■​ XML: Using <bean> tags in configuration files.
■​ Annotations: Using stereotypes like @Component, @Service,
@Repository, @Controller combined with @Autowired for injection
and @Configuration for Java-based config. (Most common modern
approach).
■​ Java Code: Directly using Java configuration classes with
@Configuration and @Bean methods.
●​ Bean Scope: Defines the lifecycle and visibility of a bean instance managed by
the container. Common scopes:
1.​ singleton (Default): Only one instance of the bean is created per Spring
IoC container. This single instance is shared for all requests for that bean.
2.​ prototype: A new instance of the bean is created every time it is
requested from the container.
3.​ request (Web-aware): A new instance is created for each incoming HTTP
request (only valid in a web-aware ApplicationContext).
4.​ session (Web-aware): A new instance is created for each HTTP session
(only valid in a web-aware ApplicationContext).
5.​ application (Web-aware): Scopes a bean instance to the lifecycle of the
ServletContext (like a global singleton for the web app).
6.​ Others like websocket...
7.​ Scope is defined in XML (<bean scope="...">) or via annotation
(@Scope("prototype")).
●​ Bean Lifecycle: The sequence of events from instantiation to destruction,
managed by the container. Key phases for a singleton bean:
1.​ Instantiate: Container creates the bean instance.
2.​ Populate Properties: Container injects dependencies (DI via setters or
constructors).
3.​ Bean Name Aware: If the bean implements BeanNameAware,
setBeanName() is called.
4.​ Bean Factory Aware: If implements BeanFactoryAware,
setBeanFactory() is called.
5.​ Application Context Aware: If implements ApplicationContextAware,
setApplicationContext() is called (if using ApplicationContext).
6.​ Pre-initialization: BeanPostProcessors postProcessBeforeInitialization()
methods are called.
7.​ Initialization Callbacks:
■​ If the bean implements InitializingBean, afterPropertiesSet() is
called.
■​ If a custom init-method is defined (XML or @Bean(initMethod=...)),
it's called.
8.​ Post-initialization: BeanPostProcessors postProcessAfterInitialization()
methods are called.
9.​ Bean is Ready: The bean is now available for use.
10.​Destruction (on container shutdown):
■​ If the bean implements DisposableBean, destroy() is called.
■​ If a custom destroy-method is defined (XML or
@Bean(destroyMethod=...)), it's called.

Hibernate

5.6 Architecture and Environment

●​ What is Hibernate?
1.​ Hibernate is a popular, open-source Object-Relational Mapping (ORM)
framework for Java.
2.​ ORM: A technique that maps object-oriented domain models (Java
classes) to relational database tables. It allows developers to interact with
the database using high-level Java objects instead of writing raw SQL and
manually handling ResultSets.
●​ Purpose:
1.​ To simplify database interaction in Java applications.
2.​ To automate the transfer of data between Java objects and database
tables.
3.​ To provide features like database independence (switch databases with
configuration changes), caching, lazy loading, and transaction
management integration.
●​ Core Architecture:
1.​ Configuration (hibernate.cfg.xml or Java-based): Provides database
connection details (driver, URL, user, pass) and mapping information.
2.​ SessionFactory: A heavyweight, thread-safe object created once per
application (usually at startup) based on the configuration. It acts as a
factory for Session objects.
3.​ Session: A lightweight, single-threaded object representing a
conversation between the application and the database. It's used to
perform CRUD (Create, Read, Update, Delete) operations. Not
thread-safe. Obtained from the SessionFactory. Should be short-lived.
4.​ Transaction: Represents an atomic unit of work. Database operations in
Hibernate are typically performed within a transaction boundary
(session.beginTransaction(), tx.commit(), tx.rollback()).
5.​ Persistent Objects: Instances of your Java classes (POJOs) that are
mapped to database tables. Their state is managed by the Hibernate
Session.
6.​ Mapping Metadata: Defines how Java class properties map to database
table columns (using XML files like *.hbm.xml or JPA annotations like
@Entity, @Table, @Id, @Column).
●​ Environment Setup (Conceptual):
1.​ JDK & Build Tool: Similar to Spring.
2.​ Database: Have a relational database (MySQL, PostgreSQL, etc.) set up.
3.​ Hibernate Dependencies: Add Hibernate core library (hibernate-core)
and potentially JPA API (jakarta.persistence-api or javax.persistence-api
depending on version) to your build configuration (pom.xml / build.gradle).
4.​ JDBC Driver: Include the JDBC driver JAR for your specific database.
5.​ Configuration File: Create hibernate.cfg.xml (or use Java-based config)
specifying DB connection properties and mapping resources.
6.​ Mapping: Create mapping files (.hbm.xml) or use JPA annotations in your
entity classes.

5.7 Configuration, Sessions, Persistent Class

Configuration (hibernate.cfg.xml - Example):​


<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- Database connection settings -->

<property
name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>

<property
name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydatabase</property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password">password</property>

<!-- JDBC connection pool (use C3P0 or HikariCP in production) -->


<property name="hibernate.connection.pool_size">1</property>

<!-- SQL dialect -->

<property
name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- Echo all executed SQL to stdout -->

<property name="hibernate.show_sql">true</property>

<!-- Optional: Drop and re-create the database schema on startup -->

<property name="hibernate.hbm2ddl.auto">update</property> <!-- create, update,


validate, none -->

<!-- Mention mapping resources -->

<mapping resource="com/example/entity/Student.hbm.xml"/>

<!-- Or if using annotations: <mapping class="com.example.entity.Student"/> -->

</session-factory>

</hibernate-configuration>

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Xml​
IGNORE_WHEN_COPYING_END

Building SessionFactory:​
import org.hibernate.SessionFactory;

import org.hibernate.cfg.Configuration;
// Typically done once at application startup

Configuration cfg = new Configuration().configure("hibernate.cfg.xml"); // Loads the XML


file

SessionFactory sessionFactory = cfg.buildSessionFactory();

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
●​ Session:
○​ Obtained from the SessionFactory. Represents a unit of work.
○​ Provides methods for database operations: save(), persist(), update(),
delete(), get(), load(), createQuery() (for HQL/JPQL).
○​ Manages the state of persistent objects within its context.
○​ Must be closed after use (typically in a finally block or try-with-resources
if Session implemented AutoCloseable in your Hibernate version).

Session session = null;

Transaction tx = null;

try {

session = sessionFactory.openSession(); // Get a session

tx = session.beginTransaction(); // Start a transaction

// --- Perform operations ---

// Student student = new Student("Alice", "CS");

// session.save(student); // Save a new student

// Student s = session.get(Student.class, 1L); // Get student with ID 1

// s.setMajor("IT"); // Update the student (Hibernate detects changes)


// session.update(s); // Explicit update (can be needed depending on state)

// session.delete(s); // Delete the student

// --------------------------

tx.commit(); // Commit the transaction

} catch (Exception e) {

if (tx != null) tx.rollback(); // Rollback on error

e.printStackTrace();

} finally {

if (session != null) session.close(); // Close the session

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
●​ Persistent Class (Entity):
○​ A Java class (POJO) that represents a table in the database.
○​ Must have a no-argument constructor.
○​ Properties usually have standard getters and setters.
○​ Needs mapping metadata (XML or Annotations) to link the class and its
properties to the table and columns.
○​ Must have a property mapped as the primary key identifier (@Id or <id>).

Example (using JPA Annotations):​


import jakarta.persistence.*; // Or javax.persistence.*

@Entity // Marks this class as a JPA entity (persistent class)

@Table(name = "students") // Maps to the 'students' table (optional if class name


matches table name)
public class Student {

@Id // Marks this field as the primary key

@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-increment ID

@Column(name = "student_id") // Maps to 'student_id' column

private Long id;

@Column(name = "student_name", length = 100, nullable = false) // Maps to


'student_name'

private String name;

@Column(name = "major") // Maps to 'major'

private String major;

// Default constructor (required by Hibernate)

public Student() {}

// Parameterized constructor (optional)

public Student(String name, String major) {

this.name = name;

this.major = major;

// Getters and Setters...


// (Standard getters and setters for id, name, major)

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

5.8 Mapping Files, Mapping Types

●​ Mapping: The process of defining how Java classes and their properties
correspond to database tables and columns. Hibernate needs this metadata to
perform ORM tasks.
●​ Ways to Provide Mapping:
○​ XML Mapping Files (*.hbm.xml): The traditional Hibernate way. An XML
file is created for each persistent class.
■​ Defines the class-to-table mapping (<class name="..." table="...">).
■​ Maps the identifier property (<id name="..." column="...">).
■​ Maps regular properties (<property name="..." column="..."
type="...">).
■​ Defines relationships (one-to-one, one-to-many, many-to-many).

Example (Student.hbm.xml):​
<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.example.entity">

<class name="Student" table="students">

<id name="id" column="student_id" type="long">

<generator class="identity"/> <!-- Use auto-increment -->

</id>

<property name="name" column="student_name" type="string" length="100"


not-null="true"/>
<property name="major" column="major" type="string"/>

</class>

</hibernate-mapping>

○​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Xml​
IGNORE_WHEN_COPYING_END
○​ JPA Annotations: The modern, standard (Java Persistence API) way.
Annotations are placed directly within the Java entity class itself. Preferred
approach in most new projects. (See Student class example in 5.7).
■​ @Entity, @Table, @Id, @GeneratedValue, @Column, @Transient
(ignore field).
■​ Relationship annotations: @OneToOne, @OneToMany,
@ManyToOne, @ManyToMany.
●​ Mapping Types: Hibernate automatically maps standard Java types (String, int,
long, boolean, Date, etc.) to appropriate SQL types based on the configured
database dialect.
○​ Basic Types: string, long, int, double, boolean, date, timestamp, blob,
clob, etc. Type can often be inferred, but can be specified explicitly
(type="..." in XML, @Type annotation - less common for basic types).
○​ Component Mapping: Mapping embedded objects (value types) within
an entity. (<component> or @Embedded, @Embeddable).
○​ Relationship Mappings: Define associations between entities (covered
by JPA annotations like @OneToMany etc., or XML tags like
<many-to-one>).
○​ Inheritance Mapping: Strategies for mapping class hierarchies to
database tables (e.g., table-per-hierarchy, table-per-subclass,
table-per-concrete-class).

5.9 Examples (Conceptual Database Operations)

Using the Session object (obtained from SessionFactory) and assuming the Student
entity from 5.7:

Saving a new Student:​


Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();
Student newStudent = new Student("Bob", "Physics");

session.save(newStudent); // Persists the new student

tx.commit();

session.close();

// newStudent object now has the generated ID set

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Retrieving a Student by ID:​


Session session = sessionFactory.openSession();

// .get() returns the object or null if not found. Hits DB immediately.

Student student = session.get(Student.class, 1L); // Assuming ID 1 exists

session.close();

if (student != null) {

System.out.println("Found: " + student.getName());

// .load() returns a proxy or throws exception if not found. May not hit DB until proxy is
used.

// Student studentProxy = session.load(Student.class, 1L);

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Updating a Student:​
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 2L); // Get student with ID 2

if (student != null) {

student.setMajor("Mathematics"); // Modify the object

// Hibernate automatically detects changes to persistent objects

// during transaction commit (Automatic Dirty Checking).

// session.update(student); // Explicit update often not needed if object is managed

tx.commit(); // Changes are flushed to DB

session.close();

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Deleting a Student:​
Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 3L); // Get student with ID 3

if (student != null) {

session.delete(student); // Delete the object

tx.commit();

session.close();

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END
●​ Querying using HQL (Hibernate Query Language) / JPQL (Java Persistence
Query Language):
○​ Object-oriented query language, similar to SQL but operates on entities
and properties.

Session session = sessionFactory.openSession();

// Find all students majoring in CS

String hql = "FROM Student s WHERE s.major = :majorName"; // :majorName is a


named parameter

Query<Student> query = session.createQuery(hql, Student.class);

query.setParameter("majorName", "CS");

List<Student> csStudents = query.list();

session.close();

// Process the list csStudents...

●​ ​
IGNORE_WHEN_COPYING_START​
content_copy download​
Use code with caution.Java​
IGNORE_WHEN_COPYING_END

Integration with Spring: Spring provides excellent integration with Hibernate (often via
Spring Data JPA), simplifying SessionFactory and Session management, transaction
handling (@Transactional annotation), and DAO implementation.

You might also like