基于SSM框架的项目:图书管理系统

     阅读:73

1 总体要求

总体任务:整合 SpringMVC + Spring + MyBatis 框架,实现书城项目的增删改查以及检索功能。

网站架构依然采用前端 JSP + 控制层 Controller + 业务层 Service + 持久层 Dao + 数据库的形式。

其中:

  • 数据库由 MySQL 管理;
  • MyBatis 作为 持久层用来操作数据库;
  • SpringMVC 作为控制层与前端就行数据交互;
  • Spring 作为大杂烩托管持久层、业务层和控制层;
  • 前端页面使用了 Bootstrap 这个 JavaScript 框架进行编写。

采用自底向上的设计流程,具体步骤如下:

  1. 数据库表的创建;
  2. 基本环境搭建;
  3. Mybatis 层编写;
  4. Spring 层编写;
  5. SpringMVC 层编写;
  6. 逻辑功能的编写。

1.1 演示视频

点击:传送门

 


2 数据库的创建

# 其中 `ssmbook` 为该项目数据库的名称,`book` 为对应的表名
CREATE DATABASE IF NOT EXISTS `ssmbook`;

USE `ssmbook`;
CREATE TABLE IF NOT EXISTS `book`(
     `bookId` INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '书id',
     `bookName` VARCHAR(100) NOT NULL COMMENT '书名',
     `bookCount` INT(11) NOT NULL COMMENT '数量',
     `detail` VARCHAR(200) NOT NULL COMMENT '描述'
)ENGINE=INNODB DEFAULT CHARSET=utf8;

USE `ssmbook`;
INSERT  INTO `book`(`bookName`,`bookCount`,`detail`)VALUES
('人生',8,'作者为路遥,描述了一个人在人生各处的选择'),
('高效能人士的七个习惯',10,'作者为史蒂芬,描述了普通人所需要的七个习惯'),
('剑指 Offer ',9,'用来刷题');

 


3 基本环境搭建

1 新建一个 Maven 普通项目,并将该项目添加 Web 支持。

2 导入相关的 Maven 依赖,对应的 pom.xml 文件为:

<?xml version="1.0" encoding="UTF-8"?>
<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>sharm</groupId>
    <artifactId>ssmbuild</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 经过实践证明,这段代码确实是必须要写的 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <!-- 导入依赖 -->
    <dependencies>
        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <!--Servlet - JSP -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

        <!--Spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>

    <!-- 静态资源导出 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

</project>

3 建立基本结构和配置框架

请添加图片描述
 


4 Mybatis 层编写

该部分主要编写底层业务逻辑。

1 编写数据库配置文件 database.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbook?useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.username=root
jdbc.password=123456

2 编写数据库对应的实体类:sharm.pojo.Book

package sharm.pojo;

public class Book {
    public int bookId;
    public String bookName;
    public int bookCount;
    public String detail;
    
    // 省略无参和有参构造方法
    // 省略各个属性的 getter 和 setter 方法
    // 省略 toString 方法的重写
}

3 编写 Dao 层的 Mapper 接口:sharm.dao.BookMapper

package sharm.dao;

import org.apache.ibatis.annotations.Param;
import sharm.pojo.Book;

import java.util.List;

/**
 * 接口约束的是方法
 */
public interface BookMapper {
    
    // 增加一本书
    public int addBook(Book book);

    // 根据 id 删除一本书
    // 好奇怪,明明对应的 sql 语句是不返回内容的,为什么还可以接收到 int 型的 1,难怪 SQL 语句执行成功会返回 1
    public int deleteBookById(int id);

    // 更新书
    public int updateBook(Book book);

    // 根据 id,返回一本书
    public Book queryBookById(int id);

    // 查询全部的书
    List<Book> queryAllBook();

    // 根据书籍名进行检索
    // 可能存在同名书籍,所以这里使用 List 进行接收
    // 可以在接口上加上 @Param 注解,这样出错的概率会小很多
    List<Book> queryBookByName(@Param("bookName") String name);
}

4 编写该接口对用的 mapper.xml 文件:sharm.dao.BookMapper.xml

