J2EE 电商实战相关知识点拾遗

一、实体类设计

所谓的实体类,就是对于数据库中的表的互相映射的类。
这是一种ORM的设计思想,即一个对象,对应数据库里的一条记录

基于前面数据库表结构的分析和设计,根据创建表的SQL语句,和表与页面一一对应关系,按照表与表之间的依赖顺序,逐个设计我们项目的实体类。

包名

所有的实体类,都放在 shop/src/shop.bean 包中

toString()重写

在开发的时候,我们会用到重写这个方法:

 public String toString() {
        return "Category [name=" + name + "]";
    }

表示重写toString方法,当打印Category对象的时候,会打印其名称。 在实际业务的时候并没有调用,在测试的过程中会调用到。

示例:

Category.java

package tmall.bean;

import java.util.List;

public class Category {
    private String name;
    private int id;
    List<Product> products;
    List<List<Product>> productsByRow;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Category [name=" + name + "]";
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public List<List<Product>> getProductsByRow() {
        return productsByRow;
    }

    public void setProductsByRow(List<List<Product>> productsByRow) {
        this.productsByRow = productsByRow;
    }

}

二、DAO类设计

接下来是DAO类,DAO是Data Access Object的缩写,这些类专门用于进行数据库访问的操作。

  1. 首先讲解两个工具类,DBUtilDateUtil,因为在DAO类中会用到这两个工具类

  2. 接着按照依赖顺序,逐个讲解不同的DAO类以及其中的方法

步骤1:DBUtil

DBUtil:数据库工具类,这个类的作用是初始化驱动,并且提供一个getConnection用于获取连接。 在后续的所有DAO中,当需要获取连接的时候,都采用这种方式进行。
数据库连接的参数,如数据库名称,账号密码,编码方式等都设计在属性上,便于统一修改,降低维护成本。

package tmall.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBUtil {
    static String ip = "127.0.0.1";
    static int port = 3306;
    static String database = "tmall";
    static String encoding = "UTF-8";
    static String loginName = "root";
    static String password = "admin";

    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
        return DriverManager.getConnection(url, loginName, password);
    }

    public static void main(String[] args) throws SQLException {
        System.out.println(getConnection());

    }

}

步骤2:DateUtil

DateUtil这个日期工具类主要是用于java.util.Date类与java.sql.Timestamp 类的互相转换。

因为在实体类中日期类型的属性,使用的都是java.util.Date类。
而为了在MySQL中的日期格式里保存时间信息,必须使用datetime类型的字段,而jdbc要获取datetime类型字段的信息,需要采用java.sql.Timestamp来获取,否则只会保留日期信息,而丢失时间信息。

package tmall.util;

public class DateUtil {

    public static java.sql.Timestamp d2t(java.util.Date d) {
        if (null == d)
            return null;
        return new java.sql.Timestamp(d.getTime());
    }

    public static java.util.Date t2d(java.sql.Timestamp t) {
        if (null == t)
            return null;
        return new java.util.Date(t.getTime());
    }
}

三、导入jar包

项目中需要引入第三方 jar包,以后我们使用maven,现在先手动导入:

file

file

file

四、Filter配合Servlet

Servlet生命周期

一个Servlet的生命周期由 实例化,初始化,提供服务,销毁,被回收 几个步骤组成。

file

实例化

用户通过浏览器输入一个路径,这个路径对应的servlet被调用的时候,该Servlet就会被实例化
为LoginServlet显式提供一个构造方法 LoginServlet()

无论访问了多少次LoginServlet
LoginServlet构造方法 只会执行一次,所以Servlet是单实例的。

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

    public LoginServlet(){
        System.out.println("LoginServlet 构造方法 被调用");
    }

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //略

    }

}

初始化

LoginServlet 继承了HttpServlet,同时也继承了init(ServletConfig) 方法

init 方法是一个实例方法,所以会在构造方法执行后执行。

无论访问了多少次LoginSerlvet
init初始化 只会执行一次

import java.io.IOException;

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

public class LoginServlet extends HttpServlet {

    public LoginServlet() {
        System.out.println("LoginServlet 构造方法 被调用");
    }

    public void init(ServletConfig config) {
        System.out.println("init(ServletConfig)");
    }

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 略

    }

}

提供服务

接下来就是执行service()方法,然后通过浏览器传递过来的信息进行判断,是调用doGet()还是doPost()方法
在service()中就会编写我们的业务代码,在本例中就是判断用户输入的账号和密码是否正确

