ssm_crud项目
一、项目说明
1.项目预览
2.项目的功能点
- 1.分页
- 2.数据校验
- jquery前端校验+JSR303后端校验
- 大量使用ajax
- Rest风格的URI:使用http协议请求方式的动词,来表示对资源的操作(GET(查询),POST(新增),PUT(修改),DELETE(删除))
3.项目技术点
- 1.基础框架 ssm(spring+springmvc+mybatis)
- 2.数据库 mysql
- 3.项目的依赖管理管理 maven
- 4.分页 pagehelper
- 5.逆向工程 Mybatis-Gennerate
二、基础环境的搭建
1.创建maven工程
- 在idea中创建maven项目,选阿帕奇的webapp
2.引入项目依赖的jar包
在pom.xml文件中导入以下jar包的依赖点击进入maven仓库中央仓库
- spring (Spring JDBC(事务控制);Spring Aspects(切面编程))
- springmvc(Spring Web MVC)
- mybatis (Mybatis;Mybatis Spring(整合包))
- 数据库连接池(c3p0),驱动包(MySQL Connect)
- 其他(Jstl Servlet Api junit)
3.引入bootstrap,一个前端的框架
- 1.去bootstrap官网下载
- 2.在webapp下新建一个static文件夹,把bootstrap的文件放进去
- bootstrap的用法,参照官方文档
- 引入bootstrap之前一点要先引入jQuery(在webapp下新建一个js文件夹,去放jQuery)
- 引入的方式
三、配置文件的编写
1.web.xml的配置
(1)启动spring容器的监听器(项目一起动,spring的容器就启动)
- 配置监听器:项目一启动,就会加载spring的配置配置文件(指定spring配置文件的位置)
1
2
3
4
5
6
7
8
9
10<!-- 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> - spring的配置文件:主要配合和业务逻辑有关的
(2)配置前端控制器
用来拦截所有的请求
指定springmvc配置的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!--2、SpringMVC的前端控制器,拦截所有请求-->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>注意:如图是指定springmvc位置的配置方法,也可以不指定位置。就是在与web.xml同级的目录下创建一个servlet名-servlet的文件,如图就是创建一个springDispatcherServlet-servlet.xml的文件就可以代替springmvc.xml的文件
(3)配置字符编码过滤器(防止乱码)
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!--字符编码过滤器(所有过滤器之前) -->
<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-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>(4)配置支持Rest风格URI的过滤器
因为页面无法支持delete、put请求,还需要一个过滤器
代码
1
2
3
4
5
6
7
8
9
10<!--使用Rest风格的URI-->
<!-- HiddenHttpMethodFilter:将普通的get/post请求转化为put/delete请求 -->
<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>2.springmvc.xml的配置
主要包含网站跳转逻辑的组件
(1)扫描所有的业务逻辑组件
在java.com.wantao包下创建bean、controller、service、dao、utils、test包
我们希望只扫描controller(控制器)
1
2
3
4
5<!--springmvc的配置文件,包含网站跳转逻辑控制,配置 -->
<context:component-scan base-package="com.wantao" use-default-filters="false">
<!--只扫描控制器-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>(2)配置视图解析器
方便页面的解析
在WEB-INF下建一个view包
1
2
3
4
5<!--配置视图解析器,方便页面返回-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>(3)两个标准配置
代码
1
2
3
4
5<!--两个标准配置-->
<!-- 支持springmvc更高级的功能,比如:,JSR303校验,快捷的ajax,映射动态请求 -->
<mvc:annotation-driven/>
<!-- 将springmvc不能处理的请求交给tomcat -->
<mvc:default-servlet-handler />
3.applicationContext.xml的配置
- 主要是配置和逻辑有关的
- Spring配置文件的核心点:(数据源、与mybatis的整合、事务控制)
(1)配置数据源(c3p0)
- 代码
1
2
3
4
5
6
7
8
9
10
11<!--配置数据源-->
<bean id="PoolDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql://47.94.229.156:3306/ssm_crud?serverTimezone = GMT"></property>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver">
</property>
<property name="user" value="root">
</property>
<property name="password"
value="root">
</property>
</bean> - 也可以把连接信息写在jdbc.properties文件中
(2)和mybatis的整合配置
- 配置SqlSessionFactoryBean,他能创建SqlSessionFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<!--================配置spring和mabatis的整合=================== -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml">
</property>
<!--配置数据源 -->
<property name="dataSource" ref="PoolDataSource">
</property>
<!--配置mybatis映射文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml">
</property>
</bean>
<!-- 设置扫描器,将mybatis的接口实现类的实现(代理对象)添加到ioc容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao接口的实现,加入到IoC容器中-->
<property name="basePackage" value="com.wantao.dao">
</property>
</bean>
<!--====================================--> - 注意:在spring和mybatis整合的时候,并不是一定要把mapper的映射文件放到和dao层的文件一个路径,也可以在sqlSessionFactory中配置映射文件存放的位置;在MapperScannerConfiger中配置要扫描的dao层
(3)事务控制的配置
- 要能管事务,就是控制住数据源
1
2
3
4
5
6<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制数据源-->
<property name="dataSource" ref="PoolDataSource">
</property>
</bean>
- 1.开启注解的事务,2.使用xml配置形式的事务(比较重要的都是采用配置式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--开启注解事务或者使用xml配置形势的事务(主要的都是配置式)-->
<aop:config>
<!--切点表达式 -->
<aop:pointcut expression="execution(* com.wantao.service..*(..))" id="txPointCut" />
<!--配置事物增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
<!--配置事物增强,事务如何切入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--代表所有方法都是事务方法-->
<tx:method name="*" />
<!--以find开头的所有方法-->
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
</tx:attributes>
</tx:advice>3.mybatis-config.xml(全局配置文件+mapper文件)
全局配置文件
- 注意:这个全局配置文件不是必须的,也可以放在放在applicationContext.xml中,以property的形势放在class为SqlSessionFactoryBeanbean中。
- 搜索mybatis的官方文档(点击进入)
- 找到表头,复制进去
1
2(1)配置驼峰命名规则
- 代码
1
2
3
4
5<!-- 是否开启自动驼峰命名规则(camel case)映射,
即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>(2)配置类型别名
- 代码
1
2
3<typeAliases>
<package name="com.wantao.bean" />
</typeAliases>
配置mapper文件
- 一个mapper对应一个dao,所以需要先创建数据库
- 然后用逆向工程生成需要的dao、mapper、xml
4.逆向工程
- 去网页搜索 Mybatis Generator(GitHub–>进入帮助文档)点击进入
(1)引入依赖jar
- 在pom.xml文件中添加
1
2
3
4
5
6<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>(2)创建连接数据库的mgb.xml配置文件
- 网页位置
1 |
|
(3)创建MGBTest类完成逆向工程的创建
网页的位置
在test包下,创建一个MGBTest类
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class MGBTest {
public void gennerator()
throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("src/main/resources/mgb.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);
}
}
直接执行就可以获得逆向工程的映射文件和dao层接口、mapper、xml以及pojo
(4)生成的不一定符合要求,再添加自己的需求(联合查询)
查员工信息的时候,希望根据员工的部门号查出员工的部门名
1.EmployeeMapper.java中新增两个能查出部门的方法
1
2
3
4//带部门的查询方法List<Employee>
selectByExampleWithDept(EmployeeExample example);
//带部门的查询方法Employee
selectByPrimaryKeyWithDept(Integer empId);2.在Employee.java中引入部门信息
1
2
3
4
5
6
7
8//希望查询员工的同时,部门信息也是查询好的。
private Department department;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}3.在EmployeeMapper.xml文件中添加新建的两个带部门的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39<sql id="Base_Column_List_With_Dept">
emp_id, emp_name, gender, email,d_id,dept_id,dept_name
</sql>
<resultMap id="BaseResultMapWithDept" type="com.wantao.bean.Employee">
<id column="emp_id" jdbcType="INTEGER" property="empId" />
<result column="emp_name" jdbcType="VARCHAR" property="empName" />
<result column="gender" jdbcType="CHAR" property="gender" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="d_id" jdbcType="INTEGER"
property="dId" />
<!-- 这里注意使用association进行结果的联合显示 -->
<association javaType="com.wantao.bean.Department" property="department">
<id column="dept_id" property="deptId" />
<result column="dept_name" jdbcType="VARCHAR" property="deptName" />
</association>
</resultMap>
<select id="selectByExampleWithDept" resultMap="BaseResultMapWithDept">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List_With_Dept" />
from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.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="BaseResultMapWithDept">
select
<include refid="Base_Column_List_With_Dept" />
from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.dept_id
where emp_id = #{empId,jdbcType=INTEGER}
</select>
5.创建测试类,测试逆向工程生成的方法
- 需求:1.测试是否数据库已经连通,生成的逆向工程是否可用;2.生成初始数据,用于后面的测试
- 分析:此时的mybatis是和spring整合的,有以下两种方法
在test包下创建一个MapperTest类,用于测试Dao层的方法
方法一:通过原始方法进行测试
1
2
3
4
5
61.创建springIoC容器
2.从容器中获取mapper
//1.创建springIoC容器
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中获取
DepartmentMapper bean = ac.getBean(DepartmentMapper.class);
- 方法二:通过spring-test进行测试
推荐spring的项目就可以使用Spring的单元测试,看自动注入我们需要的组件
* 1.要导入spring的单元测试模块 @RunWith(SpringJUnit4ClassRunner.class),需要在pom.xml导入spring-test的依赖,可以在maven中央仓库搜索
1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
<scope>test</scope>
</dependency>
2.通过@ContextConfiguration指定spring配置文件的位置,他能自动创建出IoC容器; @RunWith(SpringJUnit4ClassRunner.class),指定具体单元测试的组件(这里使用spring的单元测试);需要注入什么组件,直接autowire注入组件
(1)测试spring
1
2
3
4
5
6
7
8.class)//指定具体单元测试的组件(这里使用spring的单元测试) (SpringJUnit4ClassRunner
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文)
public class DaoTest2 {
DepartmentMapper departmentMapper;
public void testCRUD(){
System.out.println(departmentMapper);//测试spring配置是否ok
}结果如下:
org.apache.ibatis.binding.MapperProxy@314c8b4a
这样就说明spring的配置成功了。
(2)插入几个部门
- 用有参构造器实现插入操作,在Department中生成有参构造器
- 注意:一旦生成有参构造构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Department {
private Integer deptId;
private String deptName;
//因为生成了有参的构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
public Department() {
}
public Department(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
.class)//指定具体单元测试的组件 (SpringJUnit4ClassRunner
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文)
public class DaoTest2 {
DepartmentMapper departmentMapper;
public void testDao() {
//1.插入几个部门
departmentMapper.insertSelective(new Department(null,"测试部"));
departmentMapper.insertSelective(new Department(null,"开发部"))
}
}
如果数据库中有数据,则说明mybatis配置正确。
这个时候进行测试的时候,不知道为啥就是连不上数据库,后来把jdbc.properties里的内容直接写入applicationContext,问题解决。
***
(3)测试员工插入,生成员工的数据,用于后面的开发
* 同样为了方便,给Employee生成有参无参构造器
1
2
3
4
5
6
7
8
9
10
public Employee(Integer empId, String empName, String gender, String email, Integer dId) {
this.empId = empId;
this.empName = empName;
this.gender = gender;
this.email = email;
this.dId = dId;
}
public Employee() {
}
在上面的测试类中直接加入下面的代码即可
1
employeeMapper.insertSelective(new Employee(null,"jerry","M","23@qq.com",1));
(4)重要需求:想要批量的添加员工用于测试,这个时候就需要sqlSession
1.首先,在applicationContext.xml中配置sqlSession
1
2
3
4
5<!--配置批量的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>2.在测试类中注入SqlSession
1
2
3
4
5
6
7
8
9
10
11
12
SqlSession sqlSession;// 批量处理
3.批量生成1000个员工用于测试
public void testDao() {
//批量插入多个员工,使用可以批量操作的sqlSession(常用语生成测试数据)
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 1000; i++) {
String uuid = UUID.randomUUID().toString().substring(0, 5) + i;
employeeMapper.insertSelective(new Employee(null, uuid, "M", uuid + "@qq.com", 1));
}
查看数据库的数据,如果加入,则成功
到此,测试就完成了
四、完成查询逻辑
(一)不分页查询
1.访问index.jsp页面
2.index.jsp页面发出查询员工列表请求
规定查询请求的URI为
/emps
在index.jsp页面直接转到当前项目下的emps请求
1
2<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<jsp:forward page="/emps"></jsp:forward>3.EmployeeController来接收请求,查出员工数据
想要接受到emps的请求,就要写一个处理器,在controller包中,写一个EmployeeController的类,用这个类处理员工的增删改查请求。
在EmployeeController的类中添加查找所有员工的方法
1
2
3
4
5
6
7
8
9
10
public class EmployeeController{
EmployeeService employeeService;
"/emps") (
public String getEmps(){
List<Employee> emps = employeeService.getAll();
return "show";
}
}- 查询需要service层的方法,在controller层自动注入EmployeeService类,让service层查询方法,所以,要在service包下创建EmployeeService.java
在EmployeeService类中,创建需要的方法
1
2
3
4
5
6
7
8
public class EmployeeService {
EmployeeMapper employeeMapper;
public List<Employee> findAllEmployee() {
//查询所有的(带上部门信息),所以调节为null
return employeeMapper.selectByExampleWithDept(null);
}查询还需要Mapper里相应的方法,自动注入(@Autowired)
通过视图解析器,拼串后会来到/WEB-INF/views/show.jsp页面做展示*

来到show.jsp页面展示
在WEB-INF下创建views包,在包下创建show.jsp
(二)分页查询
1. 在controller层中传入要查询的页数,默认值为1
在controller层中传入要查询的页数,默认值为1
1
2
3
4
5
6
7
8
9
10
11"/emps") (value =
public String showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) {
//引入PageHelper分页插件
PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据
List<Employee> employees = employeeService.findAllEmployee();
//用pageInfo封装然后交给页面,传入查询出来的数据
PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页
model.addAttribute("pageInfo",pageInfo);
return "show";
}
2. 引入PageHelper分页插件
- 搜索pagehelper插件(GitHub)–》中文文档点击进入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());//当前页码
assertEquals(10, page.getPageSize());//每页有多少条记录
assertEquals(1, page.getStartRow());//开始的记录
assertEquals(10, page.getEndRow());//结束的记录
assertEquals(183, page.getTotal());//总记录数
assertEquals(19, page.getPages());//总页码数
assertEquals(1, page.getFirstPage());//第一页
assertEquals(8, page.getLastPage());//最后一页
assertEquals(true, page.isFirstPage());//是否第一页
assertEquals(false, page.isLastPage());//是否最后一页
assertEquals(false, page.isHasPreviousPage());//是否有前一页
assertEquals(true, page.isHasNextPage());//是否有后一页 - 用PageInfo包装查出来的数据,查询出来的数据写到Model中
- 在pom.xml文件中引入相应的依赖
1
2
3
4
5<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency> - 在mybatis全局配置中注册这个插件:–》中文文档往下翻
1
2
3
4
5
6
7<!-- 引入分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页参数合理化,不可能<1,或者>最大页码-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>3.写单元测试(用spring的单元测试),测试利用分页插件能否取到值
- 1.在test包下,创建一个MVCTest的测试类
- 2.spring4测试的时候,需要servlet3.0的支持,导包的时候,需导入3.0以上的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35.class) (SpringJUnit4ClassRunner
@WebAppConfiguration//获取web的IoC
@ContextConfiguration(locations= {"classpath:applicationContext.xml","classpath:springmvc.xml"})
public class EmployeeControllerTest { //传入Springmvc的IoC
WebApplicationContext context;//传入Springmvc的ioc,为了能使它自动注入,需要@WebAppConfiguration
MockMvc mockMvc;//虚拟mvc请求,获取到处理结果
//要想使用MockMvc,需要初始化
public void init() {//需要被创建,才可以使用
mockMvc=MockMvcBuilders.webAppContextSetup(context).build();
}
public void testPage() throws Exception {
//模拟请求,拿到返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn","5")).andReturn(); //请求成功后请求域中有pageInfo对象,我们取出pageInfo进行验证
MockHttpServletRequest
request=result.getRequest();
PageInfo pageInfo=(PageInfo)
request.getAttribute("pageInfo");
System.out.println("当前页码"+pageInfo.getPageNum());
System.out.println("总页码数"+pageInfo.getPages());
System.out.println("总记录数"+pageInfo.getTotal());
System.out.println("当前页面需要连续显示的页码");
int [] nums = pageInfo.getNavigatepageNums();
for(int i : nums) {
System.out.print(" "+i);
}
//获取员工数据
List<Employee>
employees = pageInfo.getList();
for(Employee employee:employees{
System.out.println("ID"+employee.getEmpId()+"===>Name:"+employee.getEmpName());
}
}
}五、完成查询界面构建(bootstrap)
- 逻辑:先来到index.sjp首页,首页会直接转到的emps请求,controller收到请求后,查询到所有的数据和分页信息后,然后来到show.jsp页面,所以我们在show.jsp页面展示员工列表就行了。
1.引入bootstrap
- 开头代码
1
2
3
4
5
6
7
8<%//存入当前项目的名称找 pageContext.setAttribute("APP_PATH",request.getContextPath());
%>
<link rel="stylesheet"
href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"></link>
<script type="text/javascript"
src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
<script type="text/javascript"
src="${APP_PATH}/static/jquery-3.1.1.min.js"></script>2.利用bootstrap搭建页面
创建栅格点击进入
- 根据官方文档可以得知:把div的class设为container即可设为栅格系统
- 分成四行,第一行显示标题,第二行显示新增和删除的按钮,第三行显示查询出的数据,第四行显示分页信息
- 每一行的class叫row,每一列的为col-md-(数字1-12)
1
2
3
4
5
6
7
8
9
10
11
12
13
14<div class="container">
<!--标题 -->
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div>
<!--按钮 -->
<div class="row"></div>
<!--显示表格数据 -->
<div class="row"></div>
<!--显示分页信息-->
<div class="row"></div>
</div>3.添加删除和新增按钮
- 设置按钮的位置(在列偏移中设置自己占4列,偏移8列)
1
2
3
4
5
6
7<!--按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button>新增</button>
<button>删除</button>
</div>
</div> - 美化按钮(根据官方文档,只需要给相应按钮的class增加值即可)
1
2
3
4
5
6
7<!--按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary">新增</button>
<button class="btn btn-danger">删除</button>
</div>
</div>4.添加显示数据的table
- 设置table的样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<table>
</div>
</div>5.添加分页显示
- 组件–》分页,在里面添加分页信息 现在页面已经搭起来了,后面要做的就是把查出来的数据,放到页面中显示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31<!--显示分页信息-->
<div class="row">
<!--分页文字信息 -->
<div class="col-md-6">
</div>
<!--分页条信息-->
<div class="col-md-6">
<nav aria-label="Page navigation">
<ul class="pagination">
<li><a href="#">首页</a></li>
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<li><a href="#">末页</a></li>
</ul>
</nav>
</div>
</div>
六、把数据插到页面中
1.引入标签库
- 员工的数据需要遍历得到,引入相应的标签库
1
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
2.遍历员工数据并填充到相应的位置
- 遍历的取出查出的数据,之前已经把数据都放到了pageInfo里面,在list里面存着
1
<c:forEach items="${pageInfo.list}" var="emp">
- 把取出的数据填充到相应的位置,并为 每一行的数据添加编辑和删除按钮 这样运行就可以看到:数据已经填充好了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<c:forEach items="${pageInfo.list}" var="emp">
<tr>
<th>${emp.empId }</th>
<th>${emp.empName }</th>
<th>${emp.gender=="M"?"男":"女" }</th>
<th>${emp.email}</th>
<th>${emp.department.deptName }</th>
<th>
<th>
<button class=" btn btn-info">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>编辑
</button>
<button class="btn btn-primary">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除
</button>
</th>
</tr>
</c:forEach>3.填充分页信息
(1) 填充文字信息
- 写在前6列
1
2
3
4<!-- 分页文字信息 -->
<div class="col-md-6">
<p>当前第${pageInfo.pageNum}页,共${pageInfo.pages}页,共${pageInfo.total }条记录</p>
</div>(2) 填充分页条信息
- 分页条逻辑
- 1.如果你是当前页码,让它高亮显示
- 2.如果当前页码是第一页,首页和上一页为关闭状态
- 3.如果当前页码是最后一页,末页和下一页为关闭状态
- 4.点击第几页就跳到第几页(包括首页、末页、上一页、下一页)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36<!-- 分页条信息 -->
<div class="col-md-6">
<nav aria-label="page navigation">
<ul class="pagination">
<li><a href="${APP_PATH}/emps?pn=1">首页</a></li>
<!-- 如果是首页则禁止点击前一页 -->
<c:if test="${pageInfo.isFirstPage }">
<li class="disabled"><a href="#" aria-label="Previous"><spanaria-hidden="true">«</span></a></li>
</c:if>
<c:if test="${!pageInfo.isFirstPage }">
<li><a href="${APP_PATH}/empspn=${pageInfo.prePage}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>
</c:if>
<!-- 显示连续的页面并使得当前页面为高亮 -->
<c:forEach items="${pageInfo.navigatepageNums }" var="pageNum">
<%--判断是不是当前查的页面,如果是高亮,不是就不高亮--%>
<c:if test="${pageNum==pageInfo.pageNum }">
<li class="active"><a href="#">${pageNum }</a></li>
</c:if>
<%--判断不是当前查的页面,如点击哪页跳到哪一页--%>
<c:if test="${pageNum!=pageInfo.pageNum }">
<li><a href="${APP_PATH}/emps?pn=${pageNum}">${pageNum }</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.isLastPage }">
<li class="disabled"><a href="#" aria-label="Next">
<span aria-hidden="true">»</span></a></li>
</c:if>
<c:if test="${!pageInfo.isLastPage }">
<li><a href="${APP_PATH}/emps?pn=${pageInfo.nextPage}" aria-label="Next">
<span aria-hidden="true">»</span></a></li>
</c:if>
<li><a href="${APP_PATH}/emps?pn=${pageInfo.pages}">末页</a></li>
</ul>
</nav>
</div> - 分页查询总结:从首页转到/emps请求,请求由控制器收到,查出分页数据以及分页信息,转发到list页面,使用c:foreach和el表达式把页面遍历展示出来。*
七、用ajax改造查询请求
- 4.点击第几页就跳到第几页(包括首页、末页、上一页、下一页)
需求分析
- 遇到的问题:此前,完成了在首页直接转到/emps请求,由控制器收到,查出分页的数据以及分页的信息,然后转发到list页面。使用<c:foreEach> 和EL表达式把页面遍历展示出来。但是,现在的做法,只能适用于浏览器/服务器的交互模型。而在移动互联网的时代,发请求的客户端不仅仅是浏览器,有可能是安卓客户端,还有可能是iOS客户端,如果是他们给服务器发请求的话,响应给他们的是一系列页面,他们很难解析。
- 解决方法:服务器把数据,以json的形式,返回给一系列的客户端
- 为了满足浏览器、安卓、iOS的要求,用ajax请求
0.业务逻辑
- 1.index.jsp页面,直接发送ajax请求进行员工的分页查询
- 2.服务器将查出的数据,以json字符串的形式返回给浏览器
- 3.浏览器收到js字符串。可以使用js对json进行解析,使用js通过dom增删改的方式改变页面。
- 4.返回json。实现客户端的无关性。
1.在Empcontroller类中写一个返回json字符串的方法
- 页面需要分页数据,可以直接返回PageInfo对象,用@ResponseBody就能直接把对象转为json字符串
- 要想@ResposeBody正常工作需要导入jackson包
- 去中央仓库搜索Jackson databind
- 在pom.xml中引入相应的依赖
1
2
3
4
5<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
- 在pom.xml中引入相应的依赖
- controller中的方法为
1
2
3
4
5
6
7
8
9
10"/emps") (value =
public PageInfo showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) {
//引入PageHelper分页插件
PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据
List<Employee> employees = employeeService.findAllEmployee();
//用pageInfo封装然后交给页面,传入查询出来的数据
PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页
return pageInfo;
}
2.创建一个专门返回json数据的类
- 处理上面的数据后,成功失败这些状态信息,应该通知给浏览器。所以,创建一个通用的能带有状态信息的返回json数据的类
- 在bean包下创建一个Message类,完成上面的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59public class Message {
// 状态码(自定义)200 成功 404 失败
private int code;
private String msg;
// 服务器要返回给浏览器的数据
private Map<String, Object> data = new HashMap<String, Object>();
//请求成功
public static Message success() {
Message message = new Message();
message.setCode(200);
message.setMsg("处理成功");
return message;
}
//请求失败
public static Message fail() {
Message message = new Message();
message.setCode(404);
message.setMsg("处理失败");
return message;
}
//定义快捷添加信息的方法(方面链式操作)
public Message add(String key, Object value) {
//以下两种形式任选其一
this.data.put(key, value);
//this.getData().put(key, value);
return this;
}
public Message() {
super();
// TODO Auto-generated constructor stub
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
} - 然后希望返回给客户端的数据带上状态信息,于是要修改EmployeeController类里的返回值
1
2
3
4
5
6
7
8"/emps") (value =
public Message showEmployeesWithJson(@RequestParam(defaultValue = "1", name = "pn") Integer pn, Model model) {
PageHelper.startPage(pn, 10);// 后面紧跟的查询为分页查询,参数值为一页有多少数据。
List<Employee> employees = employeeService.findAllEmployee();
PageInfo pageInfo = new PageInfo(employees, 5);// 用pageInfo封装然后交给页面
return Message.success().add("pageInfo", pageInfo);
} - 此时在浏览器输入/emps请求,即可得到json的字符串如下
1
{"code":200,"msg":"处理成功","data":{"pageInfo":{"total":1150,"list":[{"empId":1,"empName":"selenium","gender":"M","email":"895484122@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":2,"empName":"9a1820","gender":"M","email":"9a1820@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":3,"empName":"f893d1","gender":"M","email":"f893d1@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":4,"empName":"409742","gender":"M","email":"409742@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":5,"empName":"dd0e63","gender":"M","email":"dd0e63@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":6,"empName":"270e54","gender":"M","email":"270e54@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":7,"empName":"1dc865","gender":"M","email":"1dc865@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":8,"empName":"c7a8e6","gender":"M","email":"c7a8e6@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":9,"empName":"f78987","gender":"M","email":"f78987@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":10,"empName":"1c8b78","gender":"M","email":"1c8b78@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}}],"pageNum":1,"pageSize":10,"size":10,"startRow":1,"endRow":10,"pages":115,"prePage":0,"nextPage":2,"isFirstPage":true,"isLastPage":false,"hasPreviousPage":false,"hasNextPage":true,"navigatePages":5,"navigatepageNums":[1,2,3,4,5],"navigateFirstPage":1,"navigateLastPage":5,"firstPage":1,"lastPage":5}}}
- 以上就是我们需要解析的数据。接下来,我们就从首页出发,发一个ajax请求,拿到这些数据,对这些数据拿json解析出来,使用dom增删改的形式把数据显示出来。
1.index.jsp页面,直接发送ajax请求进行员工的分页查询
(1)准备工作
- 原来是要发请求跳转到别的页面,现在直接在index页面中显示
- 把原来的的index.jsp重命为index1.jsp,然后重新建一个index.jsp页面
- 把show.jsp页面的代码全选复制到index.jsp内
- 把分页的文字信息删掉,分页条删掉,表格数据的显示<c:foreach>也删掉 ,以后的分页文字信息、分页条信息、表格的数据显示全是对于json数据的解析。
(2)从index.jsp发起请求
- 页面加载完成以后,直接发送一个ajax请求,要到一个分页数据
1
2
3
4
5
6
7
8
9
10
11
12<script type="text/javascript">
$(function () {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn=1",
type: "get",
success: function (result) {//请求成功的回调函数,result就是服务器响应给浏览器的数据
console.log(result);//到控制台,查看是否有数据
}
});
});
</script>(3)显示查询信息
- 查询界面的显示
- table的细化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<!--显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
<table>
</div>
</div>(4)解析list里的员工数据
- 把员工的数据和操作的按钮一起添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42function build_emps_table(result) {
//因为发送ajax请求,页面无刷新,需要先清空table表格,避免重复出现
$("#emps_table tbody").empty();
//得到所有员工的数据
var emps = result.data.pageInfo.list;
//遍历员工数据,回调函数:第一个参数是索引,第二个是对象
$.each(emps, function (index, item) {
//alert(item.empName);//测试一下能不能拿到员工数据
var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName);
var genderTd = $("<td></td>").append(item.gender == "M" ? "男" : "女");
var emailTd = $("<td></td>").append(item.email);
var deptNameTd = $("<td></td>").append(item.department.deptName);
/* <button class=" btn btn-info">
<span class="glyphicon glyphicon-pencil" aria-hidden="true">编辑</span>
</button>*/
var editBtn = $("<button></button>").addClass("btn btn-info edit_btn")
.append("<span></span>").addClass("glyphicon glyphicon-pencil")
.append("<span></span>").addClass("btn btn-default btn-sm")
.append("编辑");
//为编辑按钮添加一个自定义的属性,来表示当前员工的id
editBtn.attr("edit-id", item.empId);
var delBtn = $("<button></button>").addClass("btn btn-primary delete_btn")
.append("<span></span>").addClass("glyphicon glyphicon-remove")
.append("<span></span>").addClass("btn btn-default btn-sm")
.append("删除");
//为删除按钮添加一个自定义的属性,来表示当前删除的员工id
delBtn.attr("del-id", item.empId);
//把编辑和删除放到同一个单元格中
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
//可以链式操作的原因:append方法执行完成以后还是返回原来的元素
$("<tr></tr>").append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");//把它添加到的位置
});
}(5)解析分页信息
- 显示分页信息和分页条信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71//显示分页信息
function build_page_info(result) {
//在append之前清空分页信息
$("#page_info_area").empty();
$("#page_info_area").append("当前第" + result.data.pageInfo.pageNum + "页," +
"共" + result.data.pageInfo.pages + "页," +
"共" + result.data.pageInfo.total + "条记录");
totalRecord = result.data.pageInfo.total;
currentPage = result.data.pageInfo.pageNum;
}
//解析分页条
function build_emps_nav(result) {
//清空分页条
$("#page_nav_area").empty();
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.data.pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else {//在元素没有被禁用的时候才添加绑定事件
//为元素添加点击翻页的事件
firstPageLi.click(function () {
to_page(1);
});
prePageLi.click(function () {
to_page(result.data.pageInfo.pageNum - 1);
});
}
var nextPageLi = $("<li></li>").append($("<a></a>").append("»"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
//如果没有下一页,设置下一页和末页不能点
if (result.data.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {//在没有被禁用的时候才能发请求
nextPageLi.click(function () {
to_page(result.data.pageInfo.pageNum + 1);
});
lastPageLi.click(function () {
to_page(result.data.pageInfo.pages);
});
}
//添加首页和前一页的提示
ul.append(firstPageLi).append(prePageLi);
//遍历查询出的数据,并添加到ul
$.each(result.data.pageInfo.navigatepageNums, function (index, item) {
var numLi = $("<li></li>").append($("<a></a>").append(item));//取出来的当前元素
if (result.data.pageInfo.pageNum == item) {
//定位当前的也为亮色
numLi.addClass("active");
}
//给numLi绑定单击事件,一点击,发ajax请求,去当前页面
numLi.click(function () {
to_page(item);
});
ul.append(numLi);
});
//给ul添加下一页和末页
ul.append(nextPageLi).append(lastPageLi);
//把ul添加到nav
var navEle = $("<nav></nav>").append(ul);
//最后把导航条添加到要显示的nav中
navEle.appendTo("#page_nav_area");
} - 需要把从index页面发送请求的方式改一下,改成跳转页面的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function to_page(pn) {
$.ajax({
url:"${APP_PATH}/emp",
data:"pn="+pn,
type:"get",
success:function(result){//请求成功的回调函数,result就是服务器响应给浏览器的数据
//console.log(result);
//1.解析并显示员工数据
bulid_emps_table(result);
//2.解析并显示分页信息
//分页文字信息
build_page_info(result);
//分页条
build_emps_nav(result);
}
});
};
这样用户的信息就显示完全了,下面用ajax来完成剩下的功能
剩余项目
- 剩余的项目都用ajax请求完成
- 用rest风格的URI
REST风格
- URI*
路径 请求类型 需求 /em/{id} GET 查询员工 /emp POST 保存员工 /em/{id} PUT 修改员工 /em/{id} DELETE 删除员工
八、用户新增
0.逻辑
1.在index.jsp页面点击“新增”
2.弹出新增对话框(模态框)
3.去数据库查询部门列表显示在对话框中
4.用户输入数据,并进行校验,完成保存
1.点击新增,弹出模态框
(1)在页面中引入模态框
去bootstrap官网拷贝模态框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- 用户新增模态框 -->
<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">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>为新增按钮添加id
1
2
3<button class=" btn btn-danger">
<span class="glyphicon glyphicon-plus" aria-hidden="true" id="emp_add_model_btn">新增</span>
</button>(2)点击新增,弹出模态框
为新增按钮绑事件:结合文档中模态框的用法
1
2
3
4
5
6
7
8
9
10
11//添加按钮事件,点击按钮弹出模态框
$("#emp_add_model_btn").click(function () {
//清除表单数据(表单重置(表单的数据,表单的样式))dom
reset_form("#empAddModal form");
//$("#empAddModal form")[0].reset();
//发送ajax,查出部门信息,显示在下拉列表中
getDepts("#dept_add_select");
$("#empAddModal").modal({//点击背景不关闭模态框
backdrop: "static"
});
});(3)把模态框需要的样子定义出来
对标题、按钮修改,添加表单(水平表单,在bootstrap中找)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57<!-- 员工添加的模态框 -->
<div class="modal fade" id="empAddModal" 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_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input type="text" name="empName" class="form-control" id="empName_add_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="email_add_input" class="col-sm-2 control-label">email</label>
<div class="col-sm-10">
<input type="text" name="email" class="form-control" id="email_add_input"
placeholder="emil@qq.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">gender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_add_input" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptName</label>
<div class="col-sm-3">
<%--部门提交部门id--%>
<select class="form-control" name="dId" id="dept_add_select">
</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" id="emp_save_btn">保存</button>
</div>
</div>
</div>
</div>2.填充模态框中的部门信息
需求:希望部门信息是从数据库中查到的
分析:弹出模态框之前,发ajax请求,查出所有的部门信息,把部门信息显示到下拉列表中。
在index.jsp页面写一个查询部门信息的ajax请求的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//查出所有的部门信息,并显示在下拉列表中
function getDepts(ele) {
//清空之前下拉列表的值
$(ele).empty();
$.ajax({
url: "${APP_PATH}/depts",
type: "GET",
success: function (result) {
//console.log(result);//打印在控制台,用于测试
//显示部门信息在模态框中
//这个是按id找的,也可以按标签找$("#empAddModal select")
//$("#dept_add_select").append("")
//此时需要遍历的路径是result.data.depts,函数需要的变量可以写在函数的()里,如果不写,用this也可以
$.each(result.data.depts, function () {
var optinEle = $("<option></option>").append(this.deptName).attr("value", this.deptId);
optinEle.appendTo(ele);
});
}
})控制器中需要有一个拦截他的方法,在controller包下创建一个DepartmentController.java,用来处理和部门有关的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DepartmentController {
private DepartmentService departmentService;
/**
*
* @return返回所有的部门信息(json数据)
*/
"/depts") (value =
public Message getAllDepts() {
//查出的所有部门信息
List<Department> list = departmentService.getAllDepts();
return Message.success().add("depts",list);
}
}在service包下创建DepartmentService
1
2
3
4
5
6
7
8
9"departmentService") (
public class DepartmentService {
private DepartmentMapper departmentMapper;
public List<Department> getAllDepts() {
List<Department> list=departmentMapper.selectByExample(null);
return list;
}
}在打开模态框之前,发送查询部门的方法
3.点击保存按钮保存数据
0.逻辑
点击保存,发送ajax请求,保存数据
1.方法
为保存按钮添加id
id="emp_save_btn"
为保存按钮绑事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//点击保存员工
$("#emp_save_btn").click(function () {
//1.发送ajax请求保存员工
//jquery中提供了简单的把表单数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试
/*alert($("#empAddModal form").serialize());*/
$.ajax({
url: "${APP_PATH}/emp",
type: "post",
data: $("#empAddModal form").serialize(),
success: function (result) {
//测试是否保存成功
//alert(result.msg);
//员工保存成功后:1.关闭模态框;
$("#empAddModal").modal('hide');
// 2.来到最后一页,显示刚才插入的数据
//发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页)
//这里我吗用记录数,因为记录数,一定是大于页数的
to_page(totalRecord);
}
});
});在EmployeeContorller中添加保存的方法
1
2
3
4
5
6"/emp") (value =
public Message saveEmployee( Employee employee) {
employeeService.saveEmployee(employee);
return Message.success();
}在EmployeeService中创建员工保存的方法
1
2
3
4
5
6
7
8/**
* @param
* @return void
* @description 新增用户
*/
public void saveEmployee(Employee employee) {
employeeMapper.insertSelective(employee);
}
- 遇到的问题:如果用户名相同的时候,也是可以添加上,这样不符合业务的需求;
- 解决的方法:保存数据之前校验
4.保存数据之前先校验(前端校验)
(1)如果校验失败,返回false
(2)校验的方法
- 拿到需要校验的元素
- 通过正则表达式校验(jQuery文档)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function validate_add_form() {
//1.拿到要校验的数据,使用正则表达式
var empName = $("#empName_add_input").val();//通过id拿到值
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
/*alert(regName.test(empName));//先测试一下成功吗
return false;//return false先让数据不提交*/
//先校验用户名信息是否合法
if(!regName.test(empName)){
//1.如果校验失败
// alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
return false;
}
//2.如果没有失败,校验邮箱信息
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
//如果email校验失败了
alert("邮箱格式不正确");
return false;
}
//都成功,返回true
return ture;
} - 这样就完成了校验,但是,这种(alert)弹出的方式不太美观*
(3)使用bootstrap中的表单校验状态完善校验显示的功能
- 把弹窗提示删掉
- 给用户名和邮箱的input框里添加span标签
<span class="help-block"></span>
- 给父元素添加错误的样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32//校验表单数据
function validate_add_form() {
//1.拿到要校验的数据,使用正则表达式
var empName = $("#empName_add_input").val();//通过id拿到值
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
if(!regName.test(empName)){
//1.如果校验失败
// alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他
用show_validate_msg(ele,status,msg)方法替换他
$("#empName_add_input").parent().addClass("has-error");
$("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
return false;
}else{
$("#empName_add_input").parent().addClass("has-success");
$("#empName_add_input").next("span").text("");
}
//2.如果没有失败,校验邮箱信息
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
//如果email校验失败了
// alert("邮箱格式不正确");//美化替换他
$("#email_add_input").parent().addClass("has-error");
$("#email_add_input").next("span").text("邮箱格式不正确");
return false;
}else {
$("#email_add_input").parent().addClass("has-success");
$("#enum_add_input").next("span").text("");
}
//一切都正常,返回true
return true;
} - 这个时候:如果第一次输入的是错误的,会提示错误信息,输入框会变成红色;如果第二次输入正确,提示的文字框会消失,但是,输入框依旧是红色的;原因就是元素的class没有刷新,把has-error和has-success放在一起了*
- 清空样式
(4)把校验的方法抽取出来
- 抽取公共的方法
1
2
3
4
5
6
7
8
9
10
11
12function show_validate_msg(ele, status, msg) {
//如果第一次输入失败,即使第二次成功也是红框,所以:应该清空当前校验状态
$(ele).parent().removeClass("has-sucess 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);
}
}- 把抽取的方法应用到校验的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47//校验表单数据
function validate_add_form() {
//1.拿到要校验的数据,使用正则表达式
var empName = $("#empName_add_input").val();//通过id拿到值
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
/*alert(regName.test(empName));//先测试一下成功吗
return false;*/
/*if(!regName.test(empName)){
//1.如果校验失败
// alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他
用show_validate_msg(ele,status,msg)方法替换他
$("#empName_add_input").parent().addClass("has-error");
$("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
return false;
}else{
$("#empName_add_input").parent().addClass("has-success");
$("#empName_add_input").next("span").text("");
}*/
//用一个方法整合上面的
if (!regName.test(empName)) {
show_validate_msg($("#empName_add_input"), "error", "用户名可以是2-5位中文或者是2-16位英文和数字的组合")
} else {
show_validate_msg($("#empName_add_input"), "success", "")
}
//2.如果没有失败,校验邮箱信息
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
/* if(!regEmail.test(email)){
//如果email校验失败了
// alert("邮箱格式不正确");//美化替换他
$("#email_add_input").parent().addClass("has-error");
$("#email_add_input").next("span").text("邮箱格式不正确");
return false;
}else {
$("#email_add_input").parent().addClass("has-success");
$("#enum_add_input").next("span").text("");
}*/
//用一个方法整合上面的
if (!regEmail.test(email)) {
show_validate_msg("#email_add_input", "error", "邮箱格式不正确")
return false;
} else {
show_validate_msg("#email_add_input", "success", "")
}
//一切都正常,返回true
return true;
}
- 把抽取的方法应用到校验的位置
- 这个时候,如果有两个相同名字的用户是可以注册成功的,这样就不对了*
(5)ajax校验用户名是否重复
- 逻辑:把员工姓名输入完成后,就去给服务器发送一个ajax请求,服务器来告知我们这个用户名是否可用;如果可用,可用保存;如果不可用,提示错误信息
- 可以给员工姓名这一栏绑定一个change事件,当姓名改变时,做一些事情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//校验用户名是否可用
$("#empName_add_input").change(function () {
//发送ajax请求校验用户名是否可用
var empName = this.value;//this.value是输入框的值
$.ajax({
url: "${APP_PATH}/checkSameEmployee",
data: "empName=" + empName,
type: "post",
success: function (result) {
if (result.code == 200) {
show_validate_msg("#empName_add_input", "success", "用户名可用");
} else {
show_validate_msg("#empName_add_input", "error", "用户名不可用");
}
}
});
});
- 当员工姓名改变时,发送ajax请求,校验用户名是否可用
- 在EmployeeController里面编写一个检验用户名是否可用的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
* @param检查用户名是否可用
* @return Message
* @description:检测用户是否存在 success表示用户不存在可用,fail表示用户存在不可用
* 可以通过识别状态码来分辨用户名可用不可用
*/
"/checkSameEmployee") (value =
public Message checkSameEmployee(@RequestParam("empName")
String empName) {
boolean b = employeeService.checkSameEmloyee(empName);
if(b){
return Msg.success();
}else{
return Msg.fail();
}
}
- 在EmployeeService中创建相应的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* @param
* @return boolean
* @description 数据库中是否有empName,有返回false.没有返回true,true代表可用
*/
public boolean checkSameEmployee(String empName) {
EmployeeExample example = new EmployeeExample();
//创建查询条件
Criteria criteria = example.createCriteria();
//让员工的名字必须等于给定的值
criteria.andEmpNameEqualTo(empName);
//返回给定信息的记录数
long count = employeeMapper.countByExample(example);
//看返回值是否等于0(等于0,返回true,代表当前姓名可用;大于0返回false,不可用)
return count == 0;
} - 选择输入数据库中已经有的用户名,会提示错误,如果这个时候邮箱输入正确,尽管用户名提示错误,还是可以保存成功*
(6)完善用户名校验1
- ajax校验的信息保存起来:如果校验成功了,给保存按钮添加一个属性ajax-va;如果成功值就是success,如果失败值就是error
1
2
3
4
5
6
7
8if (result.code == 200) {
show_validate_msg("#empName_add_input", "success", "用户名可用");
//若果保存成功,就加一个属性,值为success
$("#emp_save_btn").attr("ajax-va", "success");
} else {
show_validate_msg("#empName_add_input", "error","用户名不可用");
$("#emp_save_btn").attr("ajax-va", "error");
} - 点击保存按钮的时候,要先判断ajax校验用户名是否成功
这个时候可以完成要求了,但是添加成功后,再次点击新增的时候,输入框中是有数据的,这个时候再次添加还会添加成功
解决方法:点击新增时,清空输入框中的数据并且情况输入框的状态
写一个能够清空表单内容和样式的方法
1
2
3
4
5
6
7
8//清空表单样式及内容
function reset_form(ele) {
//重置表单内容
$(ele)[0].reset();
//清空表单样式
$(ele).find("*").removeClass("has-error has-success");
$(ele).find(".help-block").text("");
}
这个时候我们输入aaa这个用户名数据库中确实没有,提示可用,但是一点保存,就会提示不可用
(7)完善用户名校验2
- 检查用户名是否可用之前,判断用户名是否合法(用后端来校验)
- 对EmployeeController中的方法进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17"/checkSameEmployee") (value =
public Message checkSameEmployee(@RequestParam("empName") String empName) {
//先判断用户名是否是合法的表达式
String regx = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
//看用户名是否匹配正则表达式
if(!empName.matches(regx)){//如果匹配失败
return Message.fail().add("va_msg","用户名必须是6-16位数字和字母的组合或者2-5位中文");
}
//数据库用户名重复校验
boolean b = employeeService.checkSameEmployee(empName);
if (b) {// 用户名不存在,可用
return Message.success();
} else {
return Message.fail().add("va_msg","用户名不可用");
}
} - 失败后的提示消息也是在result中取
这样前端校验就算完成了,但是前端校验可以在网页中修改,所有重要的数据也要在后端校验
5.重要数据的后端校验(JSR303)
以前完成了jQuery前端校验,ajax用户名重复校验(前端校验防君子,不防小人,所以后端校验很有必要),重要数据要加上后端校验(JSR303);
重要的数据:前端校验+后端校验+数据库的约束
(1)想要实现JSR303的支持,必须要导入Hibernate-Validator包
1
2
3
4
5
6
7<!-- hibernate-validator jsr303数据校验 -->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.5.Final</version>
</dependency>(2)后端校验的实现
在bean包下的Employee类中,对需要后端校验的属性添加相应的注解
在Controller中,员工保存的方法,对相应的对象添加@Valid注解,用BindingResult result来封装校验的结果
如果校验成功,就提示成功,如果校验失败就返回失败
如果校验失败,返回失败的信息,在输入框的下方显示失败的信息(在result中提取失败的信息,把错误的信息都封装到map中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17"/emp") (value =
public Message saveEmployee(@Valid Employee employee, BindingResult result) {
// System.out.println(employee);
if (result.hasErrors()) {// 后端校验失败,返回校验失败的信息
Map<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
System.out.println("错误的字段命名:");
map.put(error.getField(), error.getDefaultMessage());
}
return Message.fail().add("errorField", map);
} else {
employeeService.saveEmployee(employee);
return Message.success();
}
}到首页去完成逻辑的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30//2.发送ajax请求保存员工
//jquery中提供了简单的把数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试
/*alert($("#empAddModal form").serialize());*/
$.ajax({
url: "${APP_PATH}/emp",
type: "post",
data: $("#empAddModal form").serialize(),
success: function (result) {
//后端校验是否保存成功
if (result.code == "200") {
//alert(result.msg);
//员工保存成功后:1.关闭模态框;
$("#empAddModal").modal('hide');
// 2.来到最后一页,显示刚才插入的数据
//发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页)
//这里我吗用记录数,因为记录数,一定是大于页数的
to_page(totalRecord);
} else {
//显示失败信息
// consol`e.log(result);
if (undefined != result.data.errorField.email) {
//显示邮箱的错误信息
show_validate_msg("#email_add_input", "error", result.data.errorField.email);
}
if (undefined != result.data.errorField.empName) {
show_validate_msg("#empName_add_input", "error", result.data.errorField.empName);
}
}
}
});这样就完成了员工的新增功能*
九、修改
0.逻辑
1.点击编辑
2.弹出用户修改的模态框(显示用户信息)
3.点击更新,完成用户修改
1.创建员工修改的模态框
把员工新增的模态框复制一份,用于员工修改的模态框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57<!-- 员工修改的模态框 -->
<div class="modal fade" id="empUpdateModal" 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">员工修改</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<p class="form-control-static" id="empName_update_static"></p>
</div>
</div>
<div class="form-group">
<label for="email_update_input" class="col-sm-2 control-label">email</label>
<div class="col-sm-10">
<input type="text" name="email" class="form-control" id="email_update_input"
placeholder="emil@qq.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">gender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_update_input" value="M" checked="checked">
男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_update_input" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptName</label>
<div class="col-sm-3">
<%--部门提交部门id--%>
<select class="form-control" name="dId" id="dept_update_select">
</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" id="emp_update_btn">修改</button>
</div>
</div>
</div>
</div>因为编辑按钮是通过ajax请求查出数据之后创建的,所以对他用加载完页面直接帮事件的方法行不通
为编辑绑事件的方法:1.创建完编辑按钮之后,为按钮绑事件;2.通过on方法(这里使用第二种)
之前查出的部门信息放到了员工添加的列表中,只需要把要添加的元素传进来即可
点击编辑,弹出模态框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/*修改
1.我们是按钮创建之前就绑定了click,所以绑不上
* 解决方法:
* 1.可以在添加按钮的时候绑定 2.绑定live()方法,jequry新版没有live方法,可以用on替换
* */
$(document).on("click", ".edit_btn", function () {
// alert("edit");
//查出员工信息,显示员工信息,
getEmp($(this).attr("edit-id"));
// 查出部门信息,并显示部门列表
getDepts("#dept_update_select");
//把员工的id传递给模态框的跟新按钮
$("#emp_update_btn").attr("edit-id",$(this).attr("edit-id"));
//弹出模态框
$("#empUpdateModal").modal({
backdrop: "static"
});
});2.点击编辑,显示员工员工信息
把员工的姓名改成表单的静态控件(在bootstrap中查看)
index中查询员工信息的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 查出员工信息
* */
function getEmp(id) {
$.ajax({
url: "${APP_PATH}/emp/" + id,
type: "GET",
success: function (result) {
// console.log(result);
var empData = result.data.employee;
$("#empName_update_static").text(empData.empName);//给静态文本框赋值
$("#email_update_input").val(empData.email);//给input框赋值
//性别定位
$("#empUpdateModal input[name=gender]").val([empData.gender]);
//部门定位
$("#empUpdateModal select").val([empData.dId]);
}
});
}在EmployeeController中创建相应的方法
1
2
3
4
5
6
7
8
9
10
11/**根据id查询员工
* @param
* @return Message
* @description:修改前将要修改的employee查询出来表单回显
*/
"/emp/{id}")//等价于@RequestMapping(value = "/emp/{id}",method=RequestMethod.GET) (value =
public Message getEmployee(@PathVariable("id") Integer id) {
Employee employee = employeeService.getEmployee(id);
return Message.success().add("employee", employee);
}在EmployeeService中创建相应的方法
1
2
3
4
5
6
7
8
9/**
* 按照员工id查询员工
* @param id
* @return
*/
public Employee getEmployee(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
}可以在创建编辑按钮的时候,创建一个属性,来表示当前员工的id
3.点击修改,更新员工
为更新按钮绑定单击事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25//点击修改,更新员工信息
$("#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}/emp/"+$(this).attr("edit-id"),
type:"PUT",
data:$("#empUpdateModal form").serialize(),
success:function(result){
//alert(result.msg);
//1.关闭模态框
$("#empUpdateModal").modal("hide");
//2.回到本页面
to_page(currentPage);
}
});
});因为发送的是ajax请求,要写请求的处理事件
在EmployeeController中添加相应的保存方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26/**保存员工
* 遇到的问题:请求体中有数据:
* 但是Employee对象封装不上;
* SQL语句变成了 update tbl_emp where emp_id = 1014;
* 原因:
* Tomcat:
* 1.将请求体中的数据,封装成一个map
* 2.request.getParameter("empName")就会从这个map中取值。
* 3.SpringMvc封装pojo的时候会把pojo中的每个属性值,request.getParamter("email");
* Ajax发送PUT请求引发的血案
* PUT请求:请求体中的数据,request.getParameter("empName")拿不到的根本原因就是
* Tomcat一看是put请求,不会封装请求体中的数据为map,只有post形式的请求才封装请求体为map
* @param
* @return Message
* @description:员工更新 这里ajax请求直接发put请求而不是post请求,那么所有的参数都会获取不到,因为tomcat只会封装post的数据。也就是说request.getParameter("empId")为空,springmvc也无法封装Bean
* 解决方法: 1.发送post方法,通过HiddenHttpMethodFilter
* 2.发送put请求,通过HttpPutFormContentFilter(通过web.xml配置)
*/
"/emp/{empId}") (value =
public Message saveUpdateEmployee(Employee employee) {
System.out.println(employee);
//logger.info(employee.toString());
employeeService.updateEmployee(employee);
return Message.success();
}在EmployeeService中添加相应的保存方法
1
2
3
4
5
6
7/**
* 员工更新
* @param employee
*/
public void updateEmployee(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);//根据主键有选择的更新
}- 打开修改模态框的时候,把员工id的值,传给修改按钮
- 打开修改模态框的时候,把员工id的值,传给修改按钮
因为tomcat不封装put请求发送的数据,spring提供了支持(HttpPutFormContentFilter
)
- 在web.xml中,把这个过滤器配置上
1
2
3
4
5
6
7
8<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>
- 在web.xml中,把这个过滤器配置上
更新成功之后关闭模态框,回到本界面
把员工的当前页码也记录一下
这样就可以发送成功了*
十、删除
1.单个删除
逻辑
URI:/emp/{id} DELETE形式的请求(1)单个删除的步骤
在EmployeeController中拦截发送的请求
1
2
3
4
5
6
7
8
9
10
11/**
* 单个删除的方法
* @param id
* @return
*/
"/emp/{id}",method =RequestMethod.DELETE) (value =
public Message deleteEmpById(@PathVariable("id") Integer id){
employeeService.deleteEmployee(id);
return Message.success();
}在EmployeeService中创建相应的方法
1
2
3
4
5
6
7/**
* 员工删除
* @param empId
*/
public void deleteEmployee(Integer empId) {
employeeMapper.deleteByPrimaryKey(empId);
}用on为删除按钮绑定单击事件
弹出是否确认删除的对话框(拿出员工的员工的名字)
删除成功后,返回当前页码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* 删除
* */
$(document).on("click",".delete_btn",function(){
//弹出是否确认删除对话框(找到当前的祖先节点的tr,tr下的第二个td的文本值)
//alert($(this).parents("tr").find("td:eq(1)").text());
var empName = $(this).parents("tr").find("td:eq(2)").text();
var empId = $(this).attr("del-id");//要删除的员工id
if(confirm("确认删除【"+empName+"】吗?")){
//点击确认发送ajax请求,删除即可
$.ajax({
url:"${APP_PATH}/emp/"+empId,
type:"DELETE",
success:function(result){
alert(result.msg);
//回到本页
to_page(currentPage);
}
});
}
});
构造删除按钮的时候给按钮添加一个自定义的属性来表示员工id
这样就完成了单个删除*
2.批量删除
(1)添加多选框
给第一列第一行添加多选框
解析数据的时候也需要checkbox
把CheckBox添加到遍历的元素中
(2) 完成全选全不选功能
点击最上面的按钮,让下面的按钮与最上面的按钮的checked的值保持同步
1
2
3
4
5
6
7
8//完成全选,全不选功能
$("#check_all").click(function(){
//attr获取checked是undefined;
//我们这些dom原生的属性:attr获取自定义属性的值,用prop修改和读取dom原生属性的值;
$(this).prop("checked");
//让下面的选择框和第一个的值相同
$(".check_item").prop("checked",$(this).prop("checked"));
});当下面的选择框全部点满后,上面的选择框自动的选中
为下面的选择框添加单击事件
用checked选择器,查看被选中的个数
1
2
3
4
5
6//给check_item绑定单击事件
$(document).on("click",".check_item",function(){
//判断当前选中的元素是否全选
var flag = ($(".check_item:checked").length == $(".check_item").length);
$("#check_all").prop("checked",flag);
});(3)批量删除
给全部删除的按钮添加id
给删除按钮绑定单击事件
遍历被选中的员工,并提示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//点击全部删除,就批量删除
$("#emp_del_all_btn").click(function () {
//遍历每一个被选中的,查出名字
var empNames = "";
var del_idstr = "";
$.each($(".check_item:checked"),function(){
empNames += $(this).parents("tr").find("td:eq(2)").text()+",";
//组装员工id字符串
del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-";
});
//去除多余的(最后一个)逗号:从0开始截取字符串,到导数第二个
empNames = empNames.substring(0,empNames.length-1);
//去除多余的(最后一个)-:从0开始截取字符串,到导数第二个
del_idstr = del_idstr.substring(0,del_idstr.length-1);
if(confirm("确认删除【"+empNames+"】员工吗?")){
//发送ajax请求删除
$.ajax({
url:"${APP_PATH}/emp/"+del_idstr,
type:"delete",
success:function(result){
alert(result.msg);
//回到当前页面
to_page(currentPage);
}
});
}
});把原来的方法改成单个批量二合一的方法
1
2
3
4
5
6
7
8
9
10
11
12
13/***
* 删除多个员工(批量)
* @param ids
*/
public void deleteBatch(List<Integer> ids) {
EmployeeExample example = new EmployeeExample();
Criteria criteria = example.createCriteria();
//delete from xxx where emp_id in(1,2,3)
criteria.andEmpIdIn(ids);
employeeMapper.deleteByExample(example);
}在EmployeeService中创建可以同时删除多个的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* @param
* @return Message
* @description:单个批量删除 单个删除:1 批量删除:1-2-3
*/
"/emp/{ids}") (value =
public Message deleteEmployee(@PathVariable("ids") String ids) {
if (ids.contains("-")) {//包含-就是批量删除,调用方法如下
String[] str_ids = ids.split("-");//分割成数组
List<Integer> del_ids = new ArrayList<>();
for (String id : str_ids) {
del_ids.add(Integer.parseInt(id));
}
employeeService.deleteBatch(del_ids);
} else {//不包含-就是单个删除,调用方法如下:
Integer id = Integer.parseInt(ids);
employeeService.deleteEmployee(id);
}
return Message.success();
}
恭喜你:这样ssm框架的crud项目就完成了!!!