SSM框架CRUD实战第一天

该文档是:ssm整合教程

博客连接:https://www.loveuluo.cn

日期:2021-01-11

1. 功能点

image-20210111140254346

2. 技术点

image-20210111140304620

3. 基础环境搭建

3.1 创建一个maven工程

image-20210111142809084

3.2 引入项目依赖的jar包

<!--引入项目依赖的jar包 -->
<!-- SpringMVC、Spring -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependencies>
  <!--引入pageHelper分页插件 -->
  <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.0.0</version>
  </dependency>

  <!-- MBG -->
  <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
  <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.5</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.7.RELEASE</version>
  </dependency>

  <!-- 返回json字符串的支持 -->
  <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.8</version>
  </dependency>

  <!--JSR303数据校验支持;tomcat7及以上的服务器,
  tomcat7以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el
  -->
  <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
  </dependency>

  <!-- Spring-Jdbc -->
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.7.RELEASE</version>
  </dependency>

  <!--Spring-test -->
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.7.RELEASE</version>
  </dependency>

  <!-- Spring面向切面编程 -->
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.7.RELEASE</version>
  </dependency>

  <!--MyBatis -->
  <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.2</version>
  </dependency>
  <!-- MyBatis整合Spring的适配包 -->
  <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
  </dependency>

  <!-- 数据库连接池、驱动 -->
  <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
  <dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.41</version>
  </dependency>
  <!-- (jstl,servlet-api,junit) -->
  <!-- https://mvnrepository.com/artifact/jstl/jstl -->
  <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>

  <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
  </dependency>

  <!-- junit -->
  <!-- https://mvnrepository.com/artifact/junit/junit -->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
</dependencies>

3.3 引入bootstrap前端框架和JQuery

去官网下载bootstrap,创建文件夹放进去:

image-20210111144629850

用index.jsp页面测试一下是否可用:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <%--引入jQuery--%>
    <script type="text/javascript" src="static/js/jquery-1.12.4.min.js"></script>
    <%--引入bootstrap--%>
    <link href="static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
    <%--测试一下bootstrap是否可用--%>
    <button class="btn btn-primary">按钮</button>
</body>
</html>

image-20210111145602884

3.4 编写SSM整合的关键配置文件

(1)配置web.xml:

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--1、自动Spring的容器-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--2、SpringMVC前端控制器,拦截所有请求-->
  <servlet>
    <servlet-name>dispatcherServlet</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>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!--3、字符编码过滤器-->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <!--响应和请求也设置为utf-8,一定要放在所有过滤之前-->
    <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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!--4、使用Rest风格的URI,将页面普通的post请求转为指定的delect或者put请求-->
  <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>

</web-app>

(2)配置springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--SpringMVC的配置文件,包含网站跳转逻辑的控制,配置-->
    <context:component-scan base-package="com.Luo">
        <!--只扫描控制器-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>

    <!--配置视图解析器,方便页面返回-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--两个标准配置-->
    <!--将springmvc不能处理的请求叫个tomcat-->
    <mvc:default-servlet-handler/>
    <!--能支持springmvc更高级的一些功能,例如JSR303校验,快捷的ajax,映射动态请求....-->
    <mvc:annotation-driven />

</beans>

(3)spring配置:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.Luo">
        <!--不扫描控制器-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
    </context:component-scan>

    <!--Spring配置文件,这里主要配置和业务逻辑有关的-->
    <!--=================================数据源,事务控制,xxx=================================-->
    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>

    <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--======================================================================================-->

    <!--=================================配置和Mybatis的整合=================================-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定mybatis全局配置文件的路径-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!--指定数据源-->
        <property name="dataSource" ref="pooledDataSource"></property>
        <!--指定mybatis,mapper文件的位置-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>

    <!--配置扫描器,将mybatis接口的实现加入到ioc容器中-->
    <bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--扫描所有的dao接口,加入到ioc容器-->
        <property name="basePackage" value="com.Luo.dao"></property>
    </bean>
    
    <!--配置一个可以批量查询的SqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--constructor-arg代表用有参数构造创建对象-->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
        <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    </bean>
    <!--======================================================================================-->

    <!--=================================事务控制的配置=================================-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--控制住数据源-->
        <property name="dataSource" ref="pooledDataSource"></property>
    </bean>

    <!--开启基于注解的事务,使用xml配置形式的事务(必要的主要的都是使用配置的方式)-->
    <aop:config>
        <!--切入点表达式-->
        <aop:pointcut id="txPoint" expression="execution(* com.Luo.service..*(..))"></aop:pointcut>
        <!--配置事务增强-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"></aop:advisor>
    </aop:config>

    <!--配置事务增强,事务如何切入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--所有方法都是事务方法-->
            <tx:method name="*"></tx:method>
            <!--以get方法开始的所有方法-->
            <tx:method name="get*" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--======================================================================================-->

    <!--Spring配置文件核心点(数据源、与mybatis的整合、事务控制)-->
