使用MVC和DAO模式在JSP页面中展示JDBC ResultSet的HTML。

61
我正在使用JSP和JDBC实现MVC。我已经将一个数据库类文件导入到我的JSP文件中,然后想要显示数据库表的数据。我不知道如何将Java类中的ResultSet返回给JSP页面并嵌入HTML中。
我应该如何做到这一点?
6个回答

131
在良好设计的MVC方法中,JSP文件不应包含任何Java代码,servlet类也不应包含任何JDBC代码。
假设您想在网店中显示产品列表,则需要创建以下代码。
  • A Product class representing a real world entity of a product, it should be just a Javabean.

    public class Product {
    
        private Long id; 
        private String name;
        private String description;
        private BigDecimal price;
    
        // Add/generate getters/setters/c'tors/equals/hashcode boilerplate.
    }
    
  • A DAO class which does all the nasty JDBC work and returns a nice List<Product>.

    public class ProductDAO {
    
        private DataSource dataSource;
    
        public ProductDAO(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public List<Product> list() throws SQLException {
            List<Product> products = new ArrayList<Product>();
    
            try (
                Connection connection = dataSource.getConnection();
                PreparedStatement statement = connection.prepareStatement("SELECT id, name, description, price FROM product");
                ResultSet resultSet = statement.executeQuery();
            ) {
                while (resultSet.next()) {
                    Product product = new Product();
                    product.setId(resultSet.getLong("id"));
                    product.setName(resultSet.getString("name"));
                    product.setDescription(resultSet.getString("description"));
                    product.setPrice(resultSet.getBigDecimal("price"));
                    products.add(product);
                }
            }
    
            return products;
        }
    
    }
    
  • A servlet class which obtains the list and puts it in the request scope.

    @WebServlet("/products")
    public class ProductsServlet extends HttpServlet {
    
        @Resource(name="jdbc/YourDB") // For Tomcat, define as <Resource> in context.xml and declare as <resource-ref> in web.xml.
        private DataSource dataSource;
        private ProductDAO productDAO;
    
        @Override
        public void init() {
            productDAO = new ProductDAO(dataSource);
        }
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            try {
                List<Product> products = productDAO.list();
                request.setAttribute("products", products); // Will be available as ${products} in JSP
                request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response);
            } catch (SQLException e) {
                throw new ServletException("Cannot obtain products from DB", e);
            }
        }
    
    }
    
  • Finally a JSP file in /WEB-INF/products.jsp which uses JSTL <c:forEach> to iterate over List<Product> which is made available in EL by ${products}, and uses JSTL <c:out> to escape string properties in order to avoid XSS holes when it concerns user-controlled input.

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/format" prefix="fmt" %>
    ...
    <table>
        <c:forEach items="${products}" var="product">
            <tr>
                <td>${product.id}</td>
                <td><c:out value="${product.name}" /></td>
                <td><c:out value="${product.description}" /></td>
                <td><fmt:formatNumber value="${product.price}" type="currency" currencyCode="USD" /></td>
            </tr>
        </c:forEach>
    </table>
    
为了使其正常工作,只需通过其URL调用servlet。只要该servlet被注释为@WebServlet("/products")或在web.xml中映射为<url-pattern>/products</url-pattern>,则可以通过http://example.com/contextname/products来调用它。
另请参阅:

