一个图书管理小项目:

定义对应的表结构,为了学习所以才添加大量的 SQL 规则,要记得针对货币的处理方案
create table if not exists tbl_books(
id bigint primary key auto_increment,
 book_name varchar(32) not null,
 book_price numeric(8,2) default 0,
 pub_date timestamp default current_timestamp
)engine=innodb default charset utf8;
 
使用 MyBatis 的反向引擎执行反向映射,生成对应的实体类、映射元文件和对应的映射接口
修改实体类的定义
@Data
public class Book implements Serializable{
    private Long id;
    private String bookName;
    private BigDecimal bookPrice;
    private Data pubData;
}
 
修改映射接口,首先添加一个通用的映射接口 BaseMapper
public interface BaseMapper <T extends Serializable, ID extends Serializable>{
    int deleteByPrimaryKey(ID id);
    int insertSelective(T row);
    T selectByPrimaryKey(ID id);
    int updateByPrimaryKeySelective(T row);
    //具体的分页查询考虑使用PogeHelper分页插件实现
    List<T> selectByExample(T row);
}
 
定义 BookMapper 接口,要求继承于 BaseMapper。一般在 BaseMapper 接口中定义通用方法,特殊需求的方法定义在对应的 Mapper 接口中,例如 BookMapper
public interface BookMapper extends BaseMapper<Book,Long>{
    
}
 
修改 mapper.xml 文件添加对应的 selectByExample 方法对应的 sql 语句
<select id = "selectByExample" parameterType="com.ma.entity.Book" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from tbl_books where 1 = 1;
    <if test = "bookName != null">
        and book_name like #{bookName,jdbcType = VARCHAP}
    </if>
    <if test = "bookPrice != null">
        and book_price like #{bookPrice,jdbcType = DECIMAL}
    </if>
    <if test = "pubData != null">
        and pub_data like #{pubData,jdbcType = DATE}
    </if>
    <if test = "id != null">
        and id like #{id,jdbcType = BIGINT}
    </if>
</select>
 
=Mybatis 持久层========
添加父容器,一般来说子容器中只包含 web 应用相关的配置,其它内容定义在父容器中
Resources/applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns="http://www.springframework.org/schema/beans"
       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.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" p:driverClassName="com.mysql.cj.jdbc.Driver"
          p:url="jdbc:mysql:///test?serverTimezone=UTC" p:username="root" p:password="10086" destroy-method="close"/>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource"
          p:mapperLocations="classpath:mapper/*.xml"/>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:sqlSessionFactoryBeanName="sqlSessionFactory"
          p:basePackage="com.yan.dao"/>
    <!-- 打开Spring的自动组件扫描,一般需要引入context名空间  -->
    <context:component-scan base-package="com.ma.biz"/>
    <!-- 启动声明式事务管理,一般需要引入tx名空间 -->
    <tx:annotation-driven/>  
<!-- 打开声明式事务管理的注解支持,在具体应用中一般是在业务类上添加注解针对事务进行说明 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
</beans>
 
添加业务接口
public interface IBookServ{
    boolean create(Book book);
}
 
添加对应的业务实现,需要考虑事务的问题
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@Service
public class BookMapper bookMapper;
    @Autowired
    private BookMapper bookMapper;
    @Transactional(readOnly = false,propagation = Propagation.REQUIRED)
    public boolean create(Book book){
    //Spring提供的工具类,一般用于业务数据校验,如果不满足条件则会抛出运行时异常。
    //notnull是条件,book参数用于判断notnull,参数2是报错信息
    Assert.notNull(book,"参数不允许为空");
    Assert.notNull(book.getBookName(),"数据名称不允许为空");
    int res = bookMapper.insertSelective(book);
    return res > 0;
}
 
添加控制器
pom.xml 打包方式修改为 war
<groupId>com.ma</groupId>
<artifactId>demo0710</artifactId>
<version>1.0-SNAPSHDT</version>
<packaging>war</packaging>
 
添加文件夹 webapp 以及 WEB-INF

