课程目标
-
理解 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 即可运行,运行注意观察控制台打印。
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;
三、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;
在 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、编写单元测试类
四、集成 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;
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;
IDepartmentService.java
public interface IDepartmentService {
PageInfo<Department> listAll(QueryObject qo);
}
QueryObject.java
分页条件封装
DepartmentServiceImplTest.java
5.3_前台编写
DepartmentController.java
接收分页条件,返回封装好的结果对象
// 处理查询所有部门请求
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
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
6.2.2_前台过滤查询实现
EmployeeController.java
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>