✅ Problem:
- You need a database connection in your servlet.
- You want to log information, warnings, errors (instead of just printing to console).
If you just open a Connection
inside every servlet → it’s inefficient and dangerous (resource leaks, no pooling, etc.).
If you just System.out.println()
→ it’s unprofessional for real systems (no levels, no files, no timestamps).
✅ Solution Overview:
Task | How |
---|---|
Manage database connections | Use a Connection Pool (like HikariCP, Tomcat JDBC Pool, etc.) |
Logging | Use a logging framework (like SLF4J with Logback or java.util.logging) |
Manage connections in servlets | Use connection pool + dependency injection or ServletContextListener |
✅ Step 1: Organize Database Connections
Instead of opening a new connection every time, you initialize a connection pool once and then borrow connections.
🍀 Easiest Way:
Use connection pooling via Tomcat DataSource + JNDI.
You configure the DataSource in Tomcat’s context.xml
:
<Context>
<Resource name="jdbc/MyDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"
username="dbuser"
password="dbpassword"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydatabase"/>
</Context>
In your servlet:
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.sql.DataSource;
import java.sql.Connection;
// Inside your servlet
Context initContext = new InitialContext();
DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/MyDB");
Connection conn = ds.getConnection();
// use conn
Benefit:
- Fast (connections are reused).
- Safe (no resource leaks if you close properly).
- Managed (Tomcat controls number of connections).
✅ If you don’t want to use JNDI (for example in a standalone app), use HikariCP connection pool manually:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase");
config.setUsername("dbuser");
config.setPassword("dbpassword");
HikariDataSource ds = new HikariDataSource(config);
// later: ds.getConnection();
You can initialize this once in a ServletContextListener
, so it’s shared between servlets.
✅ Step 2: Logging properly
Forget about System.out.println()
. Use SLF4J + Logback (modern combo) or at least java.util.logging
.
Example with SLF4J
(common standard):
In your servlet:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(MyServlet.class);
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("Processing request for {}", request.getRequestURI());
try {
// db logic
} catch (Exception e) {
logger.error("Database error", e);
}
}
}
Benefits:
INFO
,DEBUG
,WARN
,ERROR
levels.- Logs can be sent to files, console, remote servers.
- Can format them easily (timestamps, user IDs, etc.).
✅ Step 3: Where to initialize database pool?
Best practice:
- Use a
ServletContextListener
to initialize your database pool when app starts. - Store the
DataSource
intoServletContext
attributes. - Servlets retrieve it from there.
Example:
Context Listener:
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
import com.zaxxer.hikari.HikariDataSource;
@WebListener
public class AppContextListener implements ServletContextListener {
private HikariDataSource ds;
@Override
public void contextInitialized(ServletContextEvent sce) {
ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("user");
ds.setPassword("pass");
sce.getServletContext().setAttribute("DataSource", ds);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if (ds != null) {
ds.close();
}
}
}
Then inside any servlet:
DataSource ds = (DataSource) getServletContext().getAttribute("DataSource");
try (Connection conn = ds.getConnection()) {
// use conn safely
}
✅ Summary Plan for Professional Servlets:
Area | Best Practice |
---|---|
Database connection | Connection Pool (DataSource) |
Connection lifecycle | Open in servlet, close after use (try-with-resources ) |
Logging | SLF4J + Logback or similar |
Initialization | Use ServletContextListener |
Error handling | Catch and log exceptions properly |
Resource closing | Always close Connection , ResultSet , PreparedStatement |
🌟 Visual Diagram:
[App Startup]
↓
[ContextListener] → creates ConnectionPool (DataSource)
↓
[Servlets] → get DataSource → get Connection → work → close Connection
↓
[Logging] → all events, errors, debug info