1
非常有用的教程。我想知道这对于POST请求如何工作。我尝试通过维护一个bean来完成一个请求。但是在JSP上,我收到了一个错误,说不知道如何迭代bean。 - Dhruv
3
在设计良好的Java EE应用程序中,可以通过注入@Stateless EJB来实现完全透明地管理事务,例如 @EJB private ProductDAO productDAO;。但如果您的环境不支持EJB(例如Tomcat),在这个基本JDBC示例中,您当然也可以按照自己喜欢的方式在servlet的init()方法中实例化它。请注意,不要改变原文的意思。 - BalusC
1
@itsraja: еңЁservletзҡ„init()ж–№жі•дёӯжҲ–еңЁServletContextListenerзҡ„contextInitialized()ж–№жі•дёӯгҖӮ - BalusC
4
@Tiny:如果它是无状态的(即方法不依赖于任何实例变量,就像答案中展示的代码一样),那么它不应该有害。如果它是有状态的,则情况当然会改变。如果你手头有EJB,那么更容易的方法就是将其注释为@Stateless(或@Stateful)...并用一个JPA单行替换所有那些JDBC混乱,毕竟(除非你只是出于纯学习目的而玩弄十年前的技术/方法;))。 - BalusC
1
非常感谢您提供的精彩示例。我认为在产品Servlet中,@Resource("jdbc/MyDatasource") 应该改为 @Resource(name="jdbc/MyDatasource")。 - Dil.
显示剩余11条评论

12
在Web应用程序中,MVC并不是使用JSP类的问题。它包括使用以下模型:
  1. 浏览器发送请求到Web服务器
  2. Web服务器被配置为将请求处理为servlet或过滤器(控制器:Java代码,而不是JSP代码)
  3. 通常基于配置/注释,servlet/filter将请求分派给特定类(称为Action,控制器的特定部分)
  4. Action执行业务逻辑(例如从数据库中获取数据:模型)
  5. Action将请求转发到JSP。 JSP的作用仅是生成HTML代码(即显示数据:视图)
由于JSP通常使用JSP标签(例如JSTL)和JSP表达式语言,并且JSP标签和EL旨在从JavaBeans获取信息,因此最好以JavaBeans或JavaBeans集合的形式提供数据。
因此,控制器(操作类)的作用是获取数据,创建包含数据的JavaBean实例,并以适当的格式将其放入请求属性中,然后分派到JSP。 然后,JSP会迭代JavaBean实例并显示它们所包含的内容。
您不应该自己实现MVC框架。使用现有的框架(如Stripes,Struts等)。

5

我不知道应该如何从类文件将ResultSet返回到JSP页面

好吧,你不需要这样做。

MVC的重点是将模型(在这种情况下为M DB信息)与视图(在这种情况下为JSP)分开,使您可以更改视图而不会破坏应用程序。

为此,您可能需要使用中间对象来表示数据(通常称为DTO - Data Transfer Object - ,现在不知道他们如何称呼),并使用其他对象来获取它(通常是DAO)。

因此,基本上您有了JSP文件,获取请求参数,然后调用DAO的方法。 DAO内部具有连接到数据库并提取数据并构建DTO集合的手段,这些DTO将返回给JSP以进行呈现。

像这个极其简化(且不安全)的代码:

Employee.java

class Employee {
   String name;
   int emplid;
}

EmployeeDAO.java

class EmployeeDAO { 
   ... method to connect 
   etc. 
   List<Employee> getAllNamed( String name ) { 
       String query = "SELECT name, emplid FROM employee where name like ?";
       ResultSet rs = preparedStatement.executeQuery etc etc.
       List<Employee> results = ....
       while( rs.hasNext() ) { 
          results.add( new Employee( rs.getString("name"), rs.getInt("emplid")));
       }
       // close resources etc 
       return results;
    }
}

employee.jsp