配置 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- spring整合web应用还需要一个ContextLoaderLister复杂初始化IoC容器,并存储在application对象种 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 配置前端控制器,通过前端控制器可以将SpringMVC框架添加到web应用 -->
    <servlet>
        <!-- 配置的前端控制器名称,对应的默认配置文件位于WEB-INF目录下,名称为yan-servlet.xml -->
        <servlet-name>ma</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!-- 后缀为.do的请求将触发前端控制器的执行 -->
        <servlet-name>ma</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!-- 涉及一个父子容器的问题,ContextLoaderListener负责初始化一个IoC容器,这个叫做父容器;
        DispatcherServlet还要初始化一个IoC容器,这个叫做子容器;
        一般情况下,子容器可以直接访问父容器种的所有受管bean,而父容器无法访问子容器 -->
</web-app>
 
添加子容器配置/WEB-INF/ma-servlet.xml,实际上这个配置文件名称可以不遵守,目前讲究惯例优于配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 引入mvc名空间以简化SpringMVC相关的配置 -->
    <mvc:annotation-driven/>  <!--打开SpringMVC的注解开发支持  -->
    <!-- 使用自动组件扫描将控制器类添加到IoC容器 -->
    <context:component-scan base-package="com.yan.action"/>
    <!--配置视图解析器,不需要配置handlerMapping处理映射器和handlerAdapter处理适配器,因为有默认的两个组件配置 -->
    <!-- 视图解析器并不在编程中直接使用,所以没有id值;prefix为前缀配置,suffix为后缀配置,例如ModelAndView中视图的逻辑名称为abc,则对应的
    物理地址为 prefix+逻辑视图名+suffix,例如/WEB-INF/content/abc.jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/content/"
          p:suffix=".jsp" p:viewClass="org.springframework.web.servlet.view.JstlView"/>
</beans>
 
使用 get 请求访问/books/add 则自动打开输入数据的页面,点击提交按钮,则以 post 提交的方式插入数据
@Contraller
@RequestMapping("/books")
public class BookContraller{
    @GetMapping("/add")
    public String add(){
        return "add";
    }
}
 
根据视图解析规则定义对应的 jsp 页面,位置为/WEB-INF/content/add.jspSpringMVC 为了简化 jsp 页面的编写,引入了自定义标签库,另外还有 JSTL 标签库【java 标准标签库】
1、在 jsp 页面中需要使用标签库,必须先引入标签库
<%@ taglib prefix=“form” uri=“http://www.springframework.org/tags/form” %> 这是 SpringMVC 针对页面的 form 表单提交和报错的标签库
<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %> 这是 SUN 提供的 JSTL 标准标签库的核心标签。需要在 pom.xml 中添加依赖,否则会报错
2、定义页面
要求打开<form:form>时必须有一个对应的值 bean 用于接受和显示默认的数据
@Contraller
@RequestMapping("/books")
public class BookContraller{
    @GetMapping("/add")
    public String add(Model model){
        Book book = new Book();
        model.addAttribute("book",book);
        return "add";
    }
}
 
对应页面
<form:form action="add" modelAttribute="book">
    <form:label path="bookName">书籍名称:</from:label>
    <form:input path="bookName"/>
</form:form>
 
注意:名称对应 book
<form:form action="add" modelAttribute="book">
    <table>
        <tr>
            <td><form:label path="bookName">书籍名称:</from:label></td>
            <td><form:input path="bookName"/></td>
        </tr>
        <tr>
            <td><form:label path="bookPrice">书籍价格:</from:label></td>
            <td><form:input path="bookPrice"/></td>
        </tr>
        <tr>
            <td><form:label path="pubDate">出版日期:</from:label></td>
            <td><form:input path="pubDate"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="保存"/>
            </td>
        </tr>
    </table>
</form:form>
 
