阅读:73
总体任务:整合 SpringMVC + Spring + MyBatis 框架,实现书城项目的增删改查以及检索功能。
网站架构依然采用前端 JSP + 控制层 Controller + 业务层 Service + 持久层 Dao + 数据库的形式。
其中:
采用自底向上的设计流程,具体步骤如下:
点击:传送门
# 其中 `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,'用来刷题');
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 建立基本结构和配置框架
该部分主要编写底层业务逻辑。
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);
}
}
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>
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>
当上述代码编写完后,接下来我们只需要关心前端页面与后端控制器之间的逻辑操作即可。
文件路径: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>
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";
}
}
由于代码部分增删改查的代码糅合在一起了,因此专门拿出修改书籍功能的实现来介绍前后端联调的流程。
当前端页面无法显示书籍列表时,通过后端测试来排除错误。
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);
}
}
}
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 需要注意的地方