</beans>

(4)dbconfig.properties数据源配置:

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=123456

(5)最终视图:

image-20210111171147389

3.5 使用mybatis的逆向工程生成对应的bean以及mapper

(1)Mybatis主配置文件:

<?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.Luo.bean"/>
    </typeAliases>

</configuration>

(2)创建数据库表:

emp表,d_id和dept_id外键关联:

image-20210111170632856

dept表:

image-20210111170647966

(3)引入mybatis逆向工程所需要的jar包:

<!-- MBG -->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
  <groupId>org.mybatis.generator</groupId>
  <artifactId>mybatis-generator-core</artifactId>
  <version>1.3.5</version>
</dependency>

(4)在当前工程目录下创建逆向工程所需配置文件mbg.xml:

image-20210111184058745

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--设置生成的代码没有注释-->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- 配置数据库连接 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm_crud" userId="root"
                        password="123456">
        </jdbcConnection>

        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- 指定javaBean生成的位置 -->
        <!--这里的.\代表当前工程下的xxx-->
        <javaModelGenerator targetPackage="com.Luo.bean"
                            targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--指定sql映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 指定dao接口生成的位置,mapper接口 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.Luo.dao" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!-- table指定每个表的生成策略 -->
        <!--domainObjectName=代表逆向生成的javabean的名字-->
        <table tableName="tbl_emp" domainObjectName="Employee"></table>
        <table tableName="tbl_dept" domainObjectName="Department"></table>
    </context>
</generatorConfiguration>

(5)创建MBGTest类用于生成逆向工程:

public class MBGTest {
    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);
    }
}

执行结果:

image-20210111184922271

3.6 修改自动生成的内容

自动生成的这些可能有部分不合适或者缺少,进行修改。自动生成的没有连表查询,增加查询员工时带出部门的方法:

(1)Employee emp表的实体类:

public class Employee {
    private Integer empId;
    private String empName;
    private String gender;
    private String email;
    private Integer dId;
    //希望查询员工的同时部门信息也是查询好的
    private Department department;

(2)EmployeeMapper接口:

//查询所有员工并且带上员工的部门
List<Employee> selectByExampleWithDept(EmployeeExample example);
//根据id查到员工并带上部门
Employee selectByPrimaryKeyWithDept(Integer empId);

(3)需要的sql语句:

SELECT e.emp_id, e.emp_name, e.gender, e.email, e.d_id,d.dept_id,d.dept_name 
FROM tbl_emp e LEFT JOIN tbl_dept d ON e.d_id=d.dept_id;

(4)EmployeeMapper.xml增加连表查询的语句:

<!--==============================自己写的==============================-->
<resultMap id="WithDept" type="com.Luo.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 property="department" javaType="com.Luo.bean.Department">
    <id column="dept_id" property="deptId"></id>
    <result column="dept_name" property="deptName"></result>
  </association>
</resultMap>

<sql id="Dept_Column_List">
  e.emp_id, e.emp_name, e.gender, e.email, e.d_id,d.dept_id,d.dept_name
</sql>

<!--List<Employee> selectByExampleWithDept(EmployeeExample example);-->
<select id="selectByExampleWithDept" parameterType="com.Luo.bean.EmployeeExample" resultMap="WithDept">
  select
  <if test="distinct">
    distinct
  </if>
  <!--include标签 相当于用了上边的<sql></sql>中的内容-->
  <include refid="Dept_Column_List" />
  FROM tbl_emp e LEFT JOIN tbl_dept d ON e.d_id=d.dept_id
  <if test="_parameter != null">
    <include refid="Example_Where_Clause" />
  </if>
  <if test="orderByClause != null">
    order by ${orderByClause}
  </if>
</select>

<!--Employee selectByPrimaryKeyWithDept(Integer empId);-->
<select id="selectByPrimaryKeyWithDept" parameterType="java.lang.Integer" resultMap="WithDept">
  select
  <include refid="Dept_Column_List" />
  FROM tbl_emp e LEFT JOIN tbl_dept d ON e.d_id=d.dept_id
  where emp_id = #{empId,jdbcType=INTEGER}
</select>
<!--=================================================================-->

3.7 测试mapper

/**
 * 测试dao层的工作
 * @author lfy
 *推荐Spring的项目就可以使用Spring的单元测试,可以自动注入我们需要的组件
 *1、导入SpringTest模块
 *2、@ContextConfiguration指定Spring配置文件的位置
 *3、直接autowired要使用的组件即可
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class MapperTest {

    @Autowired
    DepartmentMapper departmentMapper;

    @Autowired
    EmployeeMapper employeeMapper;

    //在spring配置文件里配置了可以进行批量操作的SqlSession
    @Autowired
    SqlSession sqlSession;

    /**
     * 测试DepartmentMapper
     */
    @Test
    public void testCRUD(){
   /* 传统写法:
       1、创建SpringIOC容器
      ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      2、从容器中获取mapper
      DepartmentMapper bean = ioc.getBean(DepartmentMapper.class);                       */
        System.out.println(departmentMapper);

        //1、插入几个部门
//    departmentMapper.insertSelective(new Department(null, "开发部"));
//    departmentMapper.insertSelective(new Department(null, "测试部"));

        //2、生成员工数据,测试员工插入
//      employeeMapper.insertSelective(new Employee(null, "Jerry", "M", "Jerry@atguigu.com", 1,null));

        //3、批量插入多个员工;批量,使用可以执行批量操作的sqlSession。
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        for(int i = 0;i<1000;i++){
            String uid = UUID.randomUUID().toString().substring(0,5)+i;
            mapper.insertSelective(new Employee(null,uid, "M", uid+"@atguigu.com", 1,null));
        }
        System.out.println("批量完成");

    }
}

4. 查询功能

4.1 查询分析

image-20210112110100768

4.2 分页后台代码完成

(1)让index.jsp页面直接跳转/emps请求:

<%--直接转发到当前项目的emps请求--%>
<jsp:forward page="/emps"></jsp:forward>

(2)引入PageHelper分页插件:

<!--引入pageHelper分页插件 -->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.0.0</version>
</dependency>

(3)在mybatis核心配置文件mybatis-cofing中配置插件:

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

(4)Controller控制器类:

/**
 * 处理员工CRUD请求
 */
@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    /**
     * 查询员工数据(分页查询)
     * @return
     */
    @RequestMapping("/emps")
    //需要传入要去第几页,如果没传的话默认值就是第一页defaultValue = "1"
    public String getEmps(@RequestParam(value = "pageNo",defaultValue = "1")Integer pn, Model model){
        //引入PageHelper分页插件
        //在查询之前需要调用分页插件,传入当前页码和每页要显示几条数据
        PageHelper.startPage(pn,5);
        //startPage后面紧跟的这个查询就是一个分页查询
        List<Employee> emps=employeeService.getAll();
        //用PageInfo对结果进行包装,只需要将pageInfo交给页面就行了
        //封装了详细的分页信息,包括有我们查询出来的数据,6代表要连续显示的页数
        PageInfo page = new PageInfo(emps, 5);
        //放到requset域中
        model.addAttribute("pageInfo",page);
        return "list";
    }

}

(5)控制器中所需要的Service编写:

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 查询所有员工
     * @return
     */
    public List<Employee> getAll() {
        //可以传入查询条件进行查询,这里是查询所有,直接传入null
        return employeeMapper.selectByExampleWithDept(null);
    }
}