public class LoginServlet extends HttpServlet {

    public LoginServlet() {
        System.out.println("LoginServlet 构造方法 被调用");
    }

    public void init(ServletConfig config) {
        System.out.println("init(ServletConfig)");
    }

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

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

        String html = null;

        if ("admin".equals(name) && "123".equals(password))
            html = "<div style='color:green'>success</div>";
        else
            html = "<div style='color:red'>fail</div>";

        PrintWriter pw = response.getWriter();
        pw.println(html);

    }

}

销毁

接着是销毁destroy()
在如下几种情况下,会调用destroy()

  1. 该Servlet所在的web应用重新启动
    在server.xml中配置该web应用的时候用到了
    <Context path="/" docBase="e:\\project\\j2ee\\web" debug="0" reloadable="false" />

如果把 reloadable="false" 改为reloadable="true" 就表示有任何类发生的更新,web应用会自动重启
当web应用自动重启的时候,destroy()方法就会被调用

  1. 关闭tomcat的时候 destroy()方法会被调用,但是这个一般都发生的很快,不易被发现。
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

    public void destroy() {
        System.out.println("destroy()");
    }

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 略
    }

}

被回收

当该Servlet被销毁后,就满足垃圾回收的条件了。 当下一次垃圾回收GC来临的时候,就有可能被回收。这个也是不容易被观察到的现象。

Servlet service()

LoginServlet继承了HttpServlet,同时也继承了一个方法

service(HttpServletRequest , HttpServletResponse )

实际上,在执行doGet()或者doPost()之前,都会先执行service()

由service()方法进行判断,到底该调用doGet()还是doPost()

可以发现,service(), doGet(), doPost() 三种方式的参数列表都是一样的。

所以,有时候也会直接重写service()方法,在其中提供相应的服务,就不用区分到底是get还是post了。

比如把前面的登录的LoginServlet,改为提供service方法,也可以达到相同的效果

...
public class LoginServlet extends HttpServlet {

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

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

        String html = null;

        if ("admin".equals(name) && "123".equals(password))
            html = "<div style='color:green'>success</div>";
        else
            html = "<div style='color:red'>fail</div>";

        PrintWriter pw = response.getWriter();
        pw.println(html);

    }

}

Servlet 跳转

页面跳转是开发一个web应用经常会发生的事情。比如登录成功或是失败后,分别会跳转到不同的页面。跳转的方式有两种,服务端跳转客户端跳转

服务端跳转

在Servlet中进行服务端跳转的方式:

request.getRequestDispatcher("success.html").forward(request, response);

服务端跳转可以看到浏览器的地址依然是/login 路径,并不会变成success.html

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

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

        if ("admin".equals(name) && "123".equals(password)) {
            request.getRequestDispatcher("success.html").forward(request, response);
        }

    }

}

客户端跳转

在Servlet中进行客户端跳转的方式:

response.sendRedirect("fail.html");

可以观察到,浏览器地址发生了变化,变为 127.0.0.1/fail.html

示意图

file

Filter使用

Filter就像一个一个哨卡,用户的请求需要经过Filter,并且可以有多个过滤器。

编写测试 Filter

开发一个简单的FirstFilter,用来打印用户访问ip地址和访问的页面

HttpServletRequest request = (HttpServletRequest) req;

doFilter()方法中的req参数的类型是ServletRequest,需要转换为HttpServletRequest类型方便调用某些方法 (参考request常见方法)

String ip = request.getRemoteAddr();

获取来路用户的ip地址

String url = request.getRequestURL().toString();

获取用户访问的页面地址

System.out.printf("%s %s 访问了 %s%n", date, ip, url);

在控制台打印出来

chain.doFilter(request, response);

过滤器放行,表示继续运行下一个过滤器,或者最终访问的某个servlet,jsp,html等等

package filter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FirstFilter implements Filter {

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        String ip = request.getRemoteAddr();
        String url = request.getRequestURL().toString();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = new Date();
        String date = sdf.format(d);

        System.out.printf("%s %s 访问了 %s%n", date, ip, url);
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }

}

配置web.xml

在web.xml中进行filter的配置,和servlet的配置很类似

<url-pattern>/*</url-pattern>

表示所有的访问都会过滤

如果配置成

<url-pattern>*.jsp</url-pattern>

就表示只过滤jsp

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>filter.FirstFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

为者常成,行者常至