<?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">
<!--关于 MyBatis 的配置文件和针对于接口的配置文件,只需要将 config 改为 mapper 即可。-->
<mapper namespace="sharm.dao.BookMapper">
    <!--增加一个Book-->
    <insert id="addBook" parameterType="book">
        INSERT INTO ssmbook.book(bookName, bookCount, detail)
        VALUES(#{bookName}, #{bookCount}, #{detail})
    </insert>

    <!--根据id删除一个Book-->
    <delete id="deleteBookById" parameterType="int">
        DELETE FROM ssmbook.book WHERE bookId=#{bookId}
    </delete>

    <!--更新Book-->
    <update id="updateBook" parameterType="book">
        UPDATE ssmbook.book
        SET bookName = #{bookName}, bookCount = #{bookCount}, detail = #{detail}
        WHERE bookId = #{bookId}
    </update>

    <!-- 根据id查询,返回一个 Book -->
    <select id="queryBookById" resultType="book">
        SELECT * FROM ssmbook.book
        WHERE bookId = #{bookId}
    </select>

    <!-- 查询全部Book -->
    <select id="queryAllBook" resultType="book">
        SELECT * FROM ssmbook.book
    </select>

    <!-- 根据书籍名查询 Book -->
    <select id="queryBookByName" parameterType="String" resultType="book">
        SELECT * FROM ssmbook.book WHERE bookName = #{bookName}
    </select>

</mapper>

5 编写 MyBatis 的核心配置文件:resources/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 增加 MyBatis 日志 -->
    <settings>
        <!-- 没想到 name 属性的值必须是 logImpl,否则就找不到  -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!-- 取别名,此时,pojo 下的类名可以用首字母小写代替 -->
    <typeAliases>
        <package name="sharm.pojo"/>
    </typeAliases>

    <mappers>
        <mapper resource="sharm/dao/BookMapper.xml"/>
    </mappers>

</configuration>

6 编写 Service 层的接口:sharm.service.BookService

package sharm.service;

import sharm.pojo.Book;

import java.util.List;

public interface BookService {
    //增加一个Book
    int addBook(Book book);
    //根据id删除一个Book
    int deleteBookById(int id);
    //更新Book
    int updateBook(Book book);
    //根据id查询,返回一个Book
    Book queryBookById(int id);
    //查询全部Book,返回list集合
    List<Book> queryAllBook();
    // 根据书籍名进行检索
    List<Book> queryBookByName(String name);
}

7 编写 Service 层的实现类:sharm.service.BookServiceImp

package sharm.service;

import sharm.dao.BookMapper;
import sharm.pojo.Book;

import java.util.List;

public class BookServiceImp implements BookService {
    //1. 一般来说 Dao 与 Service 的内容是相似的,只不过业务层可以在 Dao 层上增加一些其它的操作,类似于开闭原则;
    //2. 所以说是只将 Dao 层修改为 xml 形式,service 层还是接口加实现类的形式。

    // 调用 dao 层的操作,设置一个 set 接口,方便 Spring 管理
    private BookMapper bookMapper;
    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    @Override
    public int addBook(Book book) {
        return bookMapper.addBook(book);
    }

    @Override
    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    @Override
    public int updateBook(Book book) {
        return bookMapper.updateBook(book);
    }

    @Override
    public Book queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    @Override
    public List<Book> queryAllBook() {
        return bookMapper.queryAllBook();
    }

    @Override
    public List<Book> queryBookByName(String name) {
        return bookMapper.queryBookByName(name);
    }
}

 


5 Spring 层编写

Spring 在这里主要起到一个整合的作用。

Spring 接管 Dao 和 Service 层,同时 MyBatis 的一些配置文件,也可以在 Spring 中配置。

1 利用 Spring 整合 MyBatis 的配置文件:resources/spring-dao.xml

利用 Spring 整合 MyBatis,其中包括了原本需要在 MyBatis 配置文件中配置的数据库参数和 SqlSessionFactory对象;

<?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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 配置整合mybatis -->
    <!-- 1.关联数据库文件 -->
    <context:property-placeholder location = "classpath:database.properties"/>

    <!-- 2.数据库连接池 -->
    <!-- 数据库连接池有很多,当然也可以用 Spring 自带的,下面两种也比较常用:
        dbcp 半自动化操作 不能自动连接
        c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
        这里我们选择 c3p0。
    -->
    <bean id = "dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 数据库连接池中核心的四个属性肯定是不变的  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 那下面就是 c3p0 自己私有的属性,也可以不加 -->
        <!-- c3p0连接池的私有属性 -->
        <property name="initialPoolSize" value="10"/>
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 3.配置SqlSessionFactory对象 -->
    <bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean">
        <!-- ref是引用(指针),value是 boolean、string等,用 ref 还是 value,需要看源码里这个 name 是什么类型的 -->
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置 MyBatis 全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
    <bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="sharm.dao"/>
    </bean>

</beans>

2 利用 Spring 整合 service 层:resources/spring-service.xml

<?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:context="http://www.springframework.org/schema/context"
       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">

    <!-- 1.扫描 service 下的 bean -->
   <context:component-scan base-package="sharm.service"/>

    <!-- 2.将我们所有的业务类,注入到 Spring 中,可以通过配置实现,也可以通过注解实现,这里我们通过配置实现 -->
    <bean id = "bookServiceImp" class="sharm.service.BookServiceImp">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!-- 3.声明式事务配置 -->
    <bean id = "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 所以说 Spring 的事务支持,底层还是通过 SQL 实现的-->
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 4.设置 AOP 横切事务支持 -->
</beans>

 


6 SpringMVC 层编写

6.1 配置文件的书写

1 Web 项目的 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">

    <!-- 1.DispatchServlet 的配置 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 还需要配置一些初始参数,比如总的 xml 配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 2. 乱码过滤 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 3.Session过期时间 -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>

</web-app>

2 SpringMVC 的配置文件:resources/spring-mvc.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
   https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 1.自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="sharm.controller" />

    <!-- 2.让 Spring MVC 不处理 html、css 等静态资源,防止资源占用 -->
    <mvc:default-servlet-handler/>

    <!-- 3.支持mvc注解驱动,这样就不需要写处理器映射器和处理器适配器,
         而 annotation-driven 配置帮助我们自动完成上述两个实例的注入。-->
    <mvc:annotation-driven />

    <!-- 4.配置 jsp 显 示ViewResolver 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

3 Spring配置整合文件:resources/applicationContext.xml

将所有配置文件都注册在同一个 ApplicationContext 下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-dao.xml"/>
    <import resource="spring-service.xml"/>
    <import resource="spring-mvc.xml"/>
</beans>

6.2 逻辑功能的编写

当上述代码编写完后,接下来我们只需要关心前端页面与后端控制器之间的逻辑操作即可。

6.2.1 首页编写

文件路径:web/index.jsp

<%@ page contentType="text/html; charset=UTF-8" isELIgnored="false" language="java" %>
<html>
<head>
    <title>首页</title>
    <style type="text/css">
        a {
            text-decoration: none;
            color: black;
            font-size: 18px;
        }
        h3{
            width: 180px;
            height: 38px;
            margin: 100px auto;
            text-align: center;
            line-height: 38px;
            background: deepskyblue;
            border-radius: 4px;
        }
    </style>
</head>
<body>
<h3>
    <%-- 配置了 Tomcat 服务器后就会自动补全了 --%>
    <a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
</body>
</html>

6.2.2 逻辑功能的实现

1 显示全部书籍的前端文件:web/WEB-INF/jsp/allBook.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>书籍列表</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap 框架的 cdn -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">
    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>书籍列表 —— 显示所有书籍</small>
                </h1>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4 column">
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
        </div>

        <div class="col-md-8 column">
            <form class="form-inline" action="${pageContext.request.contextPath}/book/queryBookByName" method =
                    "post" style="float:right">
                <%-- span 标签就是用来接收传回前端的错误信息 --%>
                <span style="color:red;font-weight: bold">${error}</span>
                <input type="text" name="bookName" class="form-control" placeholder="请输入要查询的书籍的名称">
                <input type="submit" value="查询" class="btn btn-primary">
            </form>
        </div>
    </div>

    <div class="row clearfix">
        <div class="col-md-12 column">
            <table class="table table-hover table-striped">
                <thead>
                <tr>
                    <th>书籍编号</th>
                    <th>书籍名字</th>
                    <th>书籍数量</th>
                    <th>书籍详情</th>
                    <th>操作</th>
                </tr>
                </thead>

                <tbody>
                <%-- requestScope 是 EL 表达式获取到的 request 域中的数据 --%>
                <c:forEach var="book" items="${requestScope.get('books')}">
                    <tr>
                        <%-- setter 方法要和实体类中的 setter 方法一致 --%>
                        <td>${book.getBookId()}</td>
                        <td>${book.getBookName()}</td>
                        <td>${book.getBookCount()}</td>
                        <td>${book.getDetail()}</td>
                        <td>
                            <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookId()}">更改</a>
                            |
                            <a href="${pageContext.request.contextPath}/book/deleteBook?id=${book.getBookId()}">删除</a>