(6)单元测试(也可以使用Postman测试):

/**
 * 使用Spring测试模块提供的测试请求功能,测试curd请求的正确性
 * Spring4测试的时候,需要servlet3.0的支持
 * @author lfy
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath:applicationContext.xml","classpath:springmvc.xml" })
public class MvcTest {
    // 传入Springmvc的ioc
    @Autowired
    WebApplicationContext context;
    // 虚拟mvc请求,获取到处理结果。
    MockMvc mockMvc;

    @Before
    public void initMokcMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testPage() throws Exception {
        //模拟请求拿到返回值
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pageNo", "5")).andReturn();

        //请求成功以后,请求域中会有pageInfo;我们可以取出pageInfo进行验证
        MockHttpServletRequest request = result.getRequest();
        PageInfo pi = (PageInfo) request.getAttribute("pageInfo");
        System.out.println("当前页码:"+pi.getPageNum());
        System.out.println("总页码:"+pi.getPages());
        System.out.println("总记录数:"+pi.getTotal());
        System.out.println("在页面需要连续显示的页码");
        int[] nums = pi.getNavigatepageNums();
        for (int i : nums) {
            System.out.print(" "+i);
        }
        System.out.println();
        //获取员工数据
        List<Employee> list = pi.getList();
        for (Employee employee : list) {
            System.out.println("ID:"+employee.getEmpId()+"==>Name:"+employee.getEmpName()+"==>部门:"+employee.getDepartment());
        }

    }

}

测试结果:

image-20210112134306440

4.3 搭建BootStrap分页页面

(1)引入jsp依赖:

<!--引入jsp依赖-->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.0</version>
</dependency>

(2)编写list.jsp显示数据的页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>员工列表</title>
    <!-- web路径:
    不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
    以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名才能正确的找到:
      http://localhost:3306/SSM1_CRUD_2021_01_11

      例如 http://localhost:3306/SSM1_CRUD_2021_01_11/static 代表的就是访问webapp下的staitc,
      也就是说项目名后边的/对应到了webapp目录。
    -->
    <%
        /*
        pageContext:只在当前jsp页面有用,是最小的域对象
        getContextPath:可以拿到当前项目路径,以/为开头 但 没有以/为结尾,等于说是/SSM1_CRUD_2021_01_11,而前边的/会被解析为服务器的路径
        也就是最终是这样: http://localhost:3306/SSM1_CRUD_2021_01_11
        */
        pageContext.setAttribute("APP_PATH",request.getContextPath());
    %>
    <%--引入jQuery--%>
    <%--这里路径等于说是http://localhost:3306/SSM1_CRUD_2021_01_11/static/js/jquery-1.12.4.min.js--%>
    <script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
    <%--引入bootstrap--%>
    <link href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
    <!-- 搭建显示页面 -->

    <%--container代表栅格系统,把整个页面分成了一行一行,每一行总共又能分成12列,
    根据屏幕的大小,这些列可以显示在一行也可以显示在两行,可以动态分配--%>
    <div class="container">

        <!-- 标题 -->
        <%--row代表每一行--%>
        <div class="row">
            <%--col(代表这是列)-md(大小-中等屏幕)-12(这一列占了整个12列也就是一整行)--%>
            <div class="col-md-12">
                <h1>SSM-CRUD</h1>
            </div>
        </div>

        <!-- 按钮 -->
        <%--row代表每一行--%>
        <div class="row">
            <%--col(代表这是列)-md(大小-中等屏幕)-4(12列被划分为了3列,每列的大小就是原先的4列合并)
             col-md-offset-8(往右边偏移8列)--%>
            <div class="col-md-4 col-md-offset-8">
                <button class="btn btn-primary">新增</button>
                <button class="btn btn-danger">删除</button>
            </div>
        </div>

        <!-- 显示表格数据 -->
        <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>
                        <tr>
                            <th>1</th>
                            <th>曹新千</th>
                            <th>男</th>
                            <th>CAO@QQ.com</th>
                            <th>部门</th>
                            <th>
                                <button class="btn btn-primary btn-sm">
                                    <%--span是小图标--%>
                                    <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                    编辑
                                </button>
                                <button class="btn btn-danger btn-sm">
                                    <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                    删除
                                </button>
                            </th>
                        </tr>
                </table>
            </div>
        </div>

        <!-- 显示分页信息 -->
        <div class="row">
            <%--分页文字信息--%>
            <div class="col-md-6">
                当前记录数:XXX
            </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">&laquo;</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">&raquo;</span>
                            </a>
                        </li>
                        <li><a href="#">末页</a></li>
                    </ul>
                </nav>
            </div>
        </div>


    </div>
