课程目标

  • 理解 MyBatis 逆向工程能解决什么问题,掌握怎么用。

  • 理解为什么要 SSM 集成,SSM 集成本质是什么,利用 Spring 什么功能做什么。

  • 掌握 Spring MVC + Spring + MyBatis 集成。

  • 掌握分页和过滤查询功能实现

一、练习目标(理解)

1、需求

完成部门基本的 CRUD 和分页查询,完成员工基本的 CRUD、分页查询和过滤查询(根据姓名和邮箱模糊查询,根据部门查询)。

2、技术架构

使用 Spring MVC + Spring + MyBatis,数据库选用 MySQL,视图选用 JSP。

 

3、SSM 集成作用及本质

作用:在框架上基础上开发,发挥各个框架在各层的好处,提高开发效率。

本质:

  • Spring 去集成 Spring MVC 和 MyBatis,即控制器对象、业务对象、Mapper 对象等都交由 Spring 容器管理,使用 Spring IoC 和 DI 来完成对象创建及其属性注入;

  • 使用 AOP 来配置事务;

  • 使用 Spring MVC 解决 MVC 的问题,处理请求和响应。

4、集成步骤

  • 先用 Spring 集成 MyBatis

    • 搭建项目,添加依赖配置插件。

    • 把 Spring 和 MyBatis 的等配置文件拷贝进项目 resources 目录下

    • 配置数据库链接池

    • 配置 SqlSessionFactory

    • 配置 Mapper 对象

    • 配置业务对象

    • 配置事务相关

  • 再加入 Spring MVC

    • 在 web.xml 配置端控制器和编码过滤器

    • 在项目 resources 目录下添加 Spring MVC 的配置文件,并配置 MVC 注解解析器,扫描控制器,静态资源处理,视图解析器等等

    • 在 Spring MVC 的配置文件中引入 Spring 配置文件

5、项目准备

5.1 在之前项目上改

这里所谓之前的项目就是 spring和mybatis 集成的项目,或者新建项目拷贝pom.xml 依赖。

5.2、检查依赖和插件

确定依赖和插件都没有配错,或者少配。

 

二、MyBatis 逆向工程(掌握)

1、MyBatis 逆向工程

一个 Maven 插件,根据数据的表生成实体类和 Mapper 接口和 Mapper XML。

2、插件使用

2.1、新建表

参考文档,在 ssm 库新建如下表:

CREATE TABLE `department` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`sn` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2、配置插件

在 pom.xml,配置 MyBatis 逆向工程插件:

<!-- MyBatis 逆向工程插件 -->
<plugin>
   <groupId>org.mybatis.generator</groupId>
   <artifactId>mybatis-generator-maven-plugin</artifactId>
   <version>1.3.2</version>
   <configuration>
       <verbose>true</verbose>
       <overwrite>false</overwrite>
   </configuration>
   <dependencies>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.45</version>
       </dependency>
   </dependencies>
</plugin>

2.3、插件设置

在项目中 resources 目录中新建配置文件 generatorConfig.xml,在 generatorConfig.xml 配置数据库连接、配置表等等。注意: 直接从课件目录下 practise/MyBatis 逆向工程目录中找到此文件拷贝过来修改即可。

2.4、运行插件

在 IDEA 的 Maven 窗口下中双击 mybatis-generator:generate 即可运行,运行注意观察控制台打印。

MyBatis 逆向工程在 IDEA 运行

3、编写部门业务接口及实现

里面提供最基本 CRUD 方法。

package cn.wolfcode.service;

public interface IDepartmentService {
   void delete(Long id);
   void save(Department department);
   Department get(Long id);
   List<Department> listAll();
   void update(Department department);
}
package cn.wolfcode.service.impl;

public class DepartmentServiceImpl implements IDepartmentService {
   private DepartmentMapper departmentMapper;

   @Override
   public void delete(Long id) {
       departmentMapper.deleteByPrimaryKey(id);
  }