<%
   request.setAttribute("employees", dao.getAllNamed( request.getParameter("name") );
%>
<table>
<c:forEach items="${employees}" var="employee">
<tr><td>${employee.emplid}</td><td>${employee.name}</td></tr>
</c:forEach>
</table>

我希望这能给你更好的理解。

1
使用JSP(视图)从DAO收集数据并设置请求属性和处理请求是控制器/Servlet(mvC)的工作。 JSP应仅使用JSTL $ {}或其他内容来显示控制器传递给它的数据。在MVC应用程序中,您永远不会在视图模板中执行业务逻辑。因此,您的示例违反了MVC方法论。 - DtechNet

0

我有一个问题。我不清楚代码的含义。我的代码也存在类似的问题。

我已经创建了 SQL 数据库并填充了数据。然后我想要实现一个 MainServlet(以下是代码),从数据库中获取数据,并在不同的 JSP 页面中将那些数据插入到如 h1、h2 等节中... 我必须使用 ${} 语法,但我不知道该怎么做。

简而言之,在 JSP 文件中(以下是代码,我必须使用 ${} 语法),我想要“调用” MainServlet,并从那里获取数据库中的数据并在 JSP 文件中显示。

希望我已经解释得正确,非常感谢!

MainServlet.java

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * Servlet implementation class MainServlet
 */
@WebServlet({ "/MainServlet" })
public class MainServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static final String PATH_JSP = "/WEB-INF/";
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MainServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
    }

    /**
     * @see Servlet#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String doveAndare = request.getParameter("azione");
        if(doveAndare==null)
            doveAndare = "index";
        try {
            String driverString = "com.mysql.cj.jdbc.Driver";
            Class.forName(driverString);
            String connString = "jdbc:mysql://localhost:3306/ldd_jewels?user=root&password=";
            Connection conn = DriverManager.getConnection(connString);
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * FROM JEWEL");
            while (rs.next() == true) {
                System.out.println(rs.getString("Category") + "\t" + rs.getString("Name"));
                
                /* I try that but does not work
                request.setAttribute("name", rs.getString("Name"));
                javax.servlet.RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/widering_male.jsp");
                dispatcher.forward(request, response); */
            }
            stmt.close();
            conn.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
        request.getRequestDispatcher(PATH_JSP+doveAndare+".jsp").forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

doublerow.jsp

   <section id="portfolio-details" class="portfolio-details">
        <div class="container">
          <div class="row gy-4">
            <div class="col-lg-8">
              <div class="portfolio-details-slider swiper">
                <div class="swiper-wrapper align-items-center">
                  <div class="swiper-slide">
                    <img src="assets/img/jewels/doublerow_1.jpg" alt="" />
                  </div>

                  <div class="swiper-slide">
                    <img src="assets/img/jewels/doublerow_2.jpg" alt="" />
                  </div>

                  <div class="swiper-slide">
                    <img src="assets/img/jewels/doublerow_3.jpg" alt="" />
                  </div>
                </div>
                <div class="swiper-pagination"></div>
              </div>
            </div>

            <div class="col-lg-4">
              <div class="portfolio-info">
                <h3>Product details</h3>
                <ul>
                  <li><strong>Code</strong>: 1S3D5</li>
                  <li><strong>Category</strong>: Bracelets</li>
                  <li><strong>Name</strong>: Double Row Hinged Bangle</li>
                  <li><strong>Gender</strong>: Female</li>
                  <li><strong>Material</strong>: Yellow gold</li>
                  <li><strong>Size</strong>: 121mm</li>
                  <li><strong>Price</strong>: €5500</li>
                </ul>
              </div>
              <div class="portfolio-description">
                <h2>Description of product</h2>
                <p>
                  The entwined ends of Tiffany Knot’s signature motif symbolize
                  the power of connections between people. Balancing strength
                  and elegance, each Tiffany Knot design is a complex feat of
                  craftsmanship. This bangle is crafted with yellow gold and
                  polished by hand for high shine. Wear on its own or partnered
                  with classic silhouettes for an unexpected pairing.
                </p>
              </div>
            </div>
          </div>
        </div>
      </section>

这是我的数据库: Database 我想在不同的页面中插入每个珠宝(每个珠宝都有一个JSP文件)。

-1

我认为最好将表格数据包含在集合中,例如列表,并从Java类返回该列表,在JSP中重复使用此集合。


ResultSet(以及StatementConnection)传递到其被获取的方法范围之外是一种不好的做法。您将无法在正确的范围内关闭它,从而导致 DB 资源泄漏。长期来看,应用程序将崩溃,因为DB资源已耗尽。 - BalusC

-1

您可以使用<c:forEach>标签

您可以在以下链接示例用法中找到详细的示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接