<%--                            <a href="#">更改</a> |--%>
<%--                            <a href="#">删除</a>--%>
                        </td>
                    </tr>
                </c:forEach>
                </tbody>
            </table>
        </div>
    </div>
</div>

2 增加书籍功能的前端文件:web/WEB-INF/jsp/addBook.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>新增书籍</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- 原来还必须引入 JQuery 这个 js 的库 -->
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript">
        // 页面加载完成之后
        $(function () {
            $("#sub_btn").click(function () {

                // 正则表达式参考:https://zhuanlan.zhihu.com/p/83080230
                var bookNameText = $("#bookName").val();
                // 只能为汉字
                var bookNamePatt = /^[\u4e00-\u9fa5]{0,}$/;
                if(!bookNamePatt.test(bookNameText)){
                    $("span.errorMsg").text("书籍名不合法,必须为汉字!");
                    return false;
                }

                var bookCountText = $("#bookCount").val();
                // 非负整数
                var bookCountPatt = /^[1-9]\d*|0$/;
                if(!bookCountPatt.test(bookCountText)){
                    $("span.errorMsg").text("书籍数量不合法,必须为非负整数!");
                    return false;
                }

                // 去掉错误信息
                $("span.errorMsg").text("");

            });

        })
    </script>
</head>
<body>
<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>新增书籍</small>
                </h1>
                <span class="errorMsg" style="color:red;font-weight: bold">
                    ${ requestScope.msg }
                </span>
            </div>
        </div>
    </div>
    <form action="${pageContext.request.contextPath}/book/addBook" method="post">
        书籍名称:<input type="text" name="bookName" id="bookName" required><br><br><br>
        书籍数量:<input type="text" name="bookCount" id="bookCount" required><br><br><br>
        书籍详情:<input type="text" name="detail" id="detail" required><br><br><br>
        <input type="submit" value="添加" id="sub_btn">
    </form>

</div>

3 更新书籍功能的前端文件:web/WEB-INF/jsp/updateBook.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>修改信息</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>修改信息</small>
                </h1>
            </div>
        </div>
    </div>

    <form action="${pageContext.request.contextPath}/book/updateBook" method="post">
        <%-- 只有在表单提交时在隐藏域中增加 bookId 信息,保存到数据库的时候才不会出错 --%>
        <%-- 不能使用 ${book.bookId()} 来获得数据,因为 bookId 是私有变量  --%>
        <input type="hidden" name="bookId" value="${book.getBookId()}"/>
        书籍名称:<input type="text" name="bookName" value="${book.getBookName()}"/>
        书籍数量:<input type="text" name="bookCount" value="${book.getBookCount()}"/>
        书籍详情:<input type="text" name="detail" value="${book.getDetail() }"/>
        <input type="submit" value="提交"/>
    </form>

</div>

4 后端控制器对应的逻辑为:sharm.controller.BookController

package sharm.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import sharm.pojo.Book;
import sharm.service.BookService;

import java.util.List;

@Controller
@RequestMapping("/book")
public class BookController {
    // Controller 层调用 Service 层
    @Autowired
    // 这里实现类要和 spring-service.xml 配置的内容对应
    @Qualifier("bookServiceImp")
    private BookService bookService;

    // 1.查询全部的数据,并且返回一个书籍展示页面
    @RequestMapping("/allBook")
    public String list(Model model){
        List<Book> books = bookService.queryAllBook();
        model.addAttribute("books", books);
        return "allBook";
    }

    // 2.添加书籍
    // 2.1 跳转到添加书籍页面:web-inf下的文件不能直接访问,因此需要跳转
    @RequestMapping("/toAddBook")
    public String toAddPaper() {
        // 转发,用的还是 /toAddBook 这个 url
        return "addBook";
    }
    // 上面这个 "addBook" 的 url 是 http://localhost:8080/ssmbuild_war_exploded/addBook
    // 下面这个 "addBook" 的 url 是 http://localhost:8080/ssmbuild_war_exploded/book/addBook
    // 2.2 修改页面
    @RequestMapping("/addBook")
    public String addPaper(Book book) {
        bookService.addBook(book);
        return "redirect:allBook";
    }

    // 3.修改书籍
    // 3.1 跳转到修改书籍页面
    @RequestMapping("/toUpdateBook")
    public String toUpdateBook(Model model, int id) {
        Book book = bookService.queryBookById(id);
        model.addAttribute("book", book);
        return "updateBook";
    }

    @RequestMapping("/updateBook")
    public String updateBook(Book book) {
        System.out.println(book);
        int i = bookService.updateBook(book);
        if(i == 1){
            System.out.println("修改数据成功");
        }
        return "redirect:allBook";
    }

