巩固Servlet+JSP开发模式,做一个比较完整的小项目.
成果图该项目包含了两个部分,前台和后台。
前台用于显示
后台用于管理
该项目可分为5个模块来组成:分类模块,用户模块,图书模块,购买模块,订单模块。
搭建环境 建立包结构
- index.jsp【没有body标签的】
 
  <frameset rows="25%,*">
    <frame src="${pageContext.request.contextPath}/client/head.jsp"/>
    <frame src="${pageContext.request.contextPath}/client/body.jsp"/>
  </frameset>
- head.jsp
 
<body >
<h1>欢迎来到购物中心</h1>
- 
body是空白的jsp页面
 - 效果:
 
后台分帧页面
- manager.jsp【嵌套了framset标签,也是没有body标签的】
 
<frameset rows="25%,*">
    <frame src="${pageContext.request.contextPath}/background/head.jsp"/>
    <frameset cols="15%,*">
        <frame src="${pageContext.request.contextPath}/background/left.jsp"/>
        <frame src="${pageContext.request.contextPath}/background/body.jsp"/>
    </frameset>
</frameset>
- head.jsp
 
<body >
<h1>后台管理</h1>
- left.jsp
 
<a href="#">分类管理</a>
<br>
<br>
<a href="#">图书管理</a>
<br>
<br>
<a href="#">订单管理</a>
<br>
<br>
- 
body.jsp是空白的
 - 效果:
 
值得注意的是:
- 文件夹的名字不能使用“manager”,不然会出现:403 Access Denied错误
 - frameset标签是可以嵌套的,分列用“cols”,分行用“rows”
 
- 过滤中文乱码数据
 - HTML转义
 - DAOFactory
 - JDBC连接池
 - UUID工具类
 - c3p0.xml配置文件
 
这些代码都可以在我的博客分类:代码库中找到!
分类模块
首先,我们来做分类模块吧
创建实体Category    private String id;
    private String name;
    private String description;
    //各种setter、getter
 在数据库创建表
CREATE TABLE category (
  id          VARCHAR(40) PRIMARY KEY,
  name        VARCHAR(10) NOT NULL UNIQUE ,
  description VARCHAR(255)
);
 编写CategoryDAO
/**
 * 分类模块
 *  1:添加分类
 *  2:查找分类
 *  3:修改分类
 *
 *
 * */
