目录
我们使用 SpringMVC 和 MyBatis 框架做一个简单的签到程序。我们前端使用的是 ajax 向后台传值,后台 SpringMVC 采用注解的方式进行开发(使用注解开发比较方便),最终的签到数据会通过 MyBatis 框架存入到 MySQL 数据库中。
环境搭建
数据库
我们这里使用的是MySQL数据库进行最终的存值。需要先新建名为 task 数据库(注意查看是否已经存在该数据库),在 task 数据库中新建表名为 sign 数据表,随后在表中适当添加数据。
创建表和添加数据的 SQL 文件如下所示。可以将一下SQL语句放在一个 .sql 文件中,然后选择数据库运行 SQL 文件即可。
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `sign`
-- ----------------------------
DROP TABLE IF EXISTS `sign`;
CREATE TABLE `sign` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '唯一编号,没有实际意义',
`name` varchar(255) DEFAULT NULL COMMENT '学生的姓名',
`sno` varchar(255) DEFAULT NULL COMMENT '学生的学号',
`classnumber` varchar(255) DEFAULT NULL COMMENT '班级编号',
`cours` varchar(255) DEFAULT NULL COMMENT '课程名称',
`datatime` varchar(255) DEFAULT NULL COMMENT '签到时间',
`num` varchar(255) DEFAULT NULL COMMENT '座位号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sign
-- ----------------------------
INSERT INTO `sign` VALUES ('11', '张三', '101', '电竞02', '意识练习', '8:20', '12');
INSERT INTO `sign` VALUES ('12', '李四', '102', '电竞01', '手法练习', '8:22', '13');
INSERT INTO `sign` VALUES ('13', '王五', '103', '电竞01', '手法练习', '8:21', '12');
INSERT INTO `sign` VALUES ('14', '赵六', '104', '电竞02', '意识练习', '8:21', '15');
创建完成后如图所示。
创建 Maven 项目
使用IDEA创建Maven项目,选择自己需要的JDK版本,进入下一步。我们这里推荐使用1.8版本的 JDK。
填写项目的名称和项目的存放地址,点击 Finish 即可完成 Mavne 项目的创建。
添加框架支持。需要将普通的 Maven 工程升级为 Web 工程。右键点击模块。选择 Add Framework Support 进行添加 web 框架。
选择 Web Application 点击 OK ,即可添加 Web 框架支持。
配置项目
我们需要在 pom.xml 配置文件中添加需要的依赖代码。我们需要添加 SpringMVC、mysql、MyBatis、json、junit 等框架的依赖。依赖代码如下所示。
<dependencies>
<!--引入 SpringMVC(Spring Web MVC) 的框架依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--引入 MySQL 数据库的 JDBC 依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--引入 Mybatis 框架依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--json依赖-->
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
<!-- junit单元测试-->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--在build中配置resources,来防止资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
我们通过 MyBatis 框架来连接 MySQL 数据库。我们先添加文件名为 mybatis-config.xml 的 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>
<!--引入外部配置文件-->
<properties resource="databaseconfig.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kid/serviceimpl/SignServiceImpl.xml"/>
</mappers>
</configuration>
我们还需要在 resources 包下创建一个 databaseconfig.properties 资源文件,用来保存数据库的连接信息。(注意,需要替换为自己的信息,我的密码为空,那 password 这一项就空着,没有空格)
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/task?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=root
password=
我们还需要在 resources 包下创建 springmvc-config.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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kid.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler/>
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven/>
</beans>
我们需要在 web.xml 配置文件中,注册 SpringMVC 的 DispatcherServlet,即视图解析器。DispatcherServlet 本质上还是一个 Servlet。我们这里直接添加了 SpringMVC 官方的编码过滤器,可以解决大部分乱码问题。(添加代码后可能会出现爆红情况,那是因为我们没有创建 springmvc-servlet.xml 配置文件,这里先不需要处理爆红错误,我们后续会进行创建的对应的配置文件的。)
<!--1.注册servlet-->
<!--DispatcherServlet实际上就是一个Servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--SpringMVC官方的乱码过滤器,可以解决大部分的乱码问题-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
编写项目
创建包结构
我们按照 MVC 分层编码规范进行创建包结构。在 java 包下创建 com 包,在 com 包下创建 kid 包,在 kid 包下创建 controller、pojo、service、serviceimpl、util 包。其中,pojo 包中存放的是需要用到的实体类;util 包中存放的是 MyBatis 的工具类,可以方便对数据库的操作;service 包中存放的是服务接口,即定义可能需要用到的所有功能的接口;serviceimpl 包中存放的是接口的实现类,我们这里使用的 MyBatis 框架没有实现类,使用的是配置文件与接口进行一一对应;controller 包中存放的是控制类,用来接收前端传过来的值,调用服务接口。包结构示例如图所示。
编写后端代码
pojo 包
pojo 包中存放的是需要用到的实体类。我们只是完成较为简单的签到功能,所以只需要创建一个 Sign 类即可。创建 Sign 类。
package com.kid.pojo;
/**
* 学生签到的实体类
*/
public class Sign {
private Integer id;//唯一编号,没有什么实际意义
private String name;//姓名
private String sno;//学号
private String classnumber;//班级编号
private String cours;//课程名称
private String datatime;//签到时间
private String num;//座位号
public Sign() {
}
public Sign(Integer id, String name, String sno, String classNumber, String cours, String datatime, String num) {
this.id = id;
this.name = name;
this.sno = sno;
this.classnumber = classNumber;
this.cours = cours;
this.datatime = datatime;
this.num = num;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getClassnumber() {
return classnumber;
}
public void setClassnumber(String classnumber) {
this.classnumber = classnumber;
}
public String getCours() {
return cours;
}
public void setCours(String cours) {
this.cours = cours;
}
public String getDatatime() {
return datatime;
}
public void setDatatime(String datatime) {
this.datatime = datatime;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
@Override
public String toString() {
return "Sign{" +
"id=" + id +
", name='" + name + '\'' +
", sno='" + sno + '\'' +
", classNumber='" + classnumber + '\'' +
", cours='" + cours + '\'' +
", datatime='" + datatime + '\'' +
", num='" + num + '\'' +
'}';
}
}
util 包
util 包中存放的是工具类,存放的是 MyBatis 框架连接 MySQL 数据库的工具类,用于获取 SqlSession 对象和释放 SqlSession 对象。创建 MyBatisUtil 类。
package com.kid.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* Mybatis 的工具类,可以用于获取和释放 SqlSession 对象
*/
public class MyBatisUtil {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
SqlSessionFactory sqlSessionFactory = null;
SqlSession sqlSession = null;
{
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过 SqlSessionFactory 类可以获取 SqlSession 的对象
* @return SqlSession 的对象
*/
public SqlSession getSqlSession() {
return sqlSession = sqlSessionFactory.openSession(true);
}
/**
* 关闭 SqlSession 对象
*/
public void closeSession() {
sqlSession.close();
}
}
service 包
serviceinpl 包中存放的是具体业务的接口,学生签到过程中,需要向数据库中添加签到信息,可以查询所有的签到信息,也可以查看某一条详细的签到信息(功能尚不完善,后续会在 GitHub 中进行更新)。创建 SignService 接口。
package com.kid.service;
import com.kid.pojo.Sign;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 学生签到业务的接口
*/
public interface SignService {
//查找所有签到
public List<Sign> queryAll();
//根据唯一编号查找签到信息
public Sign queryOne(@Param("id") Integer id);
//添加一个签到信息
public void addSign(Sign sign);
}
serviceimpl 包
serviceimpl 包中存放的是接口的实现类,但是 MyBatis 框架使用的不是实现类,使用的是与接口对应的配置文件。创建 SignServiceImpl.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">
<mapper namespace="com.kid.service.SignService">
<!--查找所有的签到信息-->
<select id="queryAll" resultType="com.kid.pojo.Sign">
select *
from sign
</select>
<!--根据唯一编号id进行查找签到信息-->
<select id="queryOne" resultType="com.kid.pojo.Sign">
select *
from sign
where #{id} = id
</select>
<!--添加签到信息-->
<insert id="addSign" parameterType="com.kid.pojo.Sign">
insert into sign (name, sno, classnumber, cours, datatime, num)
values (#{name}, #{sno}, #{classnumber}, #{cours}, #{datatime}, #{num})
</insert>
</mapper>
controller 包
controller 包中存放的是控制类,接收前端学生的签到信息,然后调用服务接口,将信息存入到数据库中。创建 SignController 类。
package com.kid.controller;
import com.kid.util.MyBatisUtil;
import com.kid.pojo.Sign;
import com.kid.service.SignService;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class SignController extends MyBatisUtil {
SignService signService = null;
//不会走视图解析器
@ResponseBody
//请求地址
@RequestMapping(value = "/add", produces = "text/html;charset=UTF-8;")
public String add(Sign sign) {
signService = getSqlSession().getMapper(SignService.class);
System.out.println("这里是SignController类中的add方法");//测试信息
System.out.println("sign = " + sign);//测试信息
signService.addSign(sign);
closeSession();
return "1";
}
//不走视图解析器
@ResponseBody
//请求地址
@RequestMapping(value = "/queryAll", produces = "text/html;charset=UTF-8;")
public String queryAll() {
//测试信息
System.out.println("这里是SignController类中的queryAll方法");
signService = getSqlSession().getMapper(SignService.class);
List<Sign> signList = signService.queryAll();
signList.toString();
JSONArray jsonArray = new JSONArray(signList);
closeSession();
return jsonArray.toString();
}
@ResponseBody
@RequestMapping(value = "queryOne", produces = "text/html;charset=UTF-8;")
public String queryOne(String index) {
//测试信息
System.out.println("这里是SignController类中的queryOne方法");
System.out.println("index = " + index);
signService = getSqlSession().getMapper(SignService.class);
Sign sign = signService.queryOne(Integer.parseInt(index));
System.out.println("sign = " + sign);
JSONObject jsonObject = new JSONObject(sign);
return jsonObject.toString();
}
}
创建前端显示页面
我们在 web 包下创建 html 包。包结构如下图所示(没有 css 文件和 js 文件不影响功能的实现)。
创建 input.html 文件,添加登录信息。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>签到系统</title>
<link href="css/style.css" rel="stylesheet"/>
<link href="css/response.css" rel="stylesheet"/>
</head>
<body class="bg">
<div class="content">
<div class="header">
<div class="header-nei">
<h2 class="header-title">签到</h2>
</div>
</div>
<!--head/-->
<div class="head-height"> </div>
<div class="login-reg" style="padding-top:16px;">
<form id="form1">
<div class="log-reg-list"><input type="text" name="name" class="log-reg-text2 order-txm"
placeholder="请输入姓名"/><span>*必填</span></div>
<div class="log-reg-list"><input type="text" name="sno" class="log-reg-text2 order-sj"
placeholder="请输入学号"/><span>*</span></div>
<div class="log-reg-list"><input type="text" name="classnumber" class="log-reg-text2 order-phone"
placeholder="请输入班级"/><span>*</span></div>
<div class="log-reg-list"><input type="text" name="cours" class="log-reg-text2 order-sj-name"
placeholder="课程名称"/><span>*</span></div>
<div class="log-reg-list"><input type="text" name="datatime" class="log-reg-text2 order-user"
placeholder="上课时间"/><span>*</span></div>
<div class="log-reg-list"><input type="text" name="num" class="log-reg-text2 order-phone"
placeholder="座位号"/><span>*</span></div>
</form>
<div class="log-reg-sub">
<a href="javascript:tijiao();" class="log-reg-btn">提交</a>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script src="js/style.js"></script>
</body>
<!--jquery 轻量级 前端框架 ajax 异步请求 结合strut2(spring mvc) -->
<!--json 对象 -->
<script type="text/javascript">
function tijiao() {
$.ajax({
type: "post",
url: "/Task4_war_exploded/add",
data: $('#form1').serialize(),
dataType: "json",
success: function (res) {
if (res == 1) {
alert("签到成功!");
window.location.href = "order.html";
} else {
alert("签到失败!!!");
}
},
error: function (data) {
alert("系统出现异常!!!!");
}
});
}
</script>
</html>
创建 order.html 文件,用来显示全部信息。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>信息录入系统</title>
<link href="css/style.css" rel="stylesheet"/>
<link href="css/response.css" rel="stylesheet"/>
</head>
<body class="bg">
<div class="content">
<div class="header">
<div class="header-nei">
<a href="javascript:;" onclick="javascript:history.back(-1);" class="back-up"><i
class="iconfont"></i></a>
<h2 class="header-title">定单列表</h2>
</div>
</div>
<!--head/-->
<div class="head-height"> </div>
<div class="nav" id="nav"></div>
<div class="loading" id="morediv"><a href="javascript:more()">加载全部信息...</a></div>
</div>
<!--content/-->
<div class="bg100"></div>
<script src="js/jquery.min.js"></script>
<script src="js/style.js"></script>
</body>
<!-- 显示功能页面,页面加载的时候就应该触发ajax事件 -->
<script type="text/javascript">
var _dl = '';
var data;
var flag = false;
$(function () {
//查询的ajax
$.ajax({
type: "post",
url: "/Task4_war_exploded/queryAll",
data: $('#form').serialize(),
dataType: "json",
success: function (res) {
data = res;//将查询到的数据扩大作用域,以便查看更多使用
//所有的数据都在res中 json数组
//query 如何遍历json数组
if (res != null && res.length > 0 && flag == false) {
flag = true;
// var _dl = '';
$("#nav").empty(); //先清空列表
$.each(res, function (i, v) { //每循环一次数组的时候,拼接一条html
if (i <= 5) {
_dl = _dl + //替换数组中想要显示的数据
'<dl>' +
'<a href="order-info.html?' + v.id + '">' +
'<dt>' + (i + 1) + '</dt>' +
'<dd>' + v.cours + ' ' + v.datatime + '</dd>' +
'<i class="iconfont"></i>' +
'<div class="clears"></div>' +
'</a>' +
'</dl>';
}
});
$("#nav").append(_dl); //在循环之后将拼接后的html追加到根节点(列表)
}
},
error: function (res) {
alert("系统异常");
}
});
})
function more() {
$("#nav").empty(); //先清空列表
$.each(data, function (i, v) { //每循环一次数组的时候,拼接一条html
if (i > 5) {
_dl = _dl + //替换数组中想要显示的数据
'<dl>' +
'<a href="order-info.html?' + v.id + '">' +
'<dt>' + (i + 1) + '</dt>' +
'<dd>' + v.cours + ' ' + v.datatime + '</dd>' +
'<i class="iconfont"></i>' +
'<div class="clears"></div>' +
'</a>' +
'</dl>';
}
});
$("#nav").append(_dl); //在循环之后将拼接后的html追加到根节点(列表)
document.getElementById("morediv").style.display = "none";//全部显示完以后,隐藏显示更多按钮
}
</script>
</html>
创建 order-info.html 文件,用来显示详细信息。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>信息录入系统</title>
<link href="css/style.css" rel="stylesheet"/>
<link href="css/response.css" rel="stylesheet"/>
</head>
<body class="bg">
<div class="content">
<div class="header">
<div class="header-nei">
<a href="javascript:;" onclick="javascript:history.back(-1);" class="back-up"><i
class="iconfont"></i></a>
<h2 class="header-title">签到详情</h2>
</div>
</div>
<!--head/-->
<div class="head-height"> </div>
<div class="order-info">
<table>
<tr>
<th>姓名</th>
<td id="name">正在加载</td>
</tr>
<tr>
<th>学号</th>
<td id="sno">正在加载</td>
</tr>
<tr>
<th>班级</th>
<td id="classnumber">正在加载</td>
</tr>
<tr>
<th>课程</th>
<td id="cours">正在加载</td>
</tr>
<tr>
<th>时间</th>
<td id="datatime">正在加载</td>
</tr>
<tr>
<th>座位号</th>
<td id="num">正在加载</td>
</tr>
</table>
</div>
</div>
<!--content/-->
<div class="bg100"></div>
<script src="js/jquery.min.js"></script>
<script src="js/style.js"></script>
</body>
<script type="text/javascript">
$(function () {
//获取order.html传递过来的 下标i
//http://localhost:8080/ssh_sign/html/order-info.html?0
var i = (window.location.href).split("?")[1];
//通过一个ajax 向后台传递i,获取一条数据,并且显示在对应的位置上
$.ajax({
type: "post",
url: "/Task4_war_exploded/queryOne",
data: {'index': i}, //--->index与控制类中的变量index是一样的
dataType: "json",
success: function (res) {
$("#name").text(res.name);
$("#sno").text(res.sno);
$("#classnumber").text(res.classnumber);
$("#cours").text(res.cours);
$("#datatime").text(res.datatime);
$("#num").text(res.num);
},
error: function (data) {
alert("系统出现异常")
}
});
})
</script>
</html>
可能出现的问题
Tomcat启动失败。
可能因为资源没找到,需要手动导包。
乱码问题
可以使用 SpringMVC 官方的过滤器设置字符集。在 web.xml 配置文件中添加一下代码(注意,本项目已经添加了以下代码)。
<!--SpringMVC官方的乱码过滤器,可以解决大部分的乱码问题-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>