The servlet uses JDBC. For the servlet to work, the jar file with the driver needs to be put in the WEB-INF/lib/ folder.
When the servlet starts, the driver is not picked up. What am I doing wrong?

UPD: The problem was solved by the explicit creation of the Class.forName("com.mysql.jdbc.Driver") driver Class.forName("com.mysql.jdbc.Driver") . I wonder why it took here. In a regular Java SE application, it worked without explicit creation.

Project structure
structure

Java Servlet:

 package com.company.app.logic; import java.sql.ResultSet; import java.sql.SQLException; public class Item { private int itemId; private String itemName; public Item(ResultSet rs) { try { this.itemId = rs.getInt(1); this.itemName = rs.getString(2); } catch (SQLException e) { e.printStackTrace(); } } public String getItemName() { return itemName; } public int getItemId() { return itemId; } public String toString() { return itemName; } } package com.company.app.logic; import java.sql.*; import java.util.*; public class Main { private static Main instance = new Main(); private static Connection connection; private Main() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/maindb"; connection = DriverManager.getConnection(url, "root", "root"); } catch (Exception e) { e.printStackTrace(); } } public static Main getInstance() { return instance; } public List<Item> getItems() { List<Item> items = new ArrayList<>(); Statement statement = null; ResultSet rs = null; try { statement = connection.createStatement(); rs = statement.executeQuery("SELECT * FROM items"); while (rs.next()) { Item g = new Item(rs); items.add(g); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (statement != null) { statement.close(); } } catch (SQLException e) { e.printStackTrace(); } } return items; } } package com.company.app.web; import com.company.app.logic.Item; import com.company.app.logic.Main; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.List; @WebServlet("/items") public class MainServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); PrintWriter pw = resp.getWriter(); pw.println("<table border=1>"); try { List<Item> l = Main.getInstance().getItems(); for (Item item : l) { pw.println("<tr>"); pw.println("<td>" + item.getItemId() + "</td>"); pw.println("<td>" +item.getItemName() + "</td>"); pw.println("</tr>"); } } catch (Exception e) { throw new ServletException(e); } pw.println("</table>"); } } 

Normal Java SE project:

 package com.company.app.logic; import java.sql.ResultSet; import java.sql.SQLException; public class Item { private int itemId; private String itemName; public Item(ResultSet rs) { try { this.itemId = rs.getInt(1); this.itemName = rs.getString(2); } catch (SQLException e) { e.printStackTrace(); } } public String getItemName() { return itemName; } public int getItemId() { return itemId; } public String toString() { return itemName; } } package com.company.app.logic; import java.sql.*; import java.util.*; public class Main { private static Main instance = new Main(); private static Connection connection; private Main() { try { String url = "jdbc:mysql://localhost:3306/maindb"; connection = DriverManager.getConnection(url, "root", "root"); } catch (Exception e) { e.printStackTrace(); } } public static Main getInstance() { return instance; } public List<Item> getItems() { List<Item> items = new ArrayList<>(); Statement statement = null; ResultSet rs = null; try { statement = connection.createStatement(); rs = statement.executeQuery("SELECT * FROM items"); while (rs.next()) { Item g = new Item(rs); items.add(g); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (statement != null) { statement.close(); } } catch (SQLException e) { e.printStackTrace(); } } return items; } public static void main(String[] args) { Main m = Main.getInstance(); m.getItems().forEach(System.out::println) } } 
  • Show the code. What IDE do you work with? - Vladimir Glinskikh
  • @Vladimir, updated the question, without IDE. javac -d ../WEB-INF/classes/ -cp ".:../../../lib/servlet-api.jar" com/company/app/web/MainServlet.java - jisecayeyo

1 answer 1

The problem was solved by the explicit creation of the Class.forName("com.mysql.jdbc.Driver") driver Class.forName("com.mysql.jdbc.Driver") . I wonder why it took here. In a regular Java SE application, it worked without explicit creation.

This is normal. If you look at the DriverManager code (or read a JDBC spec), you will see that it requires calling the registerDriver() static method. Obviously, for this, the driver class should pull this method, and the earliest place where it can be done is the static class initialization block ( static {...} ).

Static initialization of a class occurs when it is loaded by the classloader. Appealing to a class named Class.forName("com.mysql.jdbc.Driver") just causes the classloader to load the class, and the class in turn registers with DriverManger . Until then, he lies quietly in the CLASSPATH and nobody knows about him.


Now I understand, your question can be rephrased like this: "Why doesn't Tomcat find the JDBC driver through the service provider?".

In the case of a JavaSE application, the loading mechanism is used via the ServiceLoader from the JDBC 4 specification. When starting, CLASSPATH scanned for the file (s) META-INF/services/java.sql.Driver , from which DriverManager can get the names of the classes that implement this interface and register them automatically.

The problem with Tomcat is described on the official website and is associated with a more complex structure of loaders and memory leaks. In short: Tomcat searches for drivers through the service provider only at its start, and not at the start / start of each application. And only jar-files in the $CATALINA_BASE/lib directory (common to all web applications) are available to it. This feature is not available for classes within your application from WEB-INF/lib .

In addition, if you use the method with manual driver registration ( Class.forName ), it is strongly recommended that you unregister the driver when the application is ServletContextListener implementing ServletContextListener .

  • Why does a normal Java SE project work without Class.forName("com.mysql.jdbc.Driver") and DriverManager.registerDriver(new com.mysql.jdbc.Driver()); ? - jisecayeyo
  • I do not see your "regular JavaSE project". - Nofate
  • changed the question. - jisecayeyo
  • It seems that the appearance of JDBC 4.0 has become possible without explicitly creating a driver object. - jisecayeyo
  • @jisecayeyo, supplemented the answer - Nofate