</body>
</html>

页面结果:

image-20210112150134373

4.4 显示分页数据

list.jsp:

<!-- 显示表格数据 -->
<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>
            <%--pageInfo.list代表我们查询到的放到PageInfo对象里的数据--%>
            <c:forEach items="${pageInfo.list}" var="emp">
                <tr>
                    <th>${emp.empId}</th>
                    <th>${emp.empName}</th>
                    <th>${emp.gender}</th>
                    <th>${emp.email}</th>
                    <th>${emp.department.deptName}</th>
                    <th>
                        <button class="btn btn-primary btn-sm">
                            <%--span是小图标--%>
                            <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                            编辑
                        </button>
                        <button class="btn btn-danger btn-sm">
                            <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                            删除
                        </button>
                    </th>
                </tr>
            </c:forEach>
        </table>
    </div>
</div>

<!-- 显示分页信息 -->
<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">
                <li><a href="${APP_PATH }/emps?pageNo=1">首页</a></li>
                <%--如果有上一页再显示上一页的按钮--%>
                <c:if test="${pageInfo.hasPreviousPage }">
                    <li>
                        <a href="${APP_PATH }/emps?pageNo=${pageInfo.pageNum-1}"
                           aria-label="Previous"> <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                </c:if>
                <%--遍历分页条--%>
                <c:forEach items="${pageInfo.navigatepageNums }" var="page_Num">
                    <%--如果是当前页码,高亮显示--%>
                    <c:if test="${page_Num == pageInfo.pageNum }">
                        <li class="active"><a href="#">${page_Num }</a></li>
                    </c:if>
                    <%--如果不是当前页码就不高亮显示--%>
                    <c:if test="${page_Num != pageInfo.pageNum }">
                        <li><a href="${APP_PATH}/emps?pageNo=${page_Num}">${page_Num}</a></li>
                    </c:if>
                </c:forEach>
                <%--如果有下一页就显示下一页的按钮--%>
                <c:if test="${pageInfo.hasNextPage }">
                    <li>
                        <a href="${APP_PATH }/emps?pageNo=${pageInfo.pageNum+1 }"
                           aria-label="Next"> <span aria-hidden="true">&raquo;</span>
                        </a>
                    </li>
                </c:if>
                <li><a href="${APP_PATH }/emps?pageNo=${pageInfo.pages}">末页</a></li>
            </ul>
        </nav>
    </div>
</div>

5. 查询功能改造-ajax

5.1 查询ajax分析

image-20210112161051505

5.2 返回分页的json数据

(1)导入jackson依赖:

<!-- 返回json字符串的支持 -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.8.8</version>
</dependency>

(2)设计一个通用的返回类,可以带上返回的信息:

/**
 * 通用的返回类
 */
public class Msg {
    //状态码 100-成功 200-失败
    private int code;
    //提示信息
    private String msg;
    //用户要返回给浏览器的数据
    private Map<String,Object> extend=new HashMap<>();

//    成功的话调用的方法 返回一个带有成功状态码和信息的Msg对象
    public static Msg success(){
        Msg result=new Msg();
        result.setCode(100);
        result.setMsg("处理成功!");
        return result;
    }
//    失败的话调用的方法 返回一个带有失败状态码和信息的Msg对象
    public static Msg fail(){
        Msg result=new Msg();
        result.setCode(200);
        result.setMsg("处理失败!");
        return result;
    }
//    链式增加用户的数据的方法
    public Msg add(String key,Object value){
        this.getExtend().put(key,value);
        return this;
    }
    //get set方法
}

