Advanced-Java-Notes
Advanced-Java-Notes
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. 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.
Connecting to a database and executing SQL commands using JDBC typically involves
the following steps:
● 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:
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
}
2. 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.
● 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:
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.");
}
}
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END
● 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:
@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.");
}
}
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END
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.
● 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.
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:
○
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:
● 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.
● 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).
// Get streams
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
●
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)
● 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.).
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.
import javax.servlet.*;
import java.io.IOException;
@Override
res.setContentType("text/plain");
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).
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
@Override
response.setContentType("text/html");
out.println("<html><body>");
out.println("<p>Hello, World!</p>");
out.println("</body></html>");
@Override
response.setContentType("text/html");
out.println("<html><body>");
out.println("</body></html>");
4.
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END
The servlet container manages the lifecycle of a servlet instance. The key methods
involved are:
Summary:
● Load & Instantiate -> init() [Once] -> service() [Called per request in a new
thread] -> destroy() [Once]
● 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).
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.
● 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.
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.
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" %>
■
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:
<%
if (name == null) {
name = "Guest";
%>
■
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.
...
Welcome, ${user.name}!
</c:if>
<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.
<%
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
stmt = con.createStatement();
while(rs.next()) {
} catch (Exception e) {
} finally {
}
%>
IGNORE_WHEN_COPYING_START
content_copy download
IGNORE_WHEN_COPYING_END
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.
Servlet (ProductServlet.java):
ResultSet rs = null;
try {
p.setId(rs.getInt("id"));
p.setName(rs.getString("name"));
p.setPrice(rs.getDouble("price"));
productList.add(p);
request.setAttribute("products", productList);
} catch (SQLException e) {
} finally {
RequestDispatcher dispatcher =
request.getRequestDispatcher("/WEB-INF/views/displayProducts.jsp");
dispatcher.forward(request, response);
IGNORE_WHEN_COPYING_START
content_copy download
IGNORE_WHEN_COPYING_END
(Assume Product is a simple JavaBean class with id, name, price properties)
JSP (displayProducts.jsp):
<!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>
<tr>
<td>${product.id}</td>
<td>${product.name}</td>
<td>${product.price}</td>
</tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
IGNORE_WHEN_COPYING_START
content_copy download
IGNORE_WHEN_COPYING_END
This approach keeps database logic out of the JSP, making the code cleaner and more
maintainable.
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.
● 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.
● 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.
Define a Bean: Create a simple Java class (POJO - Plain Old Java Object).
public class MessageService {
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" ...>
</beans>
○
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Xml
IGNORE_WHEN_COPYING_END
import org.springframework.stereotype.Component;
○
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.annotation.AnnotationConfigApplicationContext; //
For Annotations
// Using XML:
// Using Annotations:
System.out.println(service.getMessage());
// ((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
● 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.
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<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>
<property
name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<!-- Optional: Drop and re-create the database schema on startup -->
<mapping resource="com/example/entity/Student.hbm.xml"/>
</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
○
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).
Transaction tx = null;
try {
// --------------------------
} catch (Exception e) {
e.printStackTrace();
} finally {
●
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>).
public Student() {}
this.name = name;
this.major = major;
●
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END
● 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
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example.entity">
</id>
</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).
Using the Session object (obtained from SessionFactory) and assuming the Student
entity from 5.7:
Transaction tx = session.beginTransaction();
Student newStudent = new Student("Bob", "Physics");
tx.commit();
session.close();
●
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution.Java
IGNORE_WHEN_COPYING_END
session.close();
if (student != null) {
// .load() returns a proxy or throws exception if not found. May not hit DB until proxy is
used.
●
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();
if (student != null) {
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();
if (student != null) {
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.
query.setParameter("majorName", "CS");
session.close();
●
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.