    // 4.删除数据
    // 4.1 跳转到修改书籍页面
    @RequestMapping("/deleteBook")
    public String deleteBook(int id) {
        int i = bookService.deleteBookById(id);
        if(i == 1){
            System.out.println("删除成功。");
        }
        return "redirect:allBook";
    }

    // 5.根据书籍名检索信息(拓展功能)
    @RequestMapping("/queryBookByName")
    // 如果需要将数据往下传的话,需要来一个 model
    public String queryBookByName(@RequestParam("bookName")String name, Model model) {
        // 由于是新增的功能,且没有使用切面编程,因此需要自底向上进行编写
        List<Book> books = bookService.queryBookByName(name);
        assert books != null;
        if(books.isEmpty()){
            System.out.println("未查到,未查到,未查到");
            model.addAttribute("error", "未查到该书籍");
        }else{
            model.addAttribute("books", books);
        }
        return "allBook";
    }
}

6.2.3 修改书籍功能实现的流程

由于代码部分增删改查的代码糅合在一起了,因此专门拿出修改书籍功能的实现来介绍前后端联调的流程

  1. allBook 页面点击修改时,首先转到控制器的 /book/toUpdateBook 页面,同时将对应的 Id 也上传上去;
  2. 该方法将对应的书籍保存到 model 域中,并且将页面转发给 /updateBook,由于是转发,所以浏览器显示的 url 是 /book/toUpdateBook;
  3. updateBook 页面根据域中的信息显示书籍;用户根据这些原始数据来修改,并且再次提交表单给控制器的 /book/updateBook;
  4. 该方法对数据库进行操作,并且重定向到 /allBook 页面进行显示;

6.2.4 查询书籍的测试代码

当前端页面无法显示书籍列表时,通过后端测试来排除错误。

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sharm.pojo.Book;
import sharm.service.BookService;

public class MyTest {
    @Test
    public void test1(){
        // 对配置文件建立一个对象
        // 查询书籍
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookServiceImp = (BookService) context.getBean("bookServiceImp");
        for (Book books : bookServiceImp.queryAllBook()) {
            System.out.println(books);
        }
    }
}

6.3 该项目全部文件的位置

请添加图片描述
 


7 遇到的问题以及拓展的功能

1 在实现增加书籍功能时,为什么不直接加载到添加数据页面,而是通过 DispatchedServlet 重定向到增加书籍页面?

因为 web-inf 下的文件不能直接访问,直接访问会造成可以在未登录状态下跳转进去修改数据库,从而造成安全问题 。

2 SQL 日志功能的实现

在 resources/mybatis-config.xml 中加入如下配置即可实现:

<!-- 增加 MyBatis 日志 -->
<settings>
   <!-- 没想到 name 属性的值必须是 logImpl,否则就找不到  -->
   <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

3 数据库字段名和实体类属性名的映射问题

当实体类的属性为 bookCount,数据库表中的字段名为 bookCounts 时,尽管在 BookMapper.xml 中的语句为:

<!--更新Book-->
<update id="updateBook" parameterType="book">
    UPDATE ssmbook.book
    SET bookName = #{bookName}, bookCounts = #{bookCount}, detail = #{detail}
    WHERE bookId = #{bookId}
</update>

其中:bookCounts = #{bookCount},然而在实际使用中无法修改数据库对应字段的值,且不会报错!!!

将两者的名称设置相同,即解决。

4 为什么在 Maven 的配置文件中需要将静态资源导出的代码也写进去

标准的 Maven 项目都会有一个 resources 目录来存放我们所有的资源配置文件,但是我们往往在项目中不仅仅会把所有的资源配置文件都放在resources中,同时我们也有可能放在项目中的其他位置,比如说在使用MyBatis 框架时,往往会将 mapper.xml 与dao 接口一起放在 dao 包中,那么执行程序的时候,其中的 xml 配置文件就一定会读取失败,不会生成到 maven 的 target 目录中,所以建议是每新建一个 Maven 项目,就把静态资源到处设置进去。

5 IDEA报错:不再支持源选项 5 请使用 6 或更高版本。怎么解决

参考文献:传送门 https://cloud.tencent.com/developer/article/1587537。

其实就是在 pom.xml 中加入 java 版本的说明

6 数据库连接失败时,可能的解决办法

将数据库连接属性中的 useSSL = true 改为 false 即连接成功;

7 需要注意的地方

  1. 默认作用域确实是同包内有效,如果同个包内,那么还是可以直接使用的;
  2. 老是将代码写在 main 方法之外,导致每次都花费时间找出这个错误;
  3. 打包项目时,Artifacts 中的 lib 目录又忘记添加了。