   @Override
   public void save(Department department) {
       departmentMapper.insert(department);
  }

   @Override
   public Department get(Long id) {
       return departmentMapper.selectByPrimaryKey(id);
  }

   @Override
   public List<Department> listAll() {
       return departmentMapper.selectAll();
  }

   @Override
   public void update(Department department) {
       departmentMapper.updateByPrimaryKey(department);
  }
}

 

三、Spring 集成 MyBatis(掌握)

1、配置数据库连接池

在 resources 目录下新建 applicationContext.xml,配置如下:

<!-- 关联 db.properties 文件 -->
<context:property-placeholder location="classpath:db.properties"/>

<!-- 配置 DataSource bean -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
   <property name="driverClassName" value="${jdbc.driverClassName}"/>
   <property name="url" value="${jdbc.url}"/>
   <property name="username" value="${jdbc.username}"/>
   <property name="password" value="${jdbc.password}"/>
</bean>

2、配置 SqlSessionFactory bean

在 applicationContext.xml,配置如下:

<!-- 配置 SqlSessionFactory bean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <!-- 关联主配置文件 目前可以不配置-->
   <property name="configLocation" value="classpath:mybatis-config.xml"/>
   <!-- 配置别名 若不用别名,可以不配置 -->
   <property name="typeAliasesPackage" value="cn.wolfcode.domain"/>
   <!-- 配置数据源-->
   <property name="dataSource" ref="dataSource"/>
   <!-- 关联 Mapper XML 可以不配置, 前提编译 Mapper 接口字节码文件与 Mapper XML 文件在一起,文件名也一样 -->
</bean>

3、配置 Mapper bean

在 applicationContext.xml,配置如下:

<!-- 配置 Mapper bean -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
   <!-- 指定 Mapper 接口所在包 -->
   <property name="basePackage" value="cn.wolfcode.mapper"/>
</bean>

4、配置 Service bean

修改 DepartmentServiceImpl,贴 IoC 和 DI 注解。

package cn.wolfcode.service.impl;

@Service
public class DepartmentServiceImpl implements IDepartmentService {

   @Autowired
   private DepartmentMapper departmentMapper;

   @Override
   public void delete(Long id) {
       departmentMapper.deleteByPrimaryKey(id);
  }

   @Override
   public void save(Department department) {
       departmentMapper.insert(department);
  }

   @Override
   public Department get(Long id) {
       return departmentMapper.selectByPrimaryKey(id);
  }

   @Override
   public List<Department> listAll() {
       return departmentMapper.selectAll();
  }

   @Override
   public void update(Department department) {
       departmentMapper.updateByPrimaryKey(department);
  }
}

在 applicationContext.xml,配置如下:

<!-- 配置 IoC DI 注解解析器 , 让 Spring 帮我们创建业务接口的实现类对象, 完成属性或者字段注入 -->
<context:component-scan base-package="cn.wolfcode.service.impl"/>

5、配置事务相关

在 applicationContext.xml,配置如下:

<!-- 配置事务管理器 WHAT-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置增强,包含 WHEN,并关联上面 WHAT-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <tx:method name="get*" read-only="true"/>
       <tx:method name="select*" read-only="true"/>
       <tx:method name="query*" read-only="true"/>
       <tx:method name="count*" read-only="true"/>
       <tx:method name="list*" read-only="true"/>
       <tx:method name="*"/>
   </tx:attributes>
</tx:advice>

<!-- 配置 AOP -->
<aop:config>
   <!-- WHERE -->
   <aop:pointcut id="txPointcut" expression="execution(* cn.wolfcode.service.impl.*ServiceImpl.*(..))"/>
   <!-- 关联 WHERE WHEN-->
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

6、编写单元测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class DepartmentServiceTest {
   @Autowired
   private IDepartmentService departmentService;

   @Test
   public void testSave() {
       Department department = new Department();
       department.setName("公关部");
       department.setSn("DC");
       departmentService.save(department);
  }
}