下一步:应该定义方法用于接受用户输入的数据
@Autowired
private IBookServ bookService;
@PostMapping("add")
public String add(Book book){
    //接收数据
    //调用业务方法执行数据入库
    bookService.create(book);
    //重定向列显示页面
    return "redirect:show";
}
@RequestMapping("/show")
public String show(){
    System.out.println("show");
    return"show";
}
@InitBinder
public void initBinder(WedDataBinder binder){
    binder.registerCustomEditor(Data.class,new CustomDataEditor(
        new SimpleDateFormat("yyyy-mm-dd"),true));
}
 
显示所有图书信息,下一步在考虑分页
控制器调用业务获取所有图书信息,并存储在 request 中转向到页面显示
@RequestMapping ("/show")
public String show(Model model){
    List<Book> bookList=bookService.getAllBooks();
    model.addAttribute("bookList",bookList);
    return "show";
}
 
由于返回 String 类型的 show,则需要定义页面,从 requet 中获取 bookList 并使用 table 进行迭代显示

在/WEB-INF/content/show.jsp 页面执行数据显示
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix = "fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1">
    <thead>
        <tr><td> </td>
            <th>编号</th><th>书名</th><th>价格</th><th>出版日期</th><th>操作</th></tr>
    </thead>
    <tbody>
    <c:forEath item="${bookList}" var = "book">
        <tr>
            <td></td>
            <td>${book.id}</td>
            <td>${book.bookName}</td>
            <td>${book.bookPrice}</td>
            <td><fmt:formatDate value="${book.pubDate}" patten="yyyy年M月d日"/></td>
            <td>删除 编辑</td>
        </tr>
    </c:forEach>
    </tbody>
</table>
</body>
</html>
 
实际上针对客户段提交的数据需要在接受到数据后进行服务器端数据校验
在 SpringMVC 中可以使用注解的方式针对客户端提交数据进行校验
1、添加依赖 hibernate-validator
2、在 ma-servlet.xml 中配置校验器
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator">
    </property>  
</bean>
 
校验器注入到处理器适配器中
这段配置 validator 属性意思是:在调用 controller 方法之后,马上 让数据校验 bean 开始工作,对数据进行校验
<mvc:annotation-driven validator="validator">
 
3、在接受客户端提交数据的值 bean 中使用注解添加验证规则
4、在控制器方法中接收数据的值 bean 前添加注解@Validated,并紧邻一 Errors/BindingResult 类型参数,用于接收校验中的报错信息
提交数据后执行方法 前会自动进行数据校验,如果校验中有报错信息则自动填充到 errors 中,但是不会自动跳转页面,需要编码判断进行页面跳转
@PostMapping("/add")
public String add(@Validate Book book,Errors errors){
    //接收数据
    if(errors.hasError()){  //判断是否有报错信息
        return "add";//如果有报错信息则返回输入界面进行报错显示
    }
    //调用业务方法执行数据入库
    bookService.create(book);
    //重定向到显示界面
    return "redirect:show";
}
 
5、在页面上显示报错信息
<td><form:label path="bookName">书籍名称:</form:label></td>
<td><form:input path="bookName"/>
<form:errors path="bookName"/>
</td>
 
对应的报错显示

使用 InitBinder 来处理 Date 类型的参数
@RequestMapping("/date")
public String date(Date date){
    System.out.println(date);
    return "hello"; 
}
@InitBinder
public void initBinder(ServletRequestDataBinder binder){
    binder.registerCustomEditor(Date.class, new CustomDateEditor(new
SimpleDateFormat("yyyy-MM-dd"), true));
}
 
系统预定义的方法 registerCustomEditor 用于针对特定数据类型的格式转换,参数 1 为目标类型 String–>Date,参数 2 为 SpringMVC 提供的预定义转换器,其中的参数为日期类型的格式
自动匹配参数
@RequestMapping("/person")
public String toPerson(String name, double age){
    System.out.println(name+" "+age);
    return "hello";
}
//使用@RequestParam 注解指定参数的 name
@RequestMapping(value="/param")
public String testRequestParam(@RequestParam(value="id") Integer id, @RequestParam(value="name") String name){
    System.out.println(id+" "+name);
    return "/hello";
}
                


















