建议先学
- 五万字的Spring5学习笔记,带你熟悉运用Spring5
- 四万多字的SpringMVC学习总结,带你领略不一样的SpringMVC
- 怎么请求数据库的数据?这套四万多字的Mybatis学习笔记给你答案,只做入门,不做深层次分析
概要
- 我们的目标是整个三大框架,来做一个简单的增删改查系统。
- 该系统有以下几个要点
- 该系统在网页中显示,数据要列出在网页上。
- 系统提供增删改查的选项功能。
- 系统提供分页的功能。
- 系统提供数据检测的功能,检查增加的数据是否符合现实规则。
创建工程
- 创建一个Maven工程,利用webapp模块。
- 创建完毕后,里面是没有正常的Maven工程的包的,比如java test包之类,我们需要根据正常模板导入,但webapp模块不要删除。
导入依赖
-
创建模块后,就要导入依赖,在pom.xml 中我们要配置基本的jar。
- spring springMvc mybatis spring-mybatis
- jstl servlet mysql-connnector durid junit
<?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>com.hyb</groupId>
<artifactId>SSM_CRUD</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--配置ssm框架jar-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--数据库连接池和驱动-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
注意,有时候你导入的依赖刷新Maven的时候可能还是会报红,这可能是镜像网站网络的问题,可以上网查阅更换aliyun的镜像网站。一般的解决办法还是在本地的镜像仓库的jar的垃圾文件删除,重新刷新一遍,若还是不行,就更换不同版本的jar。 -
写完pom.xml文件后,我们要进行一些web的文件导入,这里我们使用前端框架Bootstrap 和JQuery 来进行。前面的框架主要用来解决css,后面的主要用来解决js。我们可以在index.jsp中看看如何导入 <%--
Created by IntelliJ IDEA.
User: 黄渝斌
Date: 2021/9/11
Time: 10:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index.jsp</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<script type="text/javascript" href="js/jquery-3.5.1.js"></script>
</head>
<body>
<button type="button" class="btn btn-default">这是一个按钮</button>
</body>
</html>
注意,这里的Bootstrap 框架由于支持cdn,所以不用下载包,而JQuery则需要下载。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>CharacterEncodingFilter</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>
<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>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注意,这个xml中,注意标签的优先级。
dispatcherServlet-servlet.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">
<context:component-scan base-package="com.hyb.crud" 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/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
</beans>
applicationContext.xml
- 该配置是Spring的配置文件和Spring和Mybatis整合的配置文件
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<context:component-scan base-package="com.hyb.crud">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:property-placeholder location="classpath:jdbcSql.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="typeAliasesPackage" value="com.hyb.myBatis"></property>
</bean>
<mybatis-spring:scan base-package="com.hyb.crud.dao"/>
</beans>
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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.hyb.crud.bean"/>
</typeAliases>
</configuration>
创建表
- 创建员工表和部门表,两个表都有各自的id,员工表的外键属于部门表的id
逆向工程创建ssm项目
-
首先导入逆向工程的包。 -
我们先创建应有的包名,然后可以用逆向工程创建ssm项目 -
首先,我们得在工程目录下,创建一个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.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/hyb?serverTimezone=UTC"
userId="root"
password="15717747056HYB">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator targetPackage="com.hyb.crud.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.hyb.crud.dao"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<table tableName="employee" domainObjectName="Employee"></table>
<table tableName="department_1" domainObjectName="Department"></table>
</context>
</generatorConfiguration>
-
然后我们编写测试类,来测试,一次测试就可以创建出一个逆向工程 @Test
public void testReserval() 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);
}
-
执行,逆向工程创建完毕,完成所有基本代码实现。
修改逆向工程
-
在创建好的逆向工程中,我们会发现,在Employee的javabean中,是没有属性Department的,但是我们表里面是有这个属性的主键的,也就是Employee表里的外键。而且,我们在查询的时候,我们希望,通过员工表里的外键可以查出该部门信息。但是我们生成的sql映射文件里,是没有改sql语句执行,所以我们要修改。 -
首先,我们要修改Employee 类的属性,为其加上get set方法。 private Department department;
-
之后,我们要在Employee 的dao的mapper接口,写出该带有部门信息的查询方法 List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);
-
写完后,要写其对应的sql语句,由于该sql映射文件是动态sql,所以我们可以仿照没有部分信息的查询方法来写。 首先写我们resultMap <resultMap id="WithDeptResultMap" type="com.hyb.crud.bean.Employee">
<id column="emp_id" jdbcType="INTEGER" property="empId" />
<result column="emp_name" jdbcType="VARCHAR" property="empName" />
<result column="gender" jdbcType="VARCHAR" property="gender" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="dept_id" jdbcType="INTEGER" property="deptId" />
<association property="department" javaType="com.hyb.crud.bean.Department">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
注意,该resultMa不能在原来的resultMap中写,因为原来的resultMap是没有关联Department这个属性的,其sql语句也没有。我们写这个resultMap是为写我们带有部门信息的查询方法。 下面,我们可以写这两个对应的动态sql语句 <sql id="WithDept_Column_List">
emp_id, emp_name, gender, email, employee.dept_id, department_1.dept_name
</sql>
<select id="selectByExampleWithDept" parameterType="com.hyb.crud.bean.EmployeeExample" resultMap="WithDeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="WithDept_Column_List" />
from employee,deaprtment_1
where employee.dept_id=department_1.dept_id
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKeyWithDept" parameterType="java.lang.Integer" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
from employee,department_1
where emp_id = #{empId,jdbcType=INTEGER}
</select>
测试逆向工程
-
我们首先来测试DepartmentMapper ,而且,我们要用Spring-test的测试框架来测试 -
首先,我们新建一个test测试类,然后新建一个带有Test注解的test测试方法,之后,我们在pom.xml中加入spring-test框架。请上镜像网站搜索。 -
然后我们在测试类上,加上以下两个注解 //使用Spring的单元测试模块
@RunWith(SpringJUnit4ClassRunner.class)
//解析的文件地址
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
注意,如果没有这两个注解,而你又在pom.xml文件中加入了jar,是因为你没有刷新Maven 加入这两个注解后,我们可以自动注入DepartmentMapper了,然后尝试是否可以获取departmentMapper对象
@Autowired
DepartmentMapper departmentMapper;
@Test
public void testReserval_1(){
System.out.println(departmentMapper);
}
注意,这里的注入在IDEA中可能会标红,这不是错误的原因,不要管它。测试后,若是报错,会经常报spring-test junit 需要4.12版本以上。 -
获取对象成功后,说明我们的配置文件基本完成和没有错误,可以进行真实测试,我们可以进行简单的增删改查测试
首页列表数据
编写Controller
-
我们都知道,服务器启动的时候跳转的默认都是index.jsp,为了方便管理,我们可以在index.jsp中进行页面跳转。 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:forward page="/emps"></jsp:forward>
注意,这个页面只保留这两项,其他都删除。 -
可以看到,我们跳转到emps 页面,但这是ssm框架,我们需要处理数据,所以这里跳转的是Controller,我们可以创建一个Controller,然后编写一个方法,将里面数据写全。 package com.hyb.crud.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hyb.crud.bean.Employee;
import com.hyb.crud.service.EmployeeService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.List;
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@RequestMapping("/emps")
public String getEmps(@RequestParam(value = "page",defaultValue = "1") Integer page, Model model){
PageHelper.startPage(page,5);
List<Employee> emps = employeeService.getAll();
PageInfo<Employee> employeePageInfo = new PageInfo<Employee>(emps,5);
model.addAttribute("pageInfo",employeePageInfo);
return "list";
}
}
这里,只要我们将Service层写好就可以了。在利用分页框架的时候,记得先将PageHelper的jar导入。 -
完成后,进行测试,这里的测试,我们还是用spring-test的测试方法。 package com.hyb.crud.test;
import com.github.pagehelper.PageInfo;
import com.hyb.crud.bean.Employee;
import com.sun.xml.internal.ws.api.client.WSPortInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:applicationContext.xml","file:src/main/webapp/WEB-INF/dispatcherServlet-servlet.xml"})
public class MVCTest {
@Autowired
WebApplicationContext context;
MockMvc mockMvc;
@Before
public void initMockMvc(){
mockMvc= MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testPage() throws Exception {
MvcResult page = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("page", "1")).andReturn();
MockHttpServletRequest request = page.getRequest();
PageInfo pageInfo = (PageInfo) request.getAttribute("pageInfo");
System.out.println(pageInfo);
}
}
编写jsp页面
-
因为这里我们是通过index请求到Controller然后到list页面,所以,我们接下来就利用BootStrap 框架来编写该jsp页面。然后读取出数据。 <%--
Created by IntelliJ IDEA.
User: 黄渝斌
Date: 2021/9/12
Time: 12:31
To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/common/base.jsp"%>
<html>
<head>
<title>员工列表</title>
<%@include file="/common/link.jsp"%>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-success">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
</button>
<button class="btn btn-warning">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<tr>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>邮箱</th>
<th>部门</th>
<th >操作</th>
</tr>
<c:forEach items="${pageInfo.list}" var="emps">
<tr>
<th>${emps.empId}</th>
<th>${emps.empName}</th>
<th>${emps.gender=="1"?"男":"女"}</th>
<th>${emps.email}</th>
<th>${emps.department.deptName}</th>
<th>
<button class="btn btn-success">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> 修改
</button>
<button class="btn btn-warning">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> 删除
</button>
</th>
</tr>
</c:forEach>
</table>
</div>
</div>
<%@include file="/common/page.jsp"%>
</div>
</body>
</html>
-
从上面这个jsp页面可以知道,我们有几个jsp页面的抽取,首先是链接的抽取,我们可以将动态获取服务器地址的方法写在一个jsp页面中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
/*改路径是以/开始没有以/结束*/
pageContext.setAttribute("basePath",request.getContextPath());
%>
这个jsp页面是共用的。 -
由于链接JQuery的文件和链接BootStrap框架的都需要,我们也可以抽取在一个jsp页面中 <%--
Created by IntelliJ IDEA.
User: 黄渝斌
Date: 2021/9/12
Time: 12:29
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script type="text/javascript" href="${basePath}/js/jquery-3.5.1.js"></script>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
-
然后还有分页功能,我们也可以抽取出在一个jsp页面中 <%--
Created by IntelliJ IDEA.
User: 黄渝斌
Date: 2021/9/12
Time: 15:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div class="row">
<div class="col-md-6">
当前${pageInfo.pageNum}页,总${pageInfo.pages}页,总${pageInfo.total}记录
</div>
<div class="col-md-6">
<nav aria-label="Page navigation">
<ul class="pagination">
<c:if test="${!pageInfo.isFirstPage}">
<li>
<a href="${basePath}/emps?page=1">首页</a>
</li>
</c:if>
<c:if test="${pageInfo.hasPreviousPage}">
<li>
<a href="${basePath}/emps?page=${pageInfo.pageNum-1}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
</c:if>
<c:forEach items="${pageInfo.navigatepageNums}" var="page_num">
<c:if test="${page_num==pageInfo.pageNum}">
<li class="active"><a>${page_num}</a></li>
</c:if>
<c:if test="${page_num!=pageInfo.pageNum}">
<li><a href="${basePath}/emps?page=${page_num}">${page_num}</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.hasNextPage}">
<li>
<a href="${basePath}/emps?page=${pageInfo.pageNum+1}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</c:if>
<c:if test="${!pageInfo.isLastPage}">
<li>
<a href="${basePath}/emps?page=${pageInfo.pages}">末页</a>
</li>
</c:if>
</ul>
</nav>
</div>
</div>
-
注意,在这里我们需要导入Jsp的jar和JSl中taglib 两个重要jar包
升级查询技术
-
上面的首页列表数据,我们是利用了index.jsp发送请求到Controller ,然后再请求到具体页面,但这只适合于浏览器和客户进行交互。 -
要先做到手机端和电脑端都能和客户交互,可以使用Ajax请求的方式查询数据。 -
首先,导入Json jar包。 com.fasterxml.jackson.core jackson-databind 2.12.3 -
然后在Controller中,我们可以直接返回一个PageInfo的数据,就会转换为JSON @RequestMapping("/emps")
@ResponseBody
public PageInfo<Employee> getEmpsByAjax(@RequestParam(value = "page",defaultValue = "1") Integer page){
PageHelper.startPage(page,5);
List<Employee> emps = employeeService.getAll();
return new PageInfo<Employee>(emps,5);
}
-
但是这个JSON不是通用的,因为我们返回这个是PageInfo,那若是别的请求返回的不是PageInfo呢?而且,java行为大致都是一样的,所以我们可以做一个通用的返回javabean package com.hyb.crud.bean;
import sun.applet.resources.MsgAppletViewer;
import java.util.HashMap;
import java.util.Map;
public class Msg {
private String code;
private String msg;
private Map<String,Object> map=new HashMap<String, Object>();
public Msg add(String s,Object v){
this.map.put(s,v);
return this;
}
public static Msg fail(){
Msg msg = new Msg();
msg.setCode("100");
msg.setMsg("处理失败!");
return msg;
}
public static Msg success(){
Msg msg = new Msg();
msg.setCode("200");
msg.setMsg("处理成功!");
return msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
}
这个javabean就具备了响应警告,和数据保存的功能,这两个功能,所有请求都需要用的。 -
做出了公共返回类型,我们就得修改我们方法 public Msg getEmpsByAjax(@RequestParam(value = "page",defaultValue = "1") Integer page){
PageHelper.startPage(page,5);
List<Employee> emps = employeeService.getAll();
return Msg.success().add("pageInfo",new PageInfo<Employee>(emps,5));
}
这里add方法不是static的,但是前面success方法是静态,调用该方法后就获取到一个Msg对应,所以可以调用该非静态方法。不仅如此,该add方法是链式方法,即add后面还可以调用add,若有其他数据,就可以添加新的数据。 -
于是乎,我们就不需要list.jsp了,我们只在index.jsp做ajax请求,并将数据反馈在这个页面上。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/common/base.jsp"%>
<html>
<head>
<title>员工列表</title>
<%@include file="/common/link.jsp"%>
<script type="text/javascript" >
$(function () {
to_page(1);
})
function to_page(pn) {
$.ajax({
url:"${basePath}/emps",
data:"page="+pn,
type:"GET",
success:function (data) {
build_list(data);
build_page_count(data);
build_page(data);
}
})
}
function build_list(data) {
//清空表格里的数据
$("#emps_table tbody").empty();
var emps=data.map.pageInfo.list;
$.each(emps,function (index,item) {
// alert(item.empName);
var empIdTd=$("<td></td>").append(item.empId);
var empNameTd=$("<td></td>").append(item.empName);
var empGenderTd=$("<td></td>").append(item.gender=="1"?"男":"女");
var empEmailTd=$("<td></td>").append(item.email);
var empDeptTd=$("<td></td>").append(item.department.deptName);
var button_1=$("<button></button>").addClass("btn btn-success")
.append($("<span></span>")).addClass("glyphicon glyphicon-pencil")
.append("编辑");
var button_2=$("<button></button>").addClass("btn btn-warning")
.append($("<span></span>")).addClass("glyphicon glyphicon-trash")
.append("删除")
var button_1_2=$("<td></td>").append(button_1).append(" ").append(button_2);
$("<tr></tr>").append(empIdTd).append(empNameTd)
.append(empGenderTd).append(empEmailTd)
.append(empDeptTd).append(button_1_2)
.appendTo("#emps_table tbody");
})
}
function build_page_count(data) {
$("#page_count").empty();
var page=data.map.pageInfo;
$("#page_count").append("当前"+page.pageNum+"页,总"+page.pages+"页,总"+
page.total+"记录");
}
function build_page(data) {
$("#page").empty();
var ul=$("<ul></ul>").addClass("pagination");
//首页
var firstPage=$("<li></li>").append($("<a></a>").append("首页").attr("href","#"));
firstPage.click(function () {
to_page(1);
});
//上一页
var upPage=$("<li></li>").append($("<a></a>").append("<span></span>").append("«").attr("href","#"));
upPage.click(function () {
to_page(data.map.pageInfo.pageNum-1);
});
if(!data.map.pageInfo.hasPreviousPage){
firstPage.addClass("disabled");
upPage.addClass("disabled");
}
//首页,前一页
ul.append(firstPage).append(upPage);
$.each(data.map.pageInfo.navigatepageNums,function (index,itme) {
var numi=$("<li></li>").append($("<a></a>").append(itme).attr("href","#"));
numi.click(function () {
to_page(itme);
});
if (data.map.pageInfo.pageNum===itme){
numi.addClass("active");
}
// 当前页
ul.append(numi);
})
//下一页
var downPage=$("<li></li>").append($("<a></a>").append("<span></span>").append("»").attr("href","#"));
downPage.click(function () {
to_page(data.map.pageInfo.pageNum+1);
});
//末页
var endPage=$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
endPage.click(function () {
to_page(data.map.pageInfo.pages);
});
if(!data.map.pageInfo.hasNextPage){
endPage.addClass("disabled");
downPage.addClass("disabled");
}
ul.append(downPage).append(endPage);
var nav=$("<nav></nav>").append(ul);
$("#page").append(nav);
}
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-success">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
</button>
<button class="btn btn-warning">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-hover" 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_count">
</div>
<div class="col-md-6" id="page">
</div>
</div>
</div>
</body>
</html>
注意,在这里,我们要配置插件分页合理化,不然虽然我们在分页栏到达极限的时候跳转不了,但页面记录数还是会跳转。 在mybatis-config <plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
新增员工
-
点击新增员工,探出BootStrap 模态框,我们可以将这模态框框架修改,变成属于自己的模态框。 <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">新增员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label for="empName" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="empName" placeholder="Name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="inlineRadioOptions" id="gender_1" value="1" checked="checked">男
</label>
<label class="radio-inline">
<input type="radio" name="inlineRadioOptions" id="gender_0" value="0">女
</label>
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="email" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="deptId" class="col-sm-2 control-label">部门</label>
<div class="col-sm-10">
<select class="form-control" id="deptId">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button id="add_bnt" class="btn btn-success" data-keyboard="true" >
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
</button>
<button id="del_bnt" class="btn btn-warning">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
下面按钮对应。 -
写好,可以发送ajax请求,请求数据库中部门的名字。
$(function () {
$("#add_bnt").click(function () {
getDepts();
$("#myModal").modal({
backdrop:"static"
});
});
})
function getDepts() {
$.ajax({
url:"${basePath}/depts",
type:"GET",
success:function (data) {
console.log(data)
$.each(data.map.depts,function () {
var select=$("<option></option>").append(this.deptName).attr("value",this.deptId);
select.appendTo("#deptId");
})
}
})
}
前端+后端校验
前端
- 前端校验于程序猿来说不合理,在浏览器的开发者工具中,可以修改前端代码,如果只是用前端的知识来进行表单的验证,数据会极度的不安全,所以有必要采用后端校验。
- 说明:其实前端校验可以不要,只用后端+前端知识结合校验就可以了。
后端
- 后端校验的好处是,我们从前端发送ajax请求,然后在后端去请求数据库,例如:出现相同或者为空的时候,可以将警告保存,然后交给浏览器去处理。
例子
-
首先,我们得导入JSR303校验 的包,博主用 <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
这个版本相对比较稳定,而且import class的时候一定不要搞错,这个版本是javax的。 -
这个jar提供了相当多的注解,方便我们可以直接在javabean的属性上加入校验注解,然后jar会给我自动解析这个注解,实现错误信息的记录。
@Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{2,15}$",
message = "字母开头,允许3-16位节,允许字母数字下划线")
private String empName;
@Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$",message = "邮箱格式不正确")
private String email;
-
这时,当我们增加员工的时候,该注解会自动保存信息。当然,我们要在Controller里面获取 @RequestMapping(value = {"/emp"},method = RequestMethod.POST)
@ResponseBody
public Msg addEmp(@Valid Employee e, BindingResult result){
Map<String,Object> resultMap=new HashMap<String, Object>();
System.out.println(result.hasErrors());
if (result.hasErrors()){
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError error:fieldErrors
) {
resultMap.put(error.getField(),error.getDefaultMessage());
}
return Msg.fail().add("resultMap",resultMap);
}
employeeService.add(e);
return Msg.success();
}
这里面是在原来的基础上改进的。 -
然后就是前端的一些代码,这里为了方便,我将前段校验和后端校验都发上来。 <%--
Created by IntelliJ IDEA.
User: 黄渝斌
Date: 2021/9/12
Time: 12:31
To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/common/base.jsp"%>
<html>
<head>
<title>员工列表</title>
<%@include file="/common/link.jsp"%>
<script type="text/javascript" >
var totalPage;
$(function () {
to_page(1);
})
function to_page(pn) {
$.ajax({
url:"${basePath}/emps",
data:"page="+pn,
type:"GET",
success:function (data) {
totalData=data;
build_list(data);
build_page_count(data);
build_page(data);
}
})
}
function build_list(data) {
//清空表格里的数据
$("#emps_table tbody").empty();
var emps=data.map.pageInfo.list;
$.each(emps,function (index,item) {
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName);
var genderTd = $("<td></td>").append(item.gender==='1'?"男":"女");
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")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm")
.append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
//append方法执行完成以后还是返回原来的元素,这里每次append都会产生新的<tr></tr>
var tr=$("<tr></tr>").append(empIdTd).append(empNameTd)
.append(genderTd).append(emailTd).append(deptNameTd)
.append(btnTd)
$("#emps_table tbody").append(tr)
})
}
function build_page_count(data) {
$("#page_count").empty();
var page=data.map.pageInfo;
$("#page_count").append("当前"+page.pageNum+"页,总"+page.pages+"页,总"+
page.total+"记录");
totalPage=page.total;
}
function build_page(data) {
$("#page").empty();
var ul=$("<ul></ul>").addClass("pagination");
//首页
var firstPage=$("<li></li>").append($("<a></a>").append("首页").attr("href","#"));
firstPage.click(function () {
to_page(1);
});
//上一页
var upPage=$("<li></li>").append($("<a></a>").append("<span></span>").append("«").attr("href","#"));
upPage.click(function () {
to_page(data.map.pageInfo.pageNum-1);
});
if(!data.map.pageInfo.hasPreviousPage){
firstPage.addClass("disabled");
upPage.addClass("disabled");
}
//首页,前一页
ul.append(firstPage).append(upPage);
$.each(data.map.pageInfo.navigatepageNums,function (index,itme) {
var numi=$("<li></li>").append($("<a></a>").append(itme).attr("href","#"));
numi.click(function () {
to_page(itme);
});
if (data.map.pageInfo.pageNum===itme){
numi.addClass("active");
}
// 当前页
ul.append(numi);
})
//下一页
var downPage=$("<li></li>").append($("<a></a>").append("<span></span>").append("»").attr("href","#"));
downPage.click(function () {
to_page(data.map.pageInfo.pageNum+1);
});
//末页
var endPage=$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
endPage.click(function () {
to_page(data.map.pageInfo.pages);
});
if(!data.map.pageInfo.hasNextPage){
endPage.addClass("disabled");
downPage.addClass("disabled");
}
ul.append(downPage).append(endPage);
var nav=$("<nav></nav>").append(ul);
$("#page").append(nav);
}
// 对列表框发送ajax请求,将部门的数据显示在下拉列表框中
$(function () {
$("#add_bnt").click(function () {
getDepts();
$("#myModal").modal({
//防止点击背景就消失
backdrop:"static"
});
});
})
function getDepts() {
$("#_deptId").empty();
$.ajax({
url:"${basePath}/depts",
type:"GET",
success:function (data) {
$.each(data.map.depts,function () {
var select=$("<option></option>").append(this.deptName).attr("value",this.deptId).attr("name","deptId");
select.appendTo("#_deptId");
})
}
})
}
//点击保存按钮
$(function () {
$("#add_emp").click(function () {
//若用户不合法,阻止提交ajax请求
if (!userJudge()){
return false;
}
//若用户名已存在,阻止其提交ajax请求
if ($(this).attr("ajax_alert")==="error"){
return false;
}
$.ajax({
url:"${basePath}/emp",
type:"POST",
//表单序列化,变成json
data:$("#myModal form").serialize(),
success:function (data) {
/*
* 因为我们在后端进行校验,所以我们要在关闭模态框之前进行判断
* */
if (data.code==="200"){
// 关闭模态框
$("#myModal").modal('hide');
//清空表单项
clear();
// 跳转到最后一页
to_page(totalPage);
}else {
if (undefined!==data.map.resultMap.email){
// 显示员工名错误信息
$("#email").parent().addClass("has-error");
$("#inputErrorEmail").text(data.map.resultMap.email);
}
if (undefined!==data.map.resultMap.empName){
// 显示邮箱错误信息
$("#empName").parent().addClass("has-error");
$("#inputErrorName").text(data.map.resultMap.empName);
}
}
}
})
});
})
//表单的清空
$(function () {
$("#_reset").click(function () {
clear();
});
})
function clear() {
//表单重置
$("#myModal form")[0].reset();
// $("#empName").val("");
// $("#email").val("");
}
//用户名是否存在校验
$(function () {
//注意,这里一定要有这个失去焦点事件,或者其他事件,不然你直接让$(function(){})先加载,弹出框都没出来便发送ajax请求,对后来的验证就没有了
$("#empName").blur(function () {
ifUser();
});
// 前端email校验,会有bug
$("#email").blur(function () {
emailJudge();
});
});
function ifUser() {
var empName=$("#empName").val();
$.ajax({
url:"${basePath}/change",
type:"GET",
data:"empName="+empName,
success:function (data) {
if (data.code==="200"){
$("#empName").parent().removeClass("has-error");
$("#empName").parent().addClass("has-success");
$("#inputErrorName").text("");
//设置状态属性
$("#add_emp").attr("ajax_alert","success")
}else {
$("#empName").parent().removeClass("has-success");
$("#empName").parent().addClass("has-error");
$("#inputErrorName").text(data.map.success_msg);
$("#add_emp").attr("ajax_alert","error")
}
}
})
}
//前端email校验
function emailJudge() {
var email=$("#email").val();
var jemail=/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
if (!jemail.test(email)) {
$("#email").parent().addClass("has-error");
$("#inputErrorEmail").text("请输入正确的邮箱格式");
return false;
}
if (jemail.test(email)) {
$("#email").parent().removeClass("has-error");
$("#email").parent().addClass("has-success");
$("#inputErrorEmail").text("");
}
}
//表单校验,前端校验
function userJudge() {
var username= $("#empName").val();
//帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
var juser=/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/;
if (!juser.test(username)) {
$("#empName").parent().addClass("has-error");
$("#inputErrorName").text("字母开头,允许3-16位,允许字母数字下划线");
return false;
}
if (juser.test(username)) {
$("#empName").parent().removeClass("has-error");
$("#empName").parent().addClass("has-success");
// $("#inputErrorName").text("")
ifUser();
return true;
}
return false;
}
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<%--弹出框新增--%>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">新增员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="_form">
<div class="form-group">
<label for="empName" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="empName" id="empName" placeholder="Name">
<span id="inputErrorName" class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender_1" value="1" checked="checked">男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender_0" value="0">女
</label>
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="email" id="email" placeholder="Email">
<span id="inputErrorEmail" class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="_deptId" class="col-sm-2 control-label" >部门</label>
<div class="col-sm-10">
<select class="form-control" name="deptId" id="_deptId">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" name="_reset" id="_reset">
清空
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="add_emp">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button id="add_bnt" class="btn btn-success" data-keyboard="true" >
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
</button>
<button id="del_bnt" class="btn btn-warning">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-hover" 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_count">
</div>
<div class="col-md-6" id="page">
</div>
</div>
</div>
</body>
</html>
里面还设计一个后端设计 @RequestMapping(value={"/change"},method = RequestMethod.GET)
@ResponseBody
public Msg queryIsEmpty(@RequestParam("empName") String empName){
boolean isEmpty = employeeService.queryIsEmptyByName(empName);
String s="^[a-zA-Z][a-zA-Z0-9_]{2,15}$";
boolean matches = empName.matches(s);
if (!matches){
return Msg.fail().add("success_msg","字母开头,允许3-16位,允许字母数字下划线");
}
if (isEmpty){
return Msg.success();
}else {
return Msg.fail().add("success_msg","用户名已存在");
}
}
上面这个设计,是纯手写的后端校验。JSR303 是一个后端校验的框架,这两个是区分开的。
编辑员工
-
编辑员工我们要弹出模态框,然后将数据回显在表单上,最后做数据验证。 -
首先是探出模态框,和添加员工的jsp代码一样,但是属性名字不能一样。 <div class="modal fade" id="myModal_update" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel_update">修改员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="_form_update">
<div class="form-group">
<label for="empName_update" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="empName" id="empName_update" placeholder="Name">
<span id="inputErrorName_update" class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender_1_update" value="1" checked="checked">男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender_0_update" value="0">女
</label>
</div>
</div>
<div class="form-group">
<label for="email_update" class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="email" id="email_update" placeholder="Email">
<span id="inputErrorEmail_update" class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="_deptId_update" class="col-sm-2 control-label" >部门</label>
<div class="col-sm-10">
<select class="form-control" name="deptId" id="_deptId_update">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" name="_reset_update" id="_reset_update">
清空
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="add_emp_update">修改</button>
</div>
</div>
</div>
</div>
-
之后,便是进行数据回显,进行数据回显,我们要知道一点,这个编辑的按钮是随着数据的加载而产生的,也就是动态产生的,所以我们要为其绑定单击事件,一定要在有了该按钮的时候才可以绑定。 为了方便绑定,我们可以在产生数据的时候,为这个编辑的按钮增加一些属性。 var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_emp")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
editBtn.attr("this_id",item.empId);
editBtn.attr("this_deptId",item.deptId);
-
可以看到,上面我们将编辑的按钮动态的增加了两个属性,然后我们为编辑这个按钮增加一个单击事件,但这个按钮的单击事件要注意,因为这个是根据数据显示才出现的按钮,所以要在数据加载时绑定。绑定方法有些不同。
$(document).on("click",".edit_emp",function () {
getDepts("#_deptId_update")
var id=$(this).attr("this_id");
$("#add_emp_update").attr("empId",id)
var deptId=$(this).attr("this_deptId");
this_list(id,deptId);
$("#myModal_update").modal({
backdrop:"static"
});
})
function this_list(id,deptId) {
$.ajax({
url:"${basePath}/emp",
type:"GET",
data:"empId="+id,
success:function (data) {
var this_data=data.map.thisEmp;
$("#empName_update").attr("value",this_data.empName).text(this_data.empName);
var gender=this_data.gender;
if (gender==="1"){
$("#gender_1_update").val([gender]);
}else{
$("#gender_0_update").val([gender]);
}
$("#email_update").attr("value",this_data.email).text(this_data.email);
$("#_deptId_update option[value="+deptId+"]").prop("selected", true);
}
})
}
@RequestMapping(value = {"/emp"},method = RequestMethod.GET)
@ResponseBody
public Msg queryEmpById(@RequestParam("empId")Integer empId){
Employee employee = employeeService.queryEmpById(empId);
return Msg.success().add("thisEmp",employee);
}
注意:在ajax中,除了get和post以外的请求是不支持的,除非你的加了个参数_method=put…,若你想在type中直接输入请求方式而不加参数,要在web.xml中设置过滤器参数
<filter>
<filter-name>FormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-
搜索了数据之后,就可以根据表单进行回显数据,回显成功后,就可以和前面的一样,进行前端和后端的验证
$(function () {
$("#empName").blur(function () {
ifUser();
});
$("#email").blur(function () {
emailJudge("#email","#inputErrorEmail");
});
$("#email_update").blur(function () {
emailJudge("#email_update","#inputErrorEmail_update");
});
$("#empName_update").blur(function () {
userJudge("#empName_update","#inputErrorName_update");
});
});
前端验证完毕后,就可以进行修改的单击事件的绑定 $(document).on("click","#add_emp_update",function () {
var id=$(this).attr("empId");
$.ajax({
url:"${basePath}/emp/"+id,
type:"PUT",
data:$("#myModal_update form").serialize(),
success:function (data) {
if (data.code==="200"){
$("#add_emp_update").attr("ajax_alert","success")
$("#myModal_update").modal('hide');
clear("#myModal_update form");
to_page(presentPage);
}else {
$("#add_emp_update").attr("ajax_alert","error")
if (!data.map.isNoEmpty){
$("#empName_update").parent().addClass("has-error");
$("#inputErrorName_update").text(data.map.isNoEmptyText);
}
if (undefined!==data.map.resultMapPlus.email){
$("#email_update").parent().addClass("has-error");
$("#inputErrorEmail_update").text(data.map.resultMapPlus.email);
}
if (undefined!==data.map.resultMapPlus.empName){
$("#empName_update").parent().addClass("has-error");
$("#inputErrorName_update").text(data.map.resultMapPlus.empName);
}
}
}
})
});
我们可以看到,单击事件成功后,在后端会回传数据,下面是后端的代码
@RequestMapping(value = {"/emp/{empId}"},method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmp(@Valid Employee e,BindingResult result){
Map<String,Object> resultMap=new HashMap<String, Object>();
if (result.hasErrors()){
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError error:fieldErrors
) {
resultMap.put(error.getField(),error.getDefaultMessage());
}
return Msg.fail().add("resultMapPlus",resultMap);
}
Boolean isEmpty= employeeService.queryIsEmptyOtherName(e.getEmpId(),e.getEmpName());
if (isEmpty) {
return Msg.fail().add("isNoEmpty",false).add("isNoEmptyText","员工已存在");
}
employeeService.updateEmp(e);
return Msg.success().add("isNoEmpty",true).add("isNoEmptyText","");
}
这个后端便同时进行了数据的查询和验证。 -
注意:在做编辑员工的时候,原来一些方法是写死的,所以这里我做了一些简单的抽取。
删除员工
简单删除
-
在每一列中,我们可以点击删除按钮,提示是否删除 -
其原理和编辑员工几乎一样,首先得获取该tr里的id,用于后端删除,还有tr里的name,用于前端警告 var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm del_emp")
.append($("<span></span>").addClass("glyphicon glyphicon-trash del_emp")).append("删除");
delBtn.attr("this_id",item.empId);
-
然后发送ajax请求,这里开始验证 $(document).on("click",".del_emp",function () {
var id=$(this).attr("this_id");
var name=$(this).parents("tr").find("td:eq(1)").text();
if (confirm("你确定要删除"+name+"吗?")){
$.ajax({
url:"${basePath}/emp",
type:"delete",
data:"empId="+id,
success:function (data) {
to_page(presentPage);
}
})
}
})
-
后端代码比较简单,这里省略。
全选删除
-
前面我们做的列表是没有全选的,所以这里我们要为其添加上全选按钮,但是要注意的一个细节是,前面的简单删除,我们获取员工时用的eq,所以现在得改变 <th>
<label for="checkAll" >
<input type="checkbox" name="checkAll" id="checkAll">
</label>
</th
然后我们要为每个tr都添加这个input var empChecked=$("<td></td>").append("<input type='checkbox' class='check_item'/>")
var tr=$("<tr></tr>").append(empChecked).append(empIdTd).append(empNameTd)
.append(genderTd).append(emailTd).append(deptNameTd)
.append(btnTd)
-
然后我们为全选input添加click属性,也要为单个input添加click属性
$(function () {
$("#checkAll").click(function () {
let allChecked = $(this).prop("checked");
$(".check_item").prop("checked",allChecked);
});
$(document).on("click",".check_item",function () {
let totalLength=$(".check_item").length;
let itemLength=$(".check_item:checked").length;
if (totalLength===itemLength) $("#checkAll").prop("checked",true);
if (totalLength!==itemLength) $("#checkAll").prop("checked",false);
})
})
-
之后就是点击总删除按钮,提示信息,发送请求 $(function () {
$("#del_bnt").click(function () {
var empNames="";
var empIds="";
$.each($(".check_item:checked"),function () {
empNames+=$(this).parents("tr").find("td:eq(2)").text()+",";
empIds+=$(this).parents("tr").find("td:eq(1)").text()+"-";
})
empNames=empNames.substring(0,empNames.length-1);
empIds=empIds.substring(0,empIds.length-1);
let confirmAll;
if (totalPage===0){
confirm("该表没有员工数据!")
} else if (empNames.length===0){
confirm("你还未选中,请选择!")
} else if ($("#checkAll").prop("checked")){
confirmAll=confirm("你确定全部删除吗?");
} else {
confirmAll=confirm("确定要删除"+"["+empNames+"]"+"吗?")
}
if (confirmAll){
$.ajax({
url:"${basePath}/emp",
type:"delete",
data:"empIds="+empIds,
success:function (data) {
to_page(presentPage);
$("#checkAll").prop("checked",false);
}
})
}else {
$("#checkAll").prop("checked",false);
$.each($(".check_item:checked"),function () {
$(this).prop("checked",false);
})
}
});
})
-
发送请求后,我们去后端写代码,注意这里的Controller代码可以在单个删除的原基础上改变成批量删除 @RequestMapping(value = {"/emp"},method = RequestMethod.DELETE)
@ResponseBody
public Msg delEmp(@RequestParam("empIds")String empIds){
if (empIds.length()==1) {
employeeService.delEmp(Integer.parseInt(empIds));
}else {
List<Integer> list=new ArrayList<Integer>();
String[] empIdsString = empIds.split("-");
for (String s:empIdsString
) {
list.add(Integer.parseInt(s));
}
employeeService.delEmpByBath(list);
}
return Msg.success();
}
public void delEmpByBath(List<Integer> list){
EmployeeExample example = new EmployeeExample();
example.createCriteria().andEmpIdIn(list);
employeeMapper.deleteByExample(example);
}
public void delEmp(Integer empId) {
employeeMapper.deleteByPrimaryKey(empId);
}
|