四、集成 Spring MVC(掌握)

1、在 web.xml 配置端控制器和编码过滤器

<!-- 配置前端控制器 -->
<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:mvc.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>

<!-- 配置编码过滤器, 针对 POST -->
<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>
</filter>
<filter-mapping>
   <filter-name>characterEncodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

2、添加 Spring MVC 的配置文件

在 resources 目录新建 mvc.xml,配置如下:

<!-- IoC DI 注解解析器,配置扫描控制器。说人话:让 Spring 帮我们创建控制器对象,并注入 -->
<context:component-scan base-package="cn.wolfcode.web.controller"/>

<!-- 配置 MVC 注解解析器,时间注解,JSON 注解 -->
<mvc:annotation-driven/>

<!-- 处理静态资源 -->
<mvc:default-servlet-handler/>

<!-- 配置试图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/views/"/>
   <property name="suffix" value=".jsp"/>
</bean>

3、在 Spring MVC 的配置文件中引入 Spring 配置文件

在 mvc.xml,配置如下

<!-- 引入 applicationContext.xml -->
<import resource="classpath:applicationContext.xml"/>

4、编写 DepartmentController

package cn.wolfcode.web.controller;

@Controller
@RequestMapping("/department")
public class DepartmentController {
   @Autowired
   private IDepartmentService departmentService;

   // 处理查询所有部门请求
   @RequestMapping("/list")
   public String list(Model model){
       List<Department> departments = departmentService.listAll();
       model.addAttribute("departments", departments);
       return "department/list"; // 找视图 /WEB-INF/views/department/list.jsp
  }

   // 处理删除请求
   @RequestMapping("/delete")
   public String delete(Long id){
       if (id != null) {
           departmentService.delete(id);
      }
       return "redirect:/department/list"; // 让浏览器重新发一次请求
  }

   // 去新增或去修改页面
   @RequestMapping("/input")
   public String input(Long id, Model model){
       if (id != null) { // 表示去修改
           Department department = departmentService.get(id);
           model.addAttribute("department", department);
      }
       return "department/input"; // 让浏览器重新发一次请求
  }

   // 新增或修改
   @RequestMapping("/saveOrUpdate")
   public String saveOrUpdate(Department department){
       if(department.getId()  == null){ // 新增
           departmentService.save(department);
      }else {
           departmentService.update(department); // 修改
      }
       return "redirect:/department/list"; // 让浏览器重新发一次请求
  }
}

5、拷贝部门 JSP 修改

到课件的 practise/views 目录拷贝 JSP 文件到项目的 webapp/views/department 目录下修改。

 

五、部门分页查询(掌握)

分页功能流程图

5.1_分页插件 PageHelper 基本配置

pom.xml

<dependency>
 <groupId>com.github.pagehelper</groupId>
 <artifactId>pagehelper</artifactId>
 <version>5.1.10</version>
</dependency>

mybatis -config.xml

<plugins>
 <!-- MyBatisHelper 分页插件 -->
 <plugin interceptor="com.github.pagehelper.PageInterceptor">
   <!-- 该参数指明要连接的是哪个数据库 -->
   <property name="helperDialect" value="mysql"/>
   <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
   <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
   <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
   <property name="reasonable" value="true"/>
 </plugin>
</plugins>

applicationContext.xml

<!-- 配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource"/>
 <!-- 引入 mybatis-config.xml-->
 <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

5.2_后台编写

DepartmentServiceImpl.java

业务层加入分页功能,封装分页条件

package cn.wolfcode.service.impl;

@Service
public class DepartmentServiceImpl implements IDepartmentService {

   @Autowired
   private DepartmentMapper departmentMapper;

   public PageInfo<Department> listAll(QueryObject qo) {
    // 使用 PageHelper.startPage 方法进行分页,自动将分页参数加入 SQL 中
       PageHelper.startPage(qo.getPageNum(), qo.getPageSize());
       return new PageInfo<Department>(departmentMapper.selectAll());
  }
}

 

IDepartmentService.java

public interface IDepartmentService {
   PageInfo<Department> listAll(QueryObject qo);
}

 

QueryObject.java

分页条件封装

@Getter
@Setter
public class QueryObject{
 private int pageNum = 1;
 private int pageSize = 3;
}

 

DepartmentServiceImplTest.java

@Test
public void listAll() {
 QueryObject qo = new QueryObject();
 qo.setPageNum(2);
 PageInfo<Department> pageInfo = departmentService.listAll(qo);
 pageInfo.getList().forEach(System.out :: println);
 System.out.println("上一页码:" + pageInfo.getPrePage());
 // 最后一页时获取到的是0,需要注意后期使用前做判断
 System.out.println("下一页码:" + pageInfo.getNextPage());
 // 是否到了最后一页
 System.out.println(pageInfo.isIsLastPage());
 System.out.println("总页码:" + pageInfo.getPages());
 System.out.println("总条数:" + pageInfo.getTotal());
}

5.3_前台编写

DepartmentController.java

接收分页条件,返回封装好的结果对象

// 处理查询所有部门请求
@RequestMapping("/list")
public String list(QueryObject qo,Model model){
 PageInfo<Department> pageInfo = departmentService.listAll(qo);
 model.addAttribute("pageInfo", pageInfo);
 return "department/list"; // 找视图 /WEB-INF/views/department/list.jsp
}

 

list.jsp

回显分页查询的部门数据。

<tr align="center">
 <td colspan="9">
   <a href="/department/list?pageNum=1">首页</a>
   <a href="/department/list?pageNum=${pageInfo.prePage}">上一页</a>
   <a href="/department/list?pageNum=${pageInfo.isLastPage ? pageInfo.pages : pageInfo.nextPage}">下一页</a>
   <a href="/department/list?pageNum=${pageInfo.pages}">尾页</a>
  当前:${pageInfo.pageNum}/${pageInfo.pages}
  总条数:${pageInfo.total}
 </td>
</tr>

 

六、员工过滤查询和分页(掌握)

6.1_准备工作

根据权限系统业务对象属性设计员工表,再使用逆向工程生成 Employee.java 、EmployeeMapper.java 、EmployeeMapper.xml。

6.1.1_员工表创建

CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `admin` bit(1) DEFAULT NULL,
  `dept_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

6.1.2_修改 generatorConfig.xml 文件

<table tableName="employee">
 <property name="useActualColumnNames" value="true"/>
 <property name="constructorBased" value="false" />
 <generatedKey column="id" sqlStatement="JDBC" />
</table>

执行插件生成 Employee.java 、EmployeeMapper.java 、EmployeeMapper.xml

 

6.2_过滤查询实现

需求: 根据部门和关键字(匹配 name 或 email)查询员工信息

过滤查询功能流程图

 

6.2.1_后台过滤查询实现

1、条件封装

根据需求,将多个条件封装成 JavaBean,方便条件数据统一管理。

EmployeeQueryObject.java

@Setter
@Getter
public class EmployeeQueryObject{
   private String  keyword;
   private Long    deptId; // 若这个值为 null 情况,就不根据部门过滤数据
}

 

2、Mapper 组件实现

插件生成的查询列表没有带入查询条件,需要增加查询条件并修改SQL语句。

EmployeeMapper.java

public interface EmployeeMapper {
  List<Employee> selectAll(EmployeeQueryObject qo);
}

EmployeeMapper.xml

<!--
1、模糊查询需要把 % 和 数值合并成一个结果数据,所以需要使用concat 函数做拼接
 2、逻辑运算符 OR 的级别比 AND 低,会先执行 AND,所以需要加() 合并在一起
-->
<select id="selectAll" resultMap="BaseResultMap" >
select id, username, name, password, email, age, admin, dept_id
from employee
 <where>
   <if test="keyword != null and keyword != ''">
    (name LIKE concat('%',#{keyword},'%') OR email LIKE concat('%',#{keyword},'%'))
   </if>
   <if test="deptId != null">
    AND dept_id = #{deptId}
   </if>
 </where>
</select>

 

3、业务层实现

根据需求,创建 IEmployeeService.java,EmployeeServiceImpl.java和 selectList 方法

IEmployeeService.java

public interface IEmployeeService {
  /**
    * 根据前端传入的条件数据进行员工数据查询
    * @param qo 查询员工数据的条件合集
    * @return 查询到的满足条件的员工列表
    */
   List<Employee> listAll(EmployeeQueryObject qo);
}

EmployeeServiceImpl.java

@Service
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
   private EmployeeMapper employeeMapper;
public List<Employee> listAll(EmployeeQueryObject qo) {
       return employeeMapper.selectAll(qo);
  }
}

 

6.2.2_前台过滤查询实现

EmployeeController.java

@Controller
@RequestMapping("/employee")
public class EmployeeController {
  @Autowired
   private IEmployeeService employeeService;
   @Autowired
   private IDepartmentService departmentService;

@RequestMapping("/list")
   public String list(EmployeeQueryObject qo, Model model){
       // 根据页面传递的条件,查询员工列表
       List<Employee> employees = employeeService.listAll(qo);
    // 部门列表查询
       List<Department> departments = departmentService.listAll();
       
    // 添加共享数据
       model.addAttribute("employees",employees);
       model.addAttribute("departments",departments);
// qo SpringMVC 自行添加到 model 中
       return "employee_list";
  }
}

 

employee_list.jsp

<tr>
<td colspan="8">
关键字:<input type="text" name="keyword">
部门:<select name="deptId">
<c:forEach var="dept" items="${departments}">
<option value="${dept.id}"</option>
</c:forEach>
</select>
<input type="submit" value="查询"/>
</td>
</tr>
<tr>
<td>编号</td>
<td>名称</td>
<td>姓名</td>
<td>email</td>
<td>年龄</td>
<td>是否管理员</td>
<td>部门名称</td>
<td>操作</td>
</tr>
<c:forEach var="emp" items="${employees}" varStatus="vs">
<tr>
<td>${vs.count}</td>
<td>${emp.username}</td>
<td>${emp.name}</td>
<td>${emp.email}</td>
<td>${emp.age}</td>
<td>${emp.admin}</td>
<td>${emp.dept_id}</td>
<td>
<a href="#">编辑</a>
<a href="#">删除</a>
</td>
</tr>
</c:forEach>

 

6.2.3_前台条件回显

修改 EmployeeController.java

@RequestMapping("/list")
public String list(@ModelAttribute("qo") EmployeeQueryObject qo, Model model){

}

修改 employee_list.jsp

<tr>
<td colspan="8">
关键字:<input type="text" name="keyword" value="${qo.keyword}">
部门:<select name="deptId">
<c:forEach var="dept" items="${departments}">
<option value="${dept.id}" ${qo.deptId == dept.id ? 'selected' : ''}>${dept.name}</option>
</c:forEach>
</select>
<input type="submit" value="查询"/>
</td>
</tr>

 

6.3_过滤分页合并

由于过滤查询和分页查询的条件都是在页面传递到后台的,所以可以把过滤查询和分页查询的条件合并在一起。

又由于有些查询不需要分页,有些查询不需要过滤,所以过滤条件直接继承分页条件。

6.3.1_后台实现

查询条件修改

EmployeeQueryObject.java

@Setter
@Getter
public class EmployeeQueryObject extends QueryObject {
   private String  keyword;
   private Long    deptId; // 若这个值为 null 情况,就不根据部门过滤数据
}

 

业务层修改

IEmployeeService.java

/**
    * 根据前端传入的条件数据进行员工数据查询
    * @param qo 查询员工数据的条件合集
    * @return 查询到的满足条件的某一页员工列表 + 分页相关数据
    */
PageInfo<Employee> listAll(EmployeeQueryObject qo);

EmployeeServiceImpl.java

public PageInfo<Employee> listAll(EmployeeQueryObject qo) {
 // 启动分页
 PageHelper.startPage(qo.getPageNum(),qo.getPageSize());
 // 封装分页数据
 return new PageInfo<Employee>(employeeMapper.selectAll(qo));
}

 

6.3.1_前台实现

EmployeeController.java

@RequestMapping("/list")
public String list(@ModelAttribute("qo") EmployeeQueryObject qo, Model model){
   // 查询两张表数据
   PageInfo<Employee> pageInfo = employeeService.listAll(qo);
   List<Department> departments = departmentService.listAll();
   // 添加共享数据
   model.addAttribute("pageInfo",pageInfo);
   model.addAttribute("departments",departments);

   return "employee_list";
}

 

employee_list.jsp

<tr align="center">
 <td colspan="9">
   <a href="/employee/list?pageNum=1">首页</a>
   <a href="/employee/list?pageNum=${pageInfo.prePage}">上一页</a>
   <a href="/employee/list?pageNum=${pageInfo.isLastPage ? pageInfo.pages : pageInfo.nextPage}">下一页</a>
   <a href="/employee/list?pageNum=${pageInfo.pages}">尾页</a>
  每页显示:<select name="pageSize">
<option value ="3" ${pageInfo.pageSize == 3 ? 'selected' : ''}>3</option>
<option value ="5" ${pageInfo.pageSize == 5 ? 'selected' : ''}>5</option>
<option value ="10" ${pageInfo.pageSize == 10 ? 'selected' : ''}>10</option>
</select>
${pageInfo.pageNum}/${pageInfo.pages} 页
跳转:<input type="text" name="pageNum" style="width: 30px" value="${pageInfo.pageNum}">
<input type="submit" value="GO"/>
 </td>
</tr>

 

6.4_分页查询条件丢失问题

由于过滤查询条件数据是通过表单提交的,而分页操作的分页数据是通过超链接提交的,超链接中没有表单的过滤查询条件,所有变成无过滤的分页。

 

解决方案

1、在超链接上带过滤查询的条件

2、在表单上带分页需要的条件

 

6.4.1_方案一实现

<td colspan="8" align="center">
 <a href="/employee/list?pageNum=1&keyword=${qo.keyword}&deptId=${qo.deptId}">首页</a>
 
 <a href="/employee/list?pageNum=${pageInfo.prePage}&keyword=${qo.keyword}&deptId=${qo.deptId}">上一页</a>

 <a href="/employee/list?pageNum=${pageInfo.isLastPage?pageInfo.pages : pageInfo.nextPage}&keyword=${qo.keyword}&deptId=${qo.deptId}">下一页</a>

 <a href="/employee/list?pageNum=${pageInfo.pages}&keyword=${qo.keyword}&deptId=${qo.deptId}">尾页</a>
 
每页显示:<select name="pageSize">
 <option value ="3" ${pageInfo.pageSize == 3 ? 'selected' : ''}>3</option>
 <option value ="5" ${pageInfo.pageSize == 5 ? 'selected' : ''}>5</option>
 <option value ="10" ${pageInfo.pageSize == 10 ? 'selected' : ''}>10</option>
 </select>
${pageInfo.pageNum}/${pageInfo.pages} 页
跳转:<input type="text" id="pageNum" name="pageNum" style="width: 30px" value="${pageInfo.pageNum}">
 <input type="submit" value="GO"/>
</td>

6.4.2_方案二实现(推荐)

<script type="text/javascript">
 function goPage(pageNum){
   // 给 id为 pageNum的标签增加value 值
   document.getElementById("pageNum").value = pageNum;
   // 提交页面中第一个表单
   document.forms[0].submit();
}
</script>


<td colspan="8" align="center">
 <a href="javascript:;" onclick="goPage(1)">首页</a>
 <a href="javascript:;" onclick="goPage(${pageInfo.prePage})">上一页</a>
 <a href="javascript:;" onclick="goPage(${pageInfo.isLastPage ? pageInfo.pages : pageInfo.nextPage})">下一页</a>
 <a href="javascript:;" onclick="goPage(${pageInfo.pages})">末页</a>
每页显示:<select name="pageSize">
 <option value ="3" ${pageInfo.pageSize == 3 ? 'selected' : ''}>3</option>
 <option value ="5" ${pageInfo.pageSize == 5 ? 'selected' : ''}>5</option>
 <option value ="10" ${pageInfo.pageSize == 10 ? 'selected' : ''}>10</option>
 </select>
${pageInfo.pageNum}/${pageInfo.pages} 页
跳转:<input type="text" id="pageNum" name="pageNum" style="width: 30px" value="${pageInfo.pageNum}">
 <input type="submit" value="GO"/>
</td>

 

七、员工查询完善(掌握)

7.1_完善超管显示

超管显示为是或否,而不显示 true 或 false。修改员工 list.jsp,如下:

<td>${employee.admin ? '是' : '否'}</td>

7.2_完事部门显示

部门显示部门名称,而不是显示部门 id 值。

7.2.1、修改员工实体类

package cn.wolfcode.domain;

@Setter
@Getter
public class Employee {
   private Long id;
   private String username; // 登录用户名
   private String name; // 姓名
   private String password;
   private Integer age;
   private String email;
   private boolean admin;
   // 关联属性, 不仅可以封装部门 id 可以封装部门名称
   private Department dept;
}

7.2.2、修改员工 Mapper XML

修改查询员工 SQL 及结果集处理的 resultMap。

 <resultMap id="BaseResultMap" type="cn.wolfcode.domain.Employee">
   <id column="id" property="id"/>
   <result column="username" property="username"/>
   <result column="name" property="name"/>
   <result column="password" property="password"/>
   <result column="age" property="age"/>
   <result column="email" property="email"/>
   <result column="admin" property="admin"/>
   <result column="d_id" property="dept.id"/>
   <result column="d_name" property="dept.name"/>
   <result column="d_sn" property="dept.sn"/>
</resultMap>

<sql id="where_sql">
   <where>
     <if test="keyword != null and keyword != ''">
      (emp.name LIKE concat('%',#{keyword},'%') OR emp.email LIKE concat('%',#{keyword},'%'))
     </if>
     <if test="deptId != null">
      AND emp.dept_id = #{deptId}
     </if>
   </where>
</sql>

<select id="selectForList" resultMap="BaseResultMap">
  SELECT e.id, e.name, e.password, e.age, e.email, e.admin, e.dept_id, d.id AS d_id, d.name AS d_name, d.sn AS d_sn
  FROM employee e
  LEFT JOIN department d ON e.dept_id = d.id
   <include refid="where_sql"/>
</select>

7.2.3、修改员工 list.jsp

<td>${employee.dept.name}</td>

 

小结

  1. 掌握 MyBatis 逆向工程

  2. 掌握 Spring 集成 MyBatis

  3. 掌握 Sping 集成 Spring MVC

  4. 掌握部门的分页查询和CRUD

  5. 掌握员工的过滤查询和分页实现

  6. 掌握员工查询完善

 

练习

CREATE TABLE `department` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`sn` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `admin` bit(1) DEFAULT NULL,
  `dept_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 练习一,根据以上表结构和数据,基于 Spring MVC + Spring + MyBatis,完成部门基本的 CRUD 和分页查询,完成员工基本的 CRUD、分页查询和过滤查询(根据名字和邮箱模糊查询,根据部门查询)。

CREATE TABLE `brand` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
`brand_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 练习二,根据以上表结构和数据,基于 Spring MVC + Spring + MyBatis,完成品牌基本的 CRUD 和分页查询,完成产品基本的 CRUD、分页查询和过滤查询(根据名称模糊查询,根据价格范围查询,根据品牌查询)。