(3)Controller控制器类:

/**
 * 查询员工数据(分页查询) - AJAX异步
 * @return
 */
@RequestMapping("/emps")
@ResponseBody//返回的数据自动变成json格式
public Msg getEmpsWithJson(@RequestParam(value = "pageNo",defaultValue = "1")Integer pn, Model model){
    //引入PageHelper分页插件
    //在查询之前需要调用分页插件,传入当前页码和每页要显示几条数据
    PageHelper.startPage(pn,5);
    //startPage后面紧跟的这个查询就是一个分页查询
    List<Employee> emps=employeeService.getAll();
    //用PageInfo对结果进行包装,只需要将pageInfo交给页面就行了
    //封装了详细的分页信息,包括有我们查询出来的数据,6代表要连续显示的页数
    PageInfo page = new PageInfo(emps, 5);
    return Msg.success().add("pageInfo",page);
}

网页结果:

image-20210112163237980

image-20210112163220008

5.3 构建员工列表

不再index.jsp使用转发访问/emps跳转回list.jsp,直接修改index.jsp发送ajax请求显示数据:

<body>
<script type="text/javascript">
    //1、页面加载完成之后,直接去发送一个ajax请求,要到分页数据
    $(function () {
        $.ajax({
            url:"${APP_PATH}/emps",
            data:"pageNo=1",
            type:"GET",
            success:function (result) {
                //console.log(result);
                //1、解析并显示员工数据
                build_emps_table(result)
                //2、解析并显示分页数据
            }
        })
    });
    // 构建表格的方法
    function build_emps_table(result) {
        // 这就是我们所有的员工数据
        var emps=result.extend.pageInfo.list;
        // jQuery提供的遍历方法,emps=要遍历的元素,function=每次遍历的回调函数
        // index=索引,item=当前遍历的对象
        $.each(emps,function (index,item) {
            //添加一个td标签 并往里边添加empId的值
            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);
            //删除和编辑按钮
            var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
                .append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
            var delBtn =  $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
                .append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
            //放到一个单元格中
            var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
        //添加tr标签 并把td标签和内容都放进去
        $("<tr></tr>").append(empIdTd)
            .append(empNameTd)
            .append(genderTd)
            .append(emailTd)
            .append(deptNameTd)
            .append(btnTd)
            //添加到表格体中
            .appendTo("#emps_table tbody");
    });
    }
    // 构建分页栏的方法
    function build_page_nav(result) {

    }
</script>

<!-- 搭建显示页面 -->
<%--container代表栅格系统,把整个页面分成了一行一行,每一行总共又能分成12列,
根据屏幕的大小,这些列可以显示在一行也可以显示在两行,可以动态分配--%>
<div class="container">

    <!-- 标题 -->
    <%--row代表每一行--%>
    <div class="row">
        <%--col(代表这是列)-md(大小-中等屏幕)-12(这一列占了整个12列也就是一整行)--%>
        <div class="col-md-12">
            <h1>SSM-CRUD</h1>
        </div>
    </div>

    <!-- 按钮 -->
    <%--row代表每一行--%>
    <div class="row">
        <%--col(代表这是列)-md(大小-中等屏幕)-4(12列被划分为了3列,每列的大小就是原先的4列合并)
         col-md-offset-8(往右边偏移8列)--%>
        <div class="col-md-4 col-md-offset-8">
            <button class="btn btn-primary">新增</button>
            <button class="btn btn-danger">删除</button>
        </div>
    </div>

    <!-- 显示表格数据 -->
    <div class="row">
        <%--这一列占一整行,全部用来显示表格数据--%>
        <div class="col-md-12">
            <table class="table table-hover" id="emps_table">
                <%--thead用于区分,代表表头--%>
                <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>

    <!-- 显示分页信息 -->
    <div class="row">
        <%--分页文字信息--%>
        <div class="col-md-6">当前 页,总
            页,总  条记录
        </div>
        <%--分页条信息--%>
        <div class="col-md-6">

        </div>
    </div>

</div>
</body>
</html>

页面结果:

image-20210112185147325

5.4 构建分页条