public class CategoryImpl {
    public void addCategory(Category category) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "INSERT INTO category (id, name, description) VALUES(?,?,?)";
        try {
            queryRunner.update(sql, new Object[]{category.getId(), category.getName(), category.getDescription()});
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public Category findCategory(String id) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "SELECT * FROM category WHERE id=?";
        try {
            Category category = (Category) queryRunner.query(sql, id, new BeanHandler(Category.class));
            return category;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public List<Category> getAllCategory() {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "SELECT * FROM category";
        try {
            List<Category> categories = (List<Category>) queryRunner.query(sql, new BeanListHandler(Category.class));
             return categories;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
 测试DAO
public class demo {
    @Test
    public void add() {
        Category category = new Category();
        category.setId("2");
        category.setName("数据库系列");
        category.setDescription("这是数据库系列");
        CategoryImpl category1 = new CategoryImpl();
        category1.addCategory(category);
    }
    @Test
    public void find() {
        String id = "1";
        CategoryImpl category1 = new CategoryImpl();
        Category category = category1.findCategory(id);
        System.out.println(category.getName());
    }
    @Test
    public void getAll() {
        CategoryImpl category1 = new CategoryImpl();
        List<Category> categories = category1.getAllCategory();
        for (Category category : categories) {
            System.out.println(category.getName());
        }
    }
}
 抽取成DAO接口
public interface CategoryDao {
    void addCategory(Category category);
    Category findCategory(String id);
    List<Category> getAllCategory();
}
 后台页面的添加分类
- 在超链接上,绑定显示添加分类的页面
 
<a href="${pageContext.request.contextPath}/background/addCategory.jsp" target="body">添加分类</a>
- 显示添加分类的JSP页面
 
<form action="${pageContext.request.contextPath}/CategoryServlet?method=add" method="post">
    分类名称:<input type="text" name="name"><br>
    分类描述:<textarea name="description"></textarea><br>
    <input type="submit" value="提交">
</form>
- 处理添加分类的Servlet
 
        if (method.equals("add")) {
            try {
                //把浏览器带过来的数据封装到bean中
                Category category = WebUtils.request2Bean(request, Category.class);
                category.setId(WebUtils.makeId());
                service.addCategory(category);
                request.setAttribute("message", "添加分类成功!");
            } catch (Exception e) {
                request.setAttribute("message","添加分类失败");
                e.printStackTrace();
            }
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }
- 效果:
 
后台页面的查看分类
- 在超链接上,绑定处理请求的Servlet
 
        else if (method.equals("look")) {
            List<Category> list = service.getAllCategory();
            request.setAttribute("list", list);
            request.getRequestDispatcher("/background/lookCategory.jsp").forward(request, response);
        } 
- 显示分类页面的JSP
 
<c:if test="${empty(list)}">
    暂时还没有分类数据哦,请你添加把
</c:if>
<c:if test="${!empty(list)}">
    <table border="1px">
        <tr>
            <td>分类名字</td>
            <td>分类描述</td>
            <td>操作</td>
        </tr>
    <c:forEach items="${list}" var="category">
        <tr>
            <td>${category.name}</td>
            <td>${category.description}</td>
            <td>
                <a href="#">删除</a>
                <a href="#">修改</a>
            </td>
        </tr>
    </c:forEach>
    </table>
</c:if>
- 效果:
 
图书模块 分析
在设计图书管理的时候,我们应该想到:图书和分类是有关系的。一个分类可以对应多本图书。
为什么要这样设计?这样更加人性化,用户在购买书籍的时候,用户能够查看相关分类后的图书,而不是全部图书都显示给用户,让用户一个一个去找。
设计实体
    private String id;
    private String name;
    private String author;
    private String description;
    private double price;
    //记住图片的名称
    private String image;
    //记住分类的id
    private String category_id;
    //各种setter和getter
 设计数据库表
CREATE TABLE book (
  id          VARCHAR(40) PRIMARY KEY,
  name        VARCHAR(10) NOT NULL UNIQUE,
  description VARCHAR(255),
  author      VARCHAR(10),
  price       FLOAT,
  image       VARCHAR(100),
  category_id VARCHAR(40),
  CONSTRAINT category_id_FK FOREIGN KEY (category_id) REFERENCES category (id)
);
 编写DAO
/**
 * 图书模块
 * 1:添加图书
 * 2:查看图书
 * 3:查找图书的分页数据【图书一般来说有很多,所以要分页】
 */
public class BookDaoImpl {
    public void addBook(Book book) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "INSERT INTO book (id,name,description,author,price,image,category_id) VALUES(?,?,?,?,?,?,?)";
        try {
            queryRunner.update(sql, new Object[]{book.getId(), book.getName(), book.getDescription(), book.getAuthor(), book.getPrice(),book.getImage(), book.getCategory_id()});
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    public Book findBook(String id) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "SELECT * FROM book WHERE id=?";
        try {
            return (Book) queryRunner.query(sql, id, new BeanHandler(Book.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    /**得到图书的分页数据*/
    public List<Book> getPageData(int start, int end) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "SELECT * FROM book limit ?,?";
        try {
            return (List<Book>) queryRunner.query(sql, new BeanListHandler(Book.class), new Object[]{start, end});
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    /**得到按照分类图书的分页数据*/
    public List<Book> getPageData(int start, int end,String category_id) {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        //WHERE字句在limit字句的前边,注意Object[]的参数位置!
        String sql = "SELECT * FROM book WHERE category_id=? limit ?,?";
        try {
            return (List<Book>) queryRunner.query(sql, new BeanListHandler(Book.class), new Object[]{ category_id,start, end});
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 得到图书的总记录数
     */
    public int getTotalRecord() {
        QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
        String sql = "SELECT COUNT(*) FROM book";
        try {
            return (int) queryRunner.query(sql, new ScalarHandler());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 得到分类后图书的总记录数
     * getCategoryTotalRecord
     */
    public long getCategoryTotalRecord(String category_id) {
        try {
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            String sql = "SELECT COUNT(*) FROM book WHERE category_id=?";
            return (long) queryRunner.query(sql, category_id, new ScalarHandler());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
 测试DAO
public class BookDemo {
    BookDaoImpl bookDao = new BookDaoImpl();
    @Test
    public void add() {
        Book book = new Book();
        book.setId("5");
        book.setName("SQLServer");
        book.setAuthor("我也不知道");
        book.setImage("33333332432");
        book.setPrice(33.22);
        book.setDescription("这是一本好书");
        book.setCategory_id("2");
        bookDao.addBook(book);
    }
    @Test
    public void look() {
        List<Book> bookList = bookDao.getPageData(3, 3);
        for (Book book : bookList) {
            System.out.println(book.getName());
        }
        List<Book> books = bookDao.getPageData(0,2,"2");
        for (Book book : books) {
            System.out.println(book.getName());
        }
    }
    @Test
    public void find() {
        String id = "2";
        Book book = bookDao.findBook(id);
        System.out.println(book.getName());
    }
}
 抽取成DAO接口
public interface BookDao {
    void addBook(Book book);
    Book findBook(String id);
    List<Book> getPageData(int start, int end);
    List<Book> getPageData(int start, int end, String category_id);
    long getTotalRecord();
    long getCategoryTotalRecord(String category_id);
}
 编写Service层
    /*添加图书*/
    public void addBook(Book book) {
        bookDao.addBook(book);
    }
    /*查找图书*/
    public Book findBook(String id) {
        return bookDao.findBook(id);
    }
    /*查找图书*/
    public Book findBook(String id) {
        return bookDao.findBook(id);
    }
    /*获取图书的分页数据*/
    public Page getPageData(String pageNum) {
        Page page=null;
        if (pageNum == null) {
            page = new Page(1, bookDao.getTotalRecord());
        } else {
            page = new Page(Integer.valueOf(pageNum), bookDao.getTotalRecord());
        }
        List<Book> books = bookDao.getPageData(page.getStartIndex(), page.getLinesize());
        page.setList(books);
        return page;
    }
    /*获取图书分类后的分页数据*/
    public Page getPageData(String currentPageCount,String category_id) {
        Page page=null;
        if (currentPageCount == null) {
            page = new Page(1, bookDao.getCategoryTotalRecord(category_id));
        } else {
            page = new Page(Integer.valueOf(currentPageCount), bookDao.getCategoryTotalRecord(category_id));
        }
        List<Book> books = bookDao.getPageData(page.getStartIndex(), page.getLinesize(), category_id);
        page.setList(books);
        return page;
    }
 后台添加图书
后台要添加图书的时候,应该说明图书的类型是什么。
要想在显示添加图书的页面上知道全部类型的id,就要经过Servlet把类型的集合传送过去
绑定链接
<a href="${pageContext.request.contextPath}/BookServlet?method=addUI" target="body">添加图书</a><br>
传送类型集合的Servlet
        String method = request.getParameter("method");
        BussinessServiceImpl service = new BussinessServiceImpl();
        if (method.equals("addUI")) {
            List<Category> list = service.getAllCategory();
            request.setAttribute("list", list);
            request.getRequestDispatcher("/background/addBook.jsp").forward(request, response);
        } 
显示JSP页面
<form action="${pageContext.request.contextPath}/BookServlet?method=add" method="post" enctype="multipart/form-data">
    <table border="1px" width="30%">
        <tr>
            <td> 图书名称:</td>
            <td><input type="text" name="name"></td>
        </tr>
        <tr>
            <td> 作者:</td>
            <td><input type="text" name="author"></td>
        </tr>
        <tr>
            <td> 图书价钱:</td>
            <td><input type="text" name="price"></td>
        </tr>
        <tr>
            <td>类型:</td>
            <td>
                <select name="category_id">
                    <c:forEach items="${list}" var="category">
                        <option value="${category.id}">${category.name}</option>
                    </c:forEach>
                </select>
            </td>
        </tr>
        <tr>
            <td> 上传图片</td>
            <td><input type="file" name="image"></td>
        </tr>
        <tr>
            <td>详细描述</td>
            <td><textarea name="description"></textarea></td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="提交">
                <input type="reset" value="重置">
            </td>
        </tr>
    </table>
</form>
处理表单数据Servlet
else if (method.equals("add")) {
            //上传文件和普通数据分割开,封装到Book对象上
            Book book = uploadData(request);
            book.setId(WebUtils.makeId());
            service.addBook(book);
            request.setAttribute("message", "添加图书成功");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }
- uploadData()方法代码
 
    private Book uploadData(HttpServletRequest request) {
        Book book = new Book();
        try{
            //1.得到解析器工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //2.得到解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //设置编码
            upload.setHeaderEncoding("UTF-8");
            //为上传表单,则调用解析器解析上传数据
            List<FileItem> list = upload.parseRequest(request);  //FileItem
            //遍历list,得到用于封装第一个上传输入项数据fileItem对象
            for(FileItem item : list){
                if(item.isFormField()){
                    //得到的是普通输入项
                    String name = item.getFieldName();  //得到输入项的名称
                    String value = item.getString("UTF-8");
                    //使用BeanUtils封装数据
                    BeanUtils.setProperty(book, name, value);
                }else{
                    //得到上传输入项
                    //得到上传文件名全路径
                    String filename = item.getName();
                    //截取文件名
                    filename = filename.substring(filename.lastIndexOf("\\")+1);
                    InputStream in = item.getInputStream();   //得到上传数据
                    int len = 0;
                    byte buffer[]= new byte[1024];
                    //如果没有这个目录,就创建它
                    String savepath = this.getServletContext().getRealPath("/image");
                    File file = new File(savepath);
                    if (!file.exists()) {
                        file.mkdir();
                    }
                    FileOutputStream out = new FileOutputStream(savepath + "\\" + filename);
                    while((len=in.read(buffer))>0){
                        out.write(buffer, 0, len);
                    }
                    //设置图片的名字
                    book.setImage(filename);
                    in.close();
                    out.close();
                    //关闭临时文件
                    item.delete();
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return book;
    }
- 效果: