Spring集成MyBatis完整示例
该文详细的通过Spring IOC、MyBatis、Servlet、Maven及Spring整合MyBatis的等技术完成一个简单的图书管理功能,实现图书列表、删除、多删除、编辑、新增功能。梳理前面学习的内容《Spring整合MyBatis(Maven+MySQL)一》与《Spring整合MyBatis(Maven+MySQL)二》,做一个完整的示例完成一个简单的图书管理功能,主要使用到的技术包含Spring、MyBatis、Maven、MySQL及简单MVC等。最后的运行效果如下所示:
项目结构如下:
一、新建一个基于Maven的Web项目
1.1、创建一个简单的Maven项目,项目信息如下:
1.2、修改层面信息,在项目上右键选择属性,再选择“Project Facets”,先设置java运行环境为1.7,先去掉"Dynamic Web Module"前的勾,然后保存关闭;再打开勾选上"Dynamic Web Module",版本选择“3.0”;这里在左下解会出现一个超链接,创建“Web Content”,完成关闭。
1.3、修改项目的部署内容。项目上右键属性,选择“Deplyment Assembly”,删除不需要发布的内容如:带“test”的两个目录,WebContent目录,再添加一个main下的webapp目录。
修改后的结果如下所示:
1.4、修改项目内容。将WebContent下的内容复制到/src/main/webapp下,再删除WebContent目录,修改后的结果如下所示:
1.5、添加“服务器运行时(Server Runtime)”,添加后的结果如下:
二、创建数据库与表
启动MySQL,创建数据库,新建表books,插入测试数据,完成后的表如下所示:
创建表的sql脚本如下:
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50536 Source Host : localhost:3306 Source Database : db1 Target Server Type : MYSQL Target Server Version : 50536 File Encoding : 65001 Date: 2016-07-06 22:05:07 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `books` -- ---------------------------- DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `title` varchar(100) NOT NULL COMMENT '书名', `price` decimal(10,2) DEFAULT NULL COMMENT '价格', `publishDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '出版日期', PRIMARY KEY (`id`), UNIQUE KEY `title` (`title`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of books -- ---------------------------- INSERT INTO `books` VALUES ('1', 'Java编程思想', '98.50', '2005-01-02 00:00:00'); INSERT INTO `books` VALUES ('2', 'HeadFirst设计模式', '55.70', '2010-11-09 00:00:00'); INSERT INTO `books` VALUES ('3', '第一行Android代码', '69.90', '2015-06-23 00:00:00'); INSERT INTO `books` VALUES ('4', 'C++编程思想', '88.50', '2004-01-09 00:00:00'); INSERT INTO `books` VALUES ('5', 'HeadFirst Java', '55.70', '2013-12-17 00:00:00'); INSERT INTO `books` VALUES ('6', '疯狂Android', '19.50', '2014-07-31 00:00:00');
需特别注意的是书名是唯一键。
三、添加依赖包
项目主要依赖的jar包有Spring核心包、Spring AOP包、MyBatis ORM包、MyBatis-Spring适配包、JSTL、JUnit、Log4j2等,具体的pom.xml文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>BookStore</artifactId> <version>0.0.1</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <!--Spring框架核心库 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- aspectJ AOP 织入器 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <!-- Spring Web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!--mybatis-spring适配器 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--Spring java数据库访问包,在本例中主要用于提供数据源 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!--mysql数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!--log4j日志包 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.6.1</version> </dependency> <!-- mybatis ORM框架 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <!--c3p0 连接池 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> </project>
如果是第一次依赖相关的包,则需要下载时间,请耐心等待,如果下载失败请手动下载后复制到本地的资源库中。依赖后的项目结果如下:
四、新建POJO实体层
为了实现与数据库中的books表进行关系映射新建一个Book类,具体代码如下:
package com.zhangguo.bookstore.entities; import java.util.Date; /** * 图书实体 */ public class Book { /** * 编号 */ private int id; /** * 书名 */ private String title; /** * 价格 */ private double price; /** * 出版日期 */ private Date publishDate; public Book(int id, String title, double price, Date publishDate) { this.id = id; this.title = title; this.price = price; this.publishDate = publishDate; } public Book() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public Date getPublishDate() { return publishDate; } public void setPublishDate(Date publishDate) { this.publishDate = publishDate; } }
五、新建MyBatis SQL映射层
这个项目中我们采用接口与xml结束的形式完成关系与对象间的映射,在接口中定义一些数据访问的方法,在xml文件中定义实现数据访问需要的sql脚本。图书数据访问映射接口如下:
package com.zhangguo.bookstore.mapper; import java.util.List; import org.apache.ibatis.annotations.Param; import com.zhangguo.bookstore.entities.Book; /** * 图书数据访问接口 */ public interface BookDAO { /** * 获得所有图书 */ public List<Book> getAllBooks(); /** * 根据图书编号获得图书对象 */ public Book getBookById(@Param("id") int id); /** * 添加图书 */ public int add(Book entity); /** * 根据图书编号删除图书 */ public int delete(int id); /** * 更新图书 */ public int update(Book entity); }
为MyBatis ORM创建的映射文件BookMapper.xml(命名尽量都遵循一个规则,便于扫描,这里约定以实体名+Mapper)如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--命名空间应该是对应接口的包名+接口名 --> <mapper namespace="com.zhangguo.bookstore.mapper.BookDAO"> <!--id应该是接口中的方法,结果类型如没有配置别名则应该使用全名称 --> <!--获得所有图书 --> <select id="getAllBooks" resultType="Book"> select id,title,price,publishDate from books </select> <!--获得图书对象通过编号 --> <select id="getBookById" resultType="Book"> select id,title,price,publishDate from books where id=#{id} </select> <!-- 增加 --> <insert id="add"> insert into books(title,price,publishDate) values(#{title},#{price},#{publishDate}) </insert> <!-- 删除 --> <delete id="delete"> delete from books where id=#{id} </delete> <!-- 更新 --> <update id="update"> update books set title=#{title},price=#{price},publishDate=#{publishDate} where id=#{id} </update> </mapper>
六、完成Spring整合MyBatis配置
6.1、在源代码的根目录下新建 db.properties文件,用于存放数据库连接信息,文件内容如下:
#mysql jdbc jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8 jdbc.uid=root jdbc.pwd=root
6.2、在源代码的根目录下新建 applicationContext.xml文件,用于整合MyBatis与Spring,该文件是整个项目的控制中心,非常关键,具体的内容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!--1 引入属性文件,在配置中占位使用 --> <context:property-placeholder location="classpath*:db.properties" /> <!--2 配置C3P0数据源 --> <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!--驱动类名 --> <property name="driverClass" value="${jdbc.driver}" /> <!-- url --> <property name="jdbcUrl" value="${jdbc.url}" /> <!-- 用户名 --> <property name="user" value="${jdbc.uid}" /> <!-- 密码 --> <property name="password" value="${jdbc.pwd}" /> <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数 --> <property name="acquireIncrement" value="5"></property> <!-- 初始连接池大小 --> <property name="initialPoolSize" value="10"></property> <!-- 连接池中连接最小个数 --> <property name="minPoolSize" value="5"></property> <!-- 连接池中连接最大个数 --> <property name="maxPoolSize" value="20"></property> </bean> <!--3 会话工厂bean sqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="datasource"></property> <!-- 别名 --> <property name="typeAliasesPackage" value="com.zhangguo.bookstore.entities"></property> <!-- sql映射文件路径 --> <property name="mapperLocations" value="classpath*:com/zhangguo/bookstore/mapper/*Mapper.xml"></property> </bean> <!--4 自动扫描对象关系映射 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定会话工厂,如果当前上下文中只定义了一个则该属性可省去 --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <!-- 指定要自动扫描接口的基础包,实现接口 --> <property name="basePackage" value="com.zhangguo.bookstore.mapper"></property> </bean> <!--5 声明式事务管理 --> <!--定义事物管理器,由spring管理事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"></property> </bean> <!--支持注解驱动的事务管理,指定事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!--6 容器自动扫描IOC组件 --> <context:component-scan base-package="com.zhangguo.bookstore"></context:component-scan> <!--7 aspectj支持自动代理实现AOP功能 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans>
共有7处配置,第7处配置非必要,另外关于事务管理可以选择AOP拦截式事务管理。
七、创建服务层
创建BookService服务类,完成图书管理业务,有些项目中也叫业务层,这里我们叫服务层,具体实现如下:
package com.zhangguo.bookstore.service; import java.util.List; import javax.annotation.Resource; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.zhangguo.bookstore.entities.Book; import com.zhangguo.bookstore.mapper.BookDAO; @Service public class BookService{ @Resource BookDAO bookdao; public List<Book> getAllBooks() { return bookdao.getAllBooks(); } public Book getBookById(int id){ return bookdao.getBookById(id); } public int add(Book entity) throws Exception { if(entity.getTitle()==null||entity.getTitle().equals("")){ throw new Exception("书名必须不为空"); } return bookdao.add(entity); } @Transactional public int add(Book entity1,Book entityBak){ int rows=0; rows=bookdao.add(entity1); rows=bookdao.add(entityBak); return rows; } public int delete(int id) { return bookdao.delete(id); } /** * 多删除 */ public int delete(String[] ids){ int rows=0; for (String idStr : ids) { int id=Integer.parseInt(idStr); rows+=delete(id); } return rows; } public int update(Book entity) { return bookdao.update(entity); } }
服务层不只是一个dao的接力棒,认为他可有可无,其实是因为我们现在的的示例中没有涉及到更多的复杂业务,所以显得比较空,实现开发可能有更多的业务逻辑要在这里处理。另外给bookdao成员变量注解为自动装配,service类注解为IOC组件。
八、JUnit测试服务类
为了确保服务类中的每个方法正确,先使用JUnit进行单元测试,测试代码如下:
package com.zhangguo.bookstore.test; import static org.junit.Assert.*; import java.util.Date; import java.util.List; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zhangguo.bookstore.entities.Book; import com.zhangguo.bookstore.service.BookService; public class TestBookService { static BookService bookservice; @BeforeClass public static void before(){ ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); bookservice=ctx.getBean(BookService.class); } @Test public void testGetAllBooks() { List<Book> books=bookservice.getAllBooks(); assertNotNull(books); } @Test public void testAdd() { Book entity=new Book(0, "Hibernate 第七版", 78.1, new Date()); try { assertEquals(1, bookservice.add(entity)); } catch (Exception e) { e.printStackTrace(); } } @Test public void testDeleteInt() { assertEquals(1, bookservice.delete(9)); } @Test public void testDeleteStringArray() { String[] ids={"7","11","12"}; assertEquals(3, bookservice.delete(ids)); } @Test public void testUpdate() { Book entity=new Book(7, "Hibernate 第二版", 79.1, new Date()); try { assertEquals(1, bookservice.update(entity)); } catch (Exception e) { e.printStackTrace(); } } @Test public void testGetBookById() { assertNotNull(bookservice.getBookById(1)); } @Test public void testAddDouble(){ //因为书名相同,添加第二本会失败,用于测试事务 Book entity1=new Book(0, "Hibernate 第八版", 78.1, new Date()); Book entity2=new Book(0, "Hibernate 第八版", 78.1, new Date()); assertEquals(2, bookservice.add(entity1, entity2)); } }
所有的测试均通过,有一个想法就是能否测试完成后数据库还原,如删除的数据在测试后不被真正删除。
九、加载Spring容器与获得容器对象
9.1、修改web.xml文件,添加加载Spring容器用的监听器,修改后的结果如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0" > <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <description>Spring容器加载监听器</description> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <description>设置Spring加载时的配置文件位置,默认位置在web-inf/lib下</description> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> </web-app>
类org.springframework.web.context.ContextLoaderListener处在Spring-web.jar包中,要记得在pom.xml中添加依赖,测试是否加载成功的简单办法是:重新启动tomcat查看控制信息。
9.2、为了方便获得Spring容器实例,定义一个CtxUtil工具类,工具类的代码如下:
package com.zhangguo.bookstore.action; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * Spring容器上下文工具类,用于获取当前的Spring容器 * 实现了接口ApplicationContextAware且该类被Spring管理 *则会自动调用setApplicationContext方法获取Spring容器对象 */ @Component public class CtxUtil implements ApplicationContextAware { public static ApplicationContext ctx; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ctx=applicationContext; } /** * 根据类型获得bean */ public static <T> T getBean(Class<T> clazz){ return ctx.getBean(clazz); } /** * 根据名称名称获得bean */ public static Object getBean(String name){ return ctx.getBean(name); } }
十、简单MVC控制器封装
为了实现一个简单的MVC基础控制器,定义了一个叫BaseController的Servlet,可以让其它的Servlet继承该Servlet获得部分MVC功能,具体代码如下:
package com.zhangguo.bookstore.action; import java.io.IOException; import java.lang.reflect.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet基类 * 自定义控制器基类 */ public class BaseController extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); // 获得要执行的方法名 String act = request.getParameter("act"); // 如果用户没有提供方法名 if (act == null || act.equals("")) { // 默认方法 act = "execute"; } // 根据方法名获得方法信息获得方法信息 Method method; try { // 在对象中获得类型信息,在类型中获得方法通过方法名,与参数类型 method = this.getClass().getMethod(act, HttpServletRequest.class, HttpServletResponse.class); // 调用方法,在当前对象中调用,传递参数request与response,获得返回结果 String targetUri = method.invoke(this, request, response) + ""; // 如果返回的url是以redirect开始,则是重定向 if (targetUri.startsWith("redirect:")) { response.sendRedirect(targetUri.substring(9, targetUri.length())); } else { // 转发 request.getRequestDispatcher(targetUri).forward(request, response); } } catch (Exception e) { response.sendError(400, e.getMessage()); e.printStackTrace(); } } public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendError(400, "请使用参数act指定您要访问的方法"); } }
十一、完成图书管理功能
11.1、定义BookController控制器
该控制器继承BaseController,中间每一个参数为(HttpServletRequest request,HttpServletResponse response)的方法都充当一个Action,代码如下:
package com.zhangguo.bookstore.action; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; 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 com.zhangguo.bookstore.entities.Book; import com.zhangguo.bookstore.service.BookService; @WebServlet("/BookController.do") public class BookController extends BaseController { private static final long serialVersionUID = 1L; BookService bookservice; @Override public void init() throws ServletException { bookservice = CtxUtil.getBean(BookService.class); } // 图书列表Action public String ListBook(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("books", bookservice.getAllBooks()); return "ListBook.jsp"; } // 删除图书Action public String Delete(HttpServletRequest request, HttpServletResponse response) { int id = Integer.parseInt(request.getParameter("id")); request.setAttribute("message", bookservice.delete(id) > 0 ? "删除成功!" : "删除失败!"); request.setAttribute("books", bookservice.getAllBooks()); return "ListBook.jsp"; } // 多删除图书Action public String Deletes(HttpServletRequest request, HttpServletResponse response) { String[] ids = request.getParameterValues("ids"); if (ids!=null&&ids.length > 0) { request.setAttribute("message", bookservice.delete(ids) > 0 ? "删除成功!" : "删除失败!"); } else { request.setAttribute("message", "请选择删除项!"); } request.setAttribute("books", bookservice.getAllBooks()); return "ListBook.jsp"; } // 添加图书Action public String AddBook(HttpServletRequest request, HttpServletResponse response) { return "AddBook.jsp"; } // 保存添加图书Action public String AddBookPost(HttpServletRequest request, HttpServletResponse response) { try { String title = request.getParameter("title"); double price = Double.parseDouble(request.getParameter("price")); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date publishDate = simpleDateFormat.parse(request.getParameter("publishDate")); Book entity = new Book(0, title, price, publishDate); if (bookservice.add(entity) > 0) { request.setAttribute("model", new Book()); request.setAttribute("message", "添加成功!"); } else { request.setAttribute("model", entity); request.setAttribute("message", "添加失败!"); } } catch (Exception exp) { request.setAttribute("message", exp.getMessage()); exp.printStackTrace(); } return "AddBook.jsp"; } //编辑图书Action public String EditBook(HttpServletRequest request, HttpServletResponse response) { int id = Integer.parseInt(request.getParameter("id")); Book model=bookservice.getBookById(id); request.setAttribute("model", model); return "EditBook.jsp"; } // 保存编辑图书Action public String EditBookPost(HttpServletRequest request, HttpServletResponse response) { try { int id=Integer.parseInt(request.getParameter("id")); String title = request.getParameter("title"); double price = Double.parseDouble(request.getParameter("price")); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date publishDate = simpleDateFormat.parse(request.getParameter("publishDate")); Book entity = new Book(id, title, price, publishDate); request.setAttribute("message", bookservice.update(entity) > 0 ? "更新成功!" : "更新失败!"); request.setAttribute("model", entity); } catch (Exception exp) { request.setAttribute("message", exp.getMessage()); exp.printStackTrace(); } return "EditBook.jsp"; } }
11.2、图书列表与删除
定义视图ListBook.jsp,用于完成图书管理,实现图书的列表、删除与多删除功能,页面脚本如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link href="styles/main.css" type="text/css" rel="stylesheet" /> <title>图书管理</title> </head> <body> <div class="main"> <h2 class="title"><span>图书管理</span></h2> <form action="BookController.do?act=Deletes" method="post"> <table border="1" width="100%" class="tab"> <tr> <th><input type="checkbox" id="chbAll"></th> <th>编号</th> <th>书名</th> <th>价格</th> <th>出版日期</th> <th>操作</th> </tr> <c:forEach var="book" items="${books}"> <tr> <th><input type="checkbox" name="ids" value="${book.id}"></th> <td>${book.id}</td> <td>${book.title}</td> <td>${book.price}</td> <td><fmt:formatDate value="${book.publishDate}" pattern="yyyy年MM月dd日"/></td> <td> <a href="BookController.do?act=Delete&id=${book.id}" class="abtn">删除</a> <a href="BookController.do?act=EditBook&id=${book.id}" class="abtn">编辑</a> </td> </tr> </c:forEach> </table> <p style="color: red">${message}</p> <p> <a href="BookController.do?act=AddBook" class="abtn">添加</a> <input type="submit" value="删除选择项" class="btn"/> </p> </form> </div> </body> </html>
运行时效果如下图所示:
11.3、新增图书功能
定义页面AddBook.jsp完成添加图书功能,在控制器中有两个Action对应新增功能,一个是AddBook,完成页面展示;另一个是AddBookPost处理保存事件,页面脚本如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link href="styles/main.css" type="text/css" rel="stylesheet" /> <title>新增图书</title> </head> <body> <div class="main"> <h2 class="title"><span>新增图书</span></h2> <form action="BookController.do?act=AddBookPost" method="post"> <fieldset> <legend>图书</legend> <p> <label for="title">图书名称:</label> <input type="text" id="title" name="title" value="${model.title}"/> </p> <p> <label for="title">图书价格:</label> <input type="text" id="price" name="price" value="${model.price}"/> </p> <p> <label for="title">出版日期:</label> <input type="text" id="publishDate" name="publishDate" value="${model.publishDate}"/> </p> <p> <input type="submit" value="保存" class="btn"> </p> </fieldset> </form> <p style="color: red">${message}</p> <p> <a href="BookController.do?act=ListBook" class="abtn">返回列表</a> </p> </div> </body> </html>
运行成功时的状态如下:
11.4、编辑图书功能
定义页面EditBook.jsp完成更新图书功能,在控制器中有两个Action对应更新功能,一个是EditBook,完成页面展示与加载要编辑图书实体的信息;另一个是EditBookPost处理保存事件,页面脚本如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link href="styles/main.css" type="text/css" rel="stylesheet" /> <title>编辑图书</title> </head> <body> <div class="main"> <h2 class="title"><span>编辑图书</span></h2> <form action="BookController.do?act=EditBookPost" method="post"> <fieldset> <legend>图书</legend> <p> <label for="title">图书名称:</label> <input type="text" id="title" name="title" value="${model.title}"/> </p> <p> <label for="title">图书价格:</label> <input type="text" id="price" name="price" value="${model.price}"/> </p> <p> <label for="title">出版日期:</label> <input type="text" id="publishDate" name="publishDate" value="<fmt:formatDate value="${model.publishDate}" pattern="yyyy-MM-dd"/>"/> </p> <p> <input type="hidden" id="id" name="id" value="${model.id}"/> <input type="submit" value="保存" class="btn"> </p> </fieldset> </form> <p style="color: red">${message}</p> <p> <a href="BookController.do?act=ListBook" class="abtn">返回列表</a> </p> </div> </body> </html>
运行时的状态如下所示:
11.5、首页与样式
定义index.jsp页面,让其转发到指定的控制器(有点类似路由功能了),页面代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:forward page="BookController.do?act=ListBook"></jsp:forward>
定义了一个简陋的样式main.css,样式表脚本如下:
@CHARSET "UTF-8"; * { margin: 0; padding: 0; font-family: microsoft yahei; font-size: 14px; } body { padding-top: 20px; } .main { width: 90%; margin: 0 auto; border: 1px solid #777; padding: 20px; } .main .title { font-size: 20px; font-weight: normal; border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 5px; color: blue; } .main .title span { display: inline-block; font-size: 20px; background : blue; color: #fff; padding: 0 8px; background: blue; } a { color: blue; text-decoration: none; } a:hover { color: orangered; } .tab td, .tab, .tab th { border: 1px solid #777; border-collapse: collapse; } .tab td, .tab th { line-height: 26px; height: 26px; padding-left: 5px; } .abtn { display: inline-block; height: 20px; line-height: 20px; background: blue; color: #fff; padding: 0 5px; } .btn { height: 20px; line-height: 20px; background: blue; color: #fff; padding: 0 8px; border:0; } .abtn:hover,.btn:hover{ background: orangered; color: #fff; } p{ padding:5px 0; } fieldset{ border: 1px solid #ccc; padding:5px 10px; } fieldset legend{ margin-left:10px; font-size:16px; }
十二、总结与示例下载
这个Demo起到了承前启后的作用,通过该示例将前面学习过的Spring IOC、MyBatis、JSP、Servlet、Maven及Spring整合MyBatis的内容进行巩固,也为后面学习Spring MVC作好了铺垫。示例中隐约的实现了一些MVC的功能,这远远不够,在URL的处理、表单验证、自动映射表单等方面还可以完善,只想有一个抛砖引玉的作用就满意了,谢谢您的阅读,谢谢!