代码已上传至github,点击进入Github
项目已上传到云服务器:运行项目
整体效果展示
整体架构展示
使用的技术栈
以 Maven 架构项目,使用 Spring + SpringMVC + MyBatis 框架;采用 c3p0 开源 JDBC 数据库连接池连接 MySql 数据库;发送 AJAX 请求获取 JSON 数据,并通过 Dom 动态加载页面;使用了 Spring - Test 与Junit 编写单元测试代码;使用了 Restful 设计风格,以 AJAX 发送 PUT 与 DELETE 类型请求;采用了双端校验,前端校验 JQuery,后端校验符合 JSR303 规范
编写过程
一、基础环境搭建
- pom.xml中引入相关jar包
<!-- SpringMVC与Spring整合 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- Spring-Jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- Spring面向切面编程 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!-- MyBatis整合Spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 数据库连接池、驱动 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<!-- jstl,servlet-api,junit -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 搭建Bootstrap环境
src/main/webapp目录下创建static文件夹用来保存静态文件
(1) 将Bootstrap的bootstrap-3.3.7-dist文件夹复制到static文件夹
src/main/webapp/static目录下创建js文件夹引入自己的静态文件
(2) 将jQuery引入到js文件夹
(3) webapp目录下创建index.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<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>Insert title here</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<script type="text/javascript"
src="${APP_PATH }/static/js/jquery-1.12.4.min.js"></script>
<link
href="${APP_PATH }/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"
rel="stylesheet">
<script
src="${APP_PATH }/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
</body>
</html>
- web.xml的基本配置
(1) web.xml中编写
<!-- 1. 配置监听器启动Spring的容器 -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 2. springmvc的前端控制器 -->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 没有使用init-param标签,需要将springmvc配置文件放在正确的位置 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 3. 字符编码过滤器,一定在其余filter之前 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 4. Restful风格的配置 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 5. 支持AJAX请求发送PUT请求 -->
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2) 与web.xml同等级的目录下创建SpringMVC的配置文件
右键new → Spring Bean Configuration File → dispatcherServlet-servlet.xml
(3) src/main/resources目录下创建spring的容器
右键new → Spring Bean Configuration File → applicationContext.xml
- SpringMVC配置文件dispatcherServlet-servlet.xml的基本配置
<!--SpringMVC的配置文件,包含网页跳转逻辑的控制 -->
<!-- 只扫描控制器 -->
<context:component-scan base-package="com.qizegao" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置视图解析器,方便页面返回 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--两个标准配置 -->
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
- Spring配置文件applicationContext.xml的基本配置
<!-- 不扫描已由springmvc扫描的controller注解 -->
<context:component-scan base-package="com.qizegao">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- 引入properties配置文件,配置datasource -->
<context:property-placeholder location="classpath:dbconfig.properties" />
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 和MyBatis整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="pooledDataSource"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!-- 扫描dao接口 -->
<mybatis-spring:scan base-package="com.qizegao.dao"/>
<!-- 配置事务控制 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 使用xml配置形式的事务 -->
<aop:config>
<aop:pointcut expression="execution(* com.qizegao.service..*(..))" id="txPoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
- 创建包、文件夹,将web项目构建成如下结构
- dbconfig.properties文件中的配置
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root
- 创建两张表tbl_stu和tbl_dept
对应的sql语句
CREATE TABLE `tbl_stu` (
`stu_id` INT(11) NOT NULL AUTO_INCREMENT,
`stu_name` VARCHAR(255) NOT NULL,
`gender` CHAR(1) DEFAULT NULL,
`email` VARCHAR(255) DEFAULT NULL,
`d_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`stu_id`),
KEY `fk_emp_dept` (`d_id`),
CONSTRAINT `fk_emp_dept` FOREIGN KEY (`d_id`) REFERENCES `tbl_dept` (`dept_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
CREATE TABLE `tbl_dept` (
`dept_id` INT(11) NOT NULL AUTO_INCREMENT,
`dept_name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`dept_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
- 使用MBG逆向工程创建需要的结构
(1) pom.xml中导入依赖
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
(2) 工程名右键创建一个mbg.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 去掉生成的结构中多余的注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定javaBean生成的位置 -->
<javaModelGenerator targetPackage="com.qizegao.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--指定mapper.xml文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.qizegao.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定每个表对应的java类型 -->
<table tableName="tbl_stu" domainObjectName="Student"></table>
<table tableName="tbl_dept" domainObjectName="Department"></table>
</context>
</generatorConfiguration>
(3) src/main/java目录下com.qizegao.test包下创建MBGTest.java完成逆向工程的工作
public class MBGTest {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
}
- mybatis-config.xml文件中编写
<configuration>
<!-- 启用驼峰命名 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.qizegao.bean"/>
</typeAliases>
</configuration>
- 为逆向工程创建出的结构增加功能
将两张表联合查询(同时可以查出两张表的所有列)
(1) 在StudentMapper接口中添加两个方法
List<Student> selectByExampleWithDept(StudentExample example);
Student selectByPrimaryKeyWithDept(Integer stuId);
(2) 在Student类中添加
//新增的属性
private Department department; //以及对应的getter、setter方法
(3) 在StudentMapper.xml中添加
<resultMap type="com.qizegao.bean.Student" id="WithDeptResultMap">
<id column="stu_id" jdbcType="INTEGER" property="stuId" />
<result column="stu_name" jdbcType="VARCHAR" property="stuName" />
<result column="gender" jdbcType="CHAR" property="gender" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="d_id" jdbcType="INTEGER" property="dId" />
<!-- 指定联合查询出的部门字段 -->
<association property="department" javaType="com.qizegao.bean.Department">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
<sql id="WithDept_Column_List">
s.stu_id, s.stu_name, s.gender, s.email, s.d_id, d.dept_id, d.dept_name
</sql>
<!-- 通过部门信息联表查询 -->
<select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="WithDept_Column_List" />
FROM tbl_stu s
left join tbl_dept d on s.`d_id`=d.`dept_id`
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause} <!-- 生成的Example类中有此属性 -->
</if>
</select>
<!-- 通过学生表的主键联表查询 -->
<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
FROM tbl_stu s
left join tbl_dept d on s.`d_id`=d.`dept_id`
where stu_id = #{stuId,jdbcType=INTEGER}
</select>
- 测试搭建的基础环境是否可以运行
(1) pom.xml中引入Spring单元测试的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
(2) 为逆向工程创建出的两个Java类分别添加无参、全参构造器
(3) src/main/java目录下com.qizegao.test包中创建MapperTest类,搭建Spring单元测试环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:applicationContext.xml"})
public class MapperTest {
@Autowired
DepartmentMapper departmentMapper;
@Autowired
StudentMapper studentMapper;
@Test
public void test() {
System.out.println(departmentMapper);
//org.apache.ibatis.binding.MapperProxy@53093491
//插入三个部门(使用逆向工程自动生成的方法)
departmentMapper.insertSelective(new Department(null, "学生部"));
departmentMapper.insertSelective(new Department(null, "信息部"));
departmentMapper.insertSelective(new Department(null, "记者部"));
//插入三个学生
studentMapper.insertSelective(new Student(null, "高奇泽", "M", "[email protected]", 1));
studentMapper.insertSelective(new Student(null, "秦新宇", "M", "[email protected]", 2));
studentMapper.insertSelective(new Student(null, "王洋", "M", "[email protected]", 3));
//后续会向表中插入更多的学生
}
}
(4) 成功在数据库表中添加以上信息
二、查询
- 分页后台代码完成(使用pageHelper插件)
(1) pom.xml中引入pageHelper的jar包
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
(2) mybatis-config.xml中编写
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
(3) src/main/java目录下service包下创建StudentService类
@Service
public class StudentService {
@Autowired
StudentMapper studentMapper;
//查询所有学生
public List<Student> getAll() {
//创建Example类定义查询条件
StudentExample studentExample = new StudentExample();
studentExample.setOrderByClause("stu_id asc"); //查询结果按照学生编号升序排序
return studentMapper.selectByExampleWithDept(studentExample); //无查询条件传入null
}
}
(4) src/main/java目录下bean包下创建Msg类,用来封装服务器回显的所有数据
public class Msg {
//状态码(设定100表示成功,200表示失败)
private int code;
//提示信息
private String msg;
//用户要返回给浏览器的数据
private Map<String, Object> extend = new HashMap<String, Object>();
public static Msg success(){
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功!");
return result;
}
public static Msg fail(){
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败!");
return result;
}
//添加用户返回给浏览器的数据
public Msg add(String key,Object value){
this.getExtend().put(key, value);
return this;
}
//以及其余的getter、setter方法
}
(5) src/main/java目录下controller包下创建StudentController
@Controller
public class StudentController {
@Autowired
StudentService studentService;
//分页查询学生数据
@RequestMapping("/stus")
@ResponseBody //导入Jackson包使用
public Msg getStusWithJson(
@RequestParam(value = "pn", defaultValue = "1") Integer pn) {
PageHelper.startPage(pn, 5);
List<Student> stus = studentService.getAll();
PageInfo page = new PageInfo(stus, 5); //PageInfo封装了分页的详细信息
return Msg.success().add("pageInfo", page);
}
}
(6) 浏览器地址栏输入http://localhost:8080/my-crud/stus,浏览器回显:
基于上述回显的JSON数据构建jsp页面
- index.jsp页面中编写
<!-- 搭建显示页面 -->
<div class="container">
<!-- 标题 -->
<div class="row">
<div class="col-md-12">
<h1>软件六班学生 - 社团信息</h1>
</div>
</div>
<br/>
<!-- 按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary" id="emp_add_modal_btn">新增</button>
<button class="btn btn-danger" id="emp_delete_all_btn">删除</button>
</div>
</div>
<br/>
<!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-bordered" id="emps_table">
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>邮箱</th>
<th>社团</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 学生的信息 -->
</tbody>
</table>
</div>
</div>
<!-- 显示分页信息 -->
<div class="row">
<!--分页文字信息 -->
<div class="col-md-6" id="page_info_area"></div>
<!-- 分页条信息 -->
<div class="col-md-6" id="page_nav_area"></div>
</div>
</div>
<script type="text/javascript">
//页面加载完成以后,发送ajax请求,得到分页数据
$(function(){
//去首页
to_page(1);
});
function to_page(pn){
$.ajax({
url:"${APP_PATH}/stus",
data:"pn="+pn,
type:"GET",
//result获取到了返回的Msg对象(JSON数据)
success:function(result){
//1、解析并显示学生数据
build_emps_table(result);
//2、解析并显示分页信息
build_page_info(result);
//3、解析显示分页条数据
build_page_nav(result);
}
});
}
//1、解析并显示学生数据
function build_emps_table(result){
//每次显示一页的数据之前应该将之前的内容都清空,因为使用的都是append,
//查询新的一页时会在之前的内容之上append,可能会出现两个导航条之类的错误
//清空table表格中的学生数据
$("#emps_table tbody").empty();
var emps = result.extend.pageInfo.list; //所有学生的信息
//遍历获取的所有学生的信息
$.each(emps, function(index,item){
//获取到学生信息中每项信息
var empIdTd = $("<td></td>").append(item.stuId);
var empNameTd = $("<td></td>").append(item.stuName);
var genderTd = $("<td></td>").append(item.gender=='M'?"男":"女");
var emailTd = $("<td></td>").append(item.email);
var deptNameTd = $("<td></td>").append(item.department.deptName);
//每行中的编辑按钮
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
//每行中的删除按钮
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
//将两个按钮整合在一起
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
//将以上数据拼成一行
$("<tr></tr>").append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
});
}
//2、解析并显示分页信息
function build_page_info(result){
//清空分页信息
$("#page_info_area").empty();
$("#page_info_area").append("当前第 "+result.extend.pageInfo.pageNum+" 页, 共 "+
result.extend.pageInfo.pages+" 页, 共 "+
result.extend.pageInfo.total+" 条记录");
}
//3、解析显示分页条数据
function build_page_nav(result){
//清空分页条信息
$("#page_nav_area").empty();
//Bootstrap中的写法,导航条中的信息都要写在ul变量中
var ul = $("<ul></ul>").addClass("pagination");
//首页
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href","#"));
//前一页
var prePageLi = $("<li></li>").append($("<a></a>").append("«"));
//如果当前遍历的页码是首页(没有前一页),让首页和上一页不可点击
if(result.extend.pageInfo.hasPreviousPage == false){
//disabled是Bootstrap中的写法
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else{
//为首页和前一页添加点击翻页的事件
firstPageLi.click(function(){
to_page(1);
});
prePageLi.click(function(){
to_page(result.extend.pageInfo.pageNum -1);
});
}
//后一页
var nextPageLi = $("<li></li>").append($("<a></a>").append("»"));
//末页
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
//如果当前遍历的页码是末页(没有下一页),让末页和下一页不可点击
if(result.extend.pageInfo.hasNextPage == false){
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else{
//为下一页和末页添加点击翻页的事件
nextPageLi.click(function(){
to_page(result.extend.pageInfo.pageNum +1);
});
lastPageLi.click(function(){
to_page(result.extend.pageInfo.pages);
});
}
//导航条中添加首页和前一页
ul.append(firstPageLi).append(prePageLi);
//遍历1,2,3之类的页码
$.each(result.extend.pageInfo.navigatepageNums, function(index,item){
//numLi / item表示遍历到的1,2,3之类的页码
var numLi = $("<li></li>").append($("<a></a>").append(item));
//如果当前遍历的页码就是当前的页码,让其高亮显示
if(result.extend.pageInfo.pageNum == item){
//active是Bootstrap中的写法
numLi.addClass("active");
}
//单击事件,跳转到对应页面
numLi.click(function(){
to_page(item);
});
//向导航条中添加1,2,3之类的页码
ul.append(numLi);
});
//导航条中添加下一页和末页
ul.append(nextPageLi).append(lastPageLi);
//把ul导航条添加到导航条在页面中的位置
var navEle = $("<nav></nav>").append(ul);
navEle.appendTo("#page_nav_area");
}
</script>
效果如图:
三、新增
- index.jsp中添加点击新增按钮之后弹出如图所示新增学生模态框的代码
大量采用Bootstrap代码(此处未展示出),此模态框的id属性值为:empAddModal
- 将所有部门显示在下拉框中的代码编写
(1) 创建DepartmentService类
@Service
public class DepartmentService {
@Autowired
private DepartmentMapper departmentMapper;
//查询所有社团
public List<Department> getDepts() {
List<Department> list = departmentMapper.selectByExample(null);
return list;
}
}
(2) 创建DepartmentController类
@Controller
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
//以JSON的形式返回所有的社团信息
@RequestMapping("/depts")
@ResponseBody
public Msg getDepts(){
//查出的所有部门信息
List<Department> list = departmentService.getDepts();
return Msg.success().add("depts", list);
}
}
发送对应请求得到的JSON数据
- 检查学生姓名是否已经存在数据库的代码编写
(1) StudentService中编写
//校验用户名是否已经存在于数据库
public boolean checkUser(String stuName) {
//指定条件的查询
StudentExample example = new StudentExample();
Criteria criteria = example.createCriteria();
criteria.andStuNameEqualTo(stuName);
long count = studentMapper.countByExample(example);
return count == 0;
}
(2) StudentController中编写
//检查用户名是否已经存在于数据库
@ResponseBody
@RequestMapping("/checkuser")
public Msg checkuser(@RequestParam("stuName")String stuName){
//先判断用户名是否满足正则表达式,若不满足无需去数据库查询
String regx = "^[\u2E80-\u9FFF]{2,5}";
if(!stuName.matches(regx)){
return Msg.fail().add("va_msg", "学生姓名必须是2-5位汉字");
}
//数据库用户名重复校验
boolean b = studentService.checkUser(stuName);
if(b){
return Msg.success();
}else{
return Msg.fail().add("va_msg", "学生姓名已存在");
}
}
- 保存模态框填写的信息的代码编写(使用JSR303后端校验)
(1) Student类中给两个属性添加JSR303后端校验的注解
(2) StudentService中编写
//保存学生
public void saveStu(Student student) {
studentMapper.insertSelective(student); //MBG生成的方法
}
(3) StudentController中编写
//保存学生
@RequestMapping(value="/stu",method=RequestMethod.POST)
@ResponseBody
public Msg saveStu(@Valid Student student, BindingResult result){
if (result.hasErrors()) {
//JSR303校验结果有错误时,将JSR303校验结果封装在map中
Map<String, Object> map = new HashMap<String, Object>();
//获取所有有错误的字段
List<FieldError> errors = result.getFieldErrors();
for (FieldError fieldError : errors) {
//getFiled表示错误的字段名
//getDefaultMessage表示JavaBean定义的错误信息
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} else {
//校验结果无误时
studentService.saveStu(student);
return Msg.success();
}
}
- index.jsp中完成新增功能的编写
//点击新增按钮弹出添加学生的模态框
$("#emp_add_modal_btn").click(function(){
//调用函数清除模态框的所有数据数据,防止上一次信息残留
reset_form("#empAddModal form");
//调用函数发送ajax请求,查出社团信息,显示在下拉列表(select标签)中
getDepts("#empAddModal select");
//弹出添加学生的模态框(Bootstrap代码)
$("#empAddModal").modal({
backdrop:"static"
});
});
//清空模态框样式及内容(Bootstrap代码)
function reset_form(ele){
$(ele)[0].reset();
$(ele).find("*").removeClass("has-error has-success");
$(ele).find(".help-block").text("");
}
//发送ajax请求,查出社团信息,显示在下拉列表中
function getDepts(ele){
//清空之前下拉列表的值
$(ele).empty();
//发送AJAX请求,获取所有社团的信息
$.ajax({
url:"${APP_PATH}/depts",
type:"GET",
//result中封装了查询到的所有社团的JSON数据
success:function(result){
//遍历获取到的所有社团信息,显示在下拉列表中
$.each(result.extend.depts,function(){
var optionEle = $("<option></option>").append(this.deptName).attr("value",this.deptId);
optionEle.appendTo(ele);
});
}
});
}
//通过内容发生改变事件和ajax请求校验填写的用户名是否已经存在于数据库
$("#empName_add_input").change(function(){
var stuName = this.value;
//发送ajax请求
$.ajax({
url:"${APP_PATH}/checkuser",
data:"stuName="+stuName,
type:"POST",
success:function(result){
if(result.code==100){
//调用函数以指定的格式显示提示信息
show_validate_msg("#empName_add_input","success","学生姓名可用");
//如果用户名可用,才让其保存,保存按钮添加属性
$("#emp_save_btn").attr("ajax-va","success");
}else{
show_validate_msg("#empName_add_input","error",result.extend.va_msg);
//如果用户名不可用,不让其保存,保存按钮添加属性
$("#emp_save_btn").attr("ajax-va","error");
}
}
});
});
//添加学生模态框的保存按钮的单击事件
$("#emp_save_btn").click(function(){
//判断之前的ajax用户名是否已经存在数据库的校验是否成功
if($(this).attr("ajax-va")=="error"){
return false;
}
/* //调用函数对输入的邮箱进行正则表达式校验(用户名规则已在数据库校验)
if(!validate_add_form()){
return false; //点击保存不会进行数据提交
}; */
//发送ajax请求保存员工
$.ajax({
url:"${APP_PATH}/stu",
type:"POST",
//将用户填入的信息序列化成为字符串,用于AJAX请求
data:$("#empAddModal form").serialize(),
success:function(result){
//如果JSR303后端校验成功,保存员工
if(result.code == 100){
//关闭模态框(Bootstrap代码)
$("#empAddModal").modal('hide');
//来到最后一页,显示刚才保存的数据
to_page(999);
}else{
//如果JSR303后端校验失败,显示信息
//哪个字段有错误,显示哪个字段的错误
if(undefined != result.extend.errorFields.email){
//显示邮箱的错误信息
show_validate_msg("#email_add_input", "error", result.extend.errorFields.email);
}
if(undefined != result.extend.errorFields.stuName){
//显示学生姓名的错误信息
show_validate_msg("#empName_add_input", "error", result.extend.errorFields.stuName);
}
}
}
});
});
//使用正则表达式校验添加学生模态框的邮箱是否符合指定格式
function validate_add_form(){
//用户名已在数据库中检查,此处无需再查
//校验邮箱信息
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
//调用函数以指定的格式显示提示信息
show_validate_msg("#email_add_input", "error", "邮箱格式不正确");
return false;
}else{
show_validate_msg("#email_add_input", "success", "");
}
return true;
}
//添加学生模态框校验结果的提示信息样式(Bootstrap代码)
function show_validate_msg(ele,status,msg){
$(ele).parent().removeClass("has-success has-error");
$(ele).next("span").text("");
if("success"==status){
$(ele).parent().addClass("has-success");
$(ele).next("span").text(msg);
}else if("error" == status){
$(ele).parent().addClass("has-error");
$(ele).next("span").text(msg);
}
}
四、修改
- index.jsp中添加点击编辑按钮之后弹出如图所示修改学生信息的模态框的代码
大量采用Bootstrap代码(此处未展示出),此模态框的id属性值为:empUpdateModal
- 根据id查询学生信息的代码编写
(1) StudentService中编写
//根据id查询学生信息
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
return student;
}
(2) StudentController中编写
//根据id查询学生信息
@RequestMapping(value="/stu/{id}", method=RequestMethod.GET)
@ResponseBody
public Msg getStu(@PathVariable("id")Integer id){
Student student = studentService.getStu(id);
return Msg.success().add("stu", student);
}
- 保存更新后的学生信息的代码编写
(1) StudentService中编写
//保存更新后的学生的信息
public void updateStu(Student student) {
studentMapper.updateByPrimaryKeySelective(student);
}
(2) StudentController中编写
@ResponseBody
@RequestMapping(value="/stu/{stuId}", method=RequestMethod.PUT)
public Msg saveStu(Student student){
studentService.updateStu(student);
return Msg.success();
}
- index.jsp中完成修改信息功能的编写
//为编辑按钮添加单击事件,弹出模态框
//不能直接使用click为编辑按钮绑定单击事件
//因为编辑按钮是在页面加载完成事件中添加的,单击事件早于页面加载事件被绑定
//相当于还没有加载出来按钮就为其绑定了事件,故使用on来绑定事件
$(document).on("click",".edit_btn",function(){
//1、查出社团信息,并显示社团列表
getDepts("#empUpdateModal select");
//2、查出学生信息,显示学生信息
getEmp($(this).attr("edit-id"));
//3、把学生的id传递给模态框中的更新按钮,之前编辑按钮的edit-id属性已传入对应的学生id
$("#emp_update_btn").attr("edit-id",$(this).attr("edit-id"));
//4. 弹出修改信息的模态框(Bootstrap代码)
$("#empUpdateModal").modal({
backdrop:"static"
});
});
//查出学生信息,在修改信息的模态框中显示学生信息
function getEmp(id){
$.ajax({
url:"${APP_PATH}/stu/"+id,
type:"GET",
success:function(result){
var empData = result.extend.stu;
$("#empName_update_static").text(empData.stuName);
$("#email_update_input").val(empData.email);
$("#empUpdateModal input[name=gender]").val([empData.gender]);
$("#empUpdateModal select").val([empData.dId]);
}
});
}
//点击模态框中的更新,更新学生信息
$("#emp_update_btn").click(function(){
//1. 验证邮箱是否合法
var email = $("#email_update_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
show_validate_msg("#email_update_input", "error", "邮箱格式不正确");
return false;
}else{
show_validate_msg("#email_update_input", "success", "");
}
//2. 发送ajax请求保存更新的学生数据
$.ajax({
url:"${APP_PATH}/stu/"+$(this).attr("edit-id"),
//web.xml中已添加过滤器,可直接发送PUT请求
type:"PUT",
data:$("#empUpdateModal form").serialize(),
success:function(result){
//1、关闭对话框
$("#empUpdateModal").modal("hide");
//2、回到本页面(定义变量保存了当前页面的页码号)
to_page(currentPage);
}
});
});
五、删除
- StudentService中添加
//单个删除
public void deleteStu(Integer id) {
studentMapper.deleteByPrimaryKey(id);
}
//批量删除
public void deleteBatch(List<Integer> ids) {
StudentExample studentExample = new StudentExample();
Criteria criteria = studentExample.createCriteria();
//delete from tbl_stu where stu_id in(1,2,3...)
criteria.andStuIdIn(ids);
studentMapper.deleteByExample(studentExample);
}
- StudentController中添加
//删除学生
//批量删除时不同的学生通过 "-" 连接
@ResponseBody
@RequestMapping(value="/stu/{ids}",method=RequestMethod.DELETE)
public Msg deleteEmp(@PathVariable("ids")String ids){
//批量删除
if(ids.contains("-")){
List<Integer> del_ids = new ArrayList<>();
String[] str_ids = ids.split("-");
//将所有学生id组装成集合
for (String string : str_ids) {
del_ids.add(Integer.parseInt(string));
}
studentService.deleteBatch(del_ids);
}else{
Integer id = Integer.parseInt(ids);
studentService.deleteStu(id);
}
return Msg.success();
}
- index.jsp中完成删除学生功能的编写
//为单个删除绑定单击事件
$(document).on("click",".delete_btn",function(){
//1、弹出是否确认删除对话框
var stuName = $(this).parents("tr").find("td:eq(2)").text();
var stuId = $(this).attr("del-id");
if(confirm("确认删除【"+stuName+"】吗?")){
//确认,发送ajax请求删除即可
$.ajax({
url:"${APP_PATH}/stu/"+stuId,
type:"DELETE",
success:function(result){
//显示处理成功
alert(result.msg);
//回到本页
to_page(currentPage);
}
});
}
});
//全选框绑定单击事件完成全选/全不选功能
$("#check_all").click(function(){
$(".check_item").prop("checked",$(this).prop("checked"));
});
//为单选框绑定单击事件,全部选中以后全选框随之选中
$(document).on("click",".check_item",function(){
//选中的个数是否等于单选框个数
var flag = $(".check_item:checked").length==$(".check_item").length;
$("#check_all").prop("checked",flag);
});
//批量删除按钮绑定单击事件
$("#emp_delete_all_btn").click(function(){
//表示所有被选中的学生的姓名
var stuNames = "";
//将学中学生的id使用"-"拼接
var del_idstr = "";
//遍历每一个选中的学生
$.each($(".check_item:checked"),function(){
stuNames += $(this).parents("tr").find("td:eq(2)").text()+",";
del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-";
});
//去除stuNames最后多余的逗号
stuNames = stuNames.substring(0, stuNames.length-1);
//去除del_idstr最后多余的 "-"
del_idstr = del_idstr.substring(0, del_idstr.length-1);
//弹出确认删除对话框
if(confirm("确认删除【"+stuNames+"】吗?")){
//发送ajax请求批量删除
$.ajax({
url:"${APP_PATH}/stu/"+del_idstr,
type:"DELETE",
success:function(result){
//显示处理成功
alert(result.msg);
//回到当前页面
to_page(currentPage);
}
});
}
});