(1)在mybatis核心配置文件中配置PageHelper插件参数:

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--调整分页合理化,例如点上一页页码小于0会自动变成1,大于最大页码会变成最大页码-->
        <property name="reasonable" value="true"></property>
    </plugin>
</plugins>

(2)给两个div分别加上id:

image-20210112190236313

(3)index.jsp修改,加上发送ajax请求要去第几页的方法,构建分页信息的方法,构建分页条的方法:

<script type="text/javascript">
    //1、页面加载完成之后,直接去发送一个ajax请求,要到分页数据
    $(function () {
        //去首页
        to_page(1)
    });

    // 把发送ajax请求要去第几页抽成一个方法
    function to_page(pageNo){
        $.ajax({
            url:"${APP_PATH}/emps",
            data:"pageNo="+pageNo,
            type:"GET",
            success:function(result){
                //console.log(result);
                //1、解析并显示员工数据
                build_emps_table(result);
                //2、解析并显示分页信息
                build_page_info(result);
                //3、解析显示分页条数据
                build_page_nav(result);
            }
        });
    }

    // 构建表格的方法
    function build_emps_table(result) {
        //清空table表格(防止点下一页把上页的内容也带过来)
        $("#emps_table tbody").empty();
        // 这就是我们所有的员工数据
        var emps=result.extend.pageInfo.list;
        // jQuery提供的遍历方法,emps=要遍历的元素,function=每次遍历的回调函数
        // index=索引,item=当前遍历的对象
        $.each(emps,function (index,item) {
            //添加一个td标签 并往里边添加empId的值
            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);
            //删除和编辑按钮
            var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
                .append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
            var delBtn =  $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
                .append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
            //放到一个单元格中
            var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
        //添加tr标签 并把td标签和内容都放进去
        $("<tr></tr>").append(empIdTd)
            .append(empNameTd)
            .append(genderTd)
            .append(emailTd)
            .append(deptNameTd)
            .append(btnTd)
            //添加到表格体中
            .appendTo("#emps_table tbody");
    });
    }

    // 构建分页信息的方法
    function build_page_info(result) {
        $("#page_info_area").empty();
        $("#page_info_area").append("当前 "+result.extend.pageInfo.pageNum+"页,总 "+
            result.extend.pageInfo.pages+"页,总  "+result.extend.pageInfo.total+"条记录");
    }

    // 构建分页条的方法
    //解析显示分页条,点击分页要能去下一页....
    function build_page_nav(result){
        //page_nav_area
        $("#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("&laquo;"));
        if(result.extend.pageInfo.hasPreviousPage == false){
            firstPageLi.addClass("disabled");
            prePageLi.addClass("disabled");
        }else{
            //为元素添加点击翻页的事件
            firstPageLi.click(function(){
                to_page(1);
            });
            prePageLi.click(function(){
                to_page(result.extend.pageInfo.pageNum -1);
            });
        }
        var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
        var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
        if(result.extend.pageInfo.hasNextPage == false){
            nextPageLi.addClass("disabled");
            lastPageLi.addClass("disabled");
        }else{
            nextPageLi.click(function(){
                to_page(result.extend.pageInfo.pageNum +1);
            });
            lastPageLi.click(function(){
                to_page(result.extend.pageInfo.pages);
            });
        }
        //添加首页和前一页 的提示
        ul.append(firstPageLi).append(prePageLi);
        //1,2,3遍历给ul中添加页码提示
        $.each(result.extend.pageInfo.navigatepageNums,function(index,item){

            var numLi = $("<li></li>").append($("<a></a>").append(item));
            if(result.extend.pageInfo.pageNum == item){
                numLi.addClass("active");
            }
            numLi.click(function(){
                to_page(item);
            });
            ul.append(numLi);
        });
        //添加下一页和末页 的提示
        ul.append(nextPageLi).append(lastPageLi);

        //把ul加入到nav
        var navEle = $("<nav></nav>").append(ul);
        navEle.appendTo("#page_nav_area");
    }

</script>

页面效果:

image-20210112193300518

最后修改:2021 年 01 月 21 日 04 : 37 PM
如果觉得我的文章对你有用,请随意赞赏