SSM整合授课笔记
1 认识SSM整合
是Spring来整合MyBatis和SpringMVC,并提供业务层事务管理。
SpringMVC本身就是Spring的一部分,所以整合相对简单
整合MyBatis需要使用由MyBatis社区来提供的整合插件,而不是由Spring来提供的整合插件mybatis-spring。主要原因是因为MyBatis出现的比Spring出现的晚。
整合插件官网:
http://mybatis.org/spring/zh/index.html
SSM整合的结果是:
- MyBatis的使用更加简单
- MyBatis中的Bean,比如SqlSessionFactory、Mapper接口的动态代理类都交给Spring的IoC容器管理
- 对业务层应用Spring的声明式事务功能。
- 整合SpringMVC(主要是ContextLoaderListener的使用,其他无差别)
2 Spring整合MyBatis
3 创建项目,创建包
4 添加依赖 pom.xml
| <dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies> | | —- |
5 日志文件 logback.xml
| <?xml version=”1.0” encoding=”UTF-8”?><configuration debug=”true”>
<appender name=”STDOUT”
class=”ch.qos.logback.core.ConsoleAppender”>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<root level=”INFO”>
<appender-ref ref=”STDOUT” />
</root>
<!-- 根据特殊需求指定局部日志级别 --><br /> <**logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG" **/><br /> <!--包级别的日志设置 --><br /> <**logger name="com.atguigu.controller" level="DEBUG"**></**logger**><br /> <**logger name="com.atguigu.serivce" level="DEBUG"**></**logger**><br /><**logger name="com.atguigu.mapper" level="DEBUG"**></**logger**><br /></**configuration**> |
| —- |
6 配置数据源
6.1 属性文件 jdbc.properties
jdbc.user=rootjdbc.password=rootjdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis-examplejdbc.driver=com.mysql.jdbc.Driver |
---|
6.2 配置Druid
创建spring配置文件:spring-persist.xml
<context:property-placeholder location=”classpath:jdbc.properties”></context:property-placeholder> <bean id=”dataSource” class=”com.alibaba.druid.pool.DruidDataSource”> <property name=”driverClassName” value=”${jdbc.driver}”></property> <property name=”url” value=”${jdbc.url}”></property> <property name=”username” value=”${jdbc.user}”></property> <property name=”password” value=”${jdbc.password}”></property> </bean> |
---|
6.3 测试Druid
@SpringJUnitConfig(locations = {“classpath:spring-persist.xml”})public class TestSSM { @Autowired private DruidDataSource dataSource; @Test public void testDatasource() throws SQLException { Connection connection = dataSource.getConnection(); System.out.println(connection); } } |
---|
7 整合MyBatis
7.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> <properties resource=”jdbc.properties”></properties> <settings> <setting name=”mapUnderscoreToCamelCase” value=”true”/> <setting name=”lazyLoadingEnabled” value=”true”/> </settings> <typeAliases> <package name=”com.atguigu.entity”/> </typeAliases> <environments default=”development”> <environment id=”development”> <transactionManager type=”JDBC”/> <dataSource type=”POOLED”> <property name=”driver” value=”${wechat.dev.driver}”/> <property name=”url” value=”${wechat.dev.url}”/> <property name=”username” value=”${wechat.dev.username}”/> <property name=”password” value=”${wechat.dev.password}”/> </dataSource> </environment> </environments> <mappers> <mapper resource=”mappers/EmployeeMapper.xml”></mapper> </mappers> </configuration> |
---|
7.2 配置SqlSessionFactoryBean
<bean id=”sqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”> <property name=”dataSource” ref=”dataSource”></property> <property name=”mapperLocations” value=”classpath:com/atguigu/mapper/*Mapper.xml”></property> <property name=”typeAliasesPackage” value=”com.atguigu.entity”></property> <property name=”configuration”> <bean class=”org.apache.ibatis.session.Configuration”> <property name=”mapUnderscoreToCamelCase” value=”true”></property> <property name=”lazyLoadingEnabled” value=”true”></property> </bean> </property> </bean> |
---|
配置SqlSessionFactory,这是一个工厂Bean,返回的不是SqlSessionFactoryBean, 而是该类的getObject()方法的返回值,SqlSessionFactory
7.3 配置Mapper接口扫描器
<bean id=”mapperScannerConfigurer” class=”org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=”basePackage” value=”com.atguigu.mapper”></property> </bean> |
---|
配置MapperScannerConfigurer:其实整合MyBatis还有其他方法,这个算是最给力的。这是一个强大的功能,让它扫描特定的包,自动帮我们成批地创建映射器Mapper,这样一来,就能大大减少配置的工作量。
底层采用了Spring针对自动侦测到的组件的默认命名策略,亦即把类/接口名字的首字母小写,其他不变,作为映射器的名字。例如,映射器接口UserMapper被扫描后创建的映射器bean名为userMapper
7.4 开发MyBatis业务:
7.5 实体类Employee
@Data @AllArgsConstructor @NoArgsConstructorpublic class Employee { private Integer empId; private String empName; private Double empSalary; } |
---|
7.6 Mapper接口EmployeeMapper
public interface EmployeeMapper { public List } |
---|
7.7 映射文件EmployeeMapper.xml
<?xml version=”1.0” encoding=”UTF-8” ?><!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “http://mybatis.org/dtd/mybatis-3-mapper.dtd"_>_<mapper namespace=”com.atguigu.mapper.EmployeeMapper”> <select id=”findAll” resultType=”employee”> select from t_emp </select> <insert id=”saveEmp”> insert into t_emp values (null,#{empName},#{empSalary}) </insert> </*mapper> |
---|
7.8 测试整合MyBatis
@SpringJUnitConfig(locations = {“classpath:spring-persist.xml”})public class TestSSM { @Autowired private EmployeeMapper employeeMapper; @Test public void testFindAll(){ List employeeList.forEach((emp)-> System.out.println(emp)); } @Test public void testSaveEmp(){ Employee emp = new Employee(null,“zhangsan”,678.0); employeeMapper.saveEmp(emp); } @Testpublic void testIoC(){ ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:spring-persist.xml”); String[] arr = context.getBeanDefinitionNames(); for(String elem :arr){ System.out.println(elem); } } } |
---|
8 Spring管理业务层事务
9 增加业务层
9.1 增加业务层接口
public interface EmployeeService { public List public int saveEmp(Employee employee); public int saveEmp2(Employee employee,Employee employee2); } |
---|
9.2 增加业务层实现类
| @Service(value = “employeeService”)public class EmployeeServiceImpl implements EmployeeService {
@Autowired<br /> **private **EmployeeMapper **employeeMapper**;<br /> @Override<br /> **public **List<Employee> findAll() {<br /> **return this**.**employeeMapper**.findAll();<br /> }<br /> @Override<br /> **public int **saveEmp(Employee employee) {<br /> **return this**.**employeeMapper**.saveEmp(employee);<br /> }<br /> @Override<br /> **public int **saveEmp2(Employee employee, Employee employee2) {<br /> **int **n1 = **this**.**employeeMapper**.saveEmp(employee);<br /> **int **n2 = **this**.**employeeMapper**.saveEmp(employee2);<br /> **return **n1+n2;<br /> }<br />} |
| —- |
9.3 扫描业务层注解
<context:component-scan base-package=”com.atguigu.service”></context:component-scan> |
---|
9.4 测试:发现没有事务
@SpringJUnitConfig(locations = {“classpath:spring-persist.xml”})public class TestSSM { @Autowired private EmployeeService employeeService; @Test public void testSaveEmpServcie(){ Employee emp = new Employee(null,“zhangsan”,678.0); employeeService.saveEmp(emp); } @Test public void testSaveEmpServcie2(){ Employee emp = new Employee(null,“lisi”,678.0); Employee emp2 = new Employee(null,“wangwuwangwuwangwuwangwuwangwuwangwuwangwu”,678.0); employeeService.saveEmp2(emp,emp2); } } |
---|
10 添加业务层事务
10.1 配置事务管理器+启动事务注解
<bean id=”transactionManager” class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”> <property name=”dataSource” ref=”dataSource”></property> </bean><tx:annotation-driven></tx:annotation-driven> |
---|
10.2 给业务层方法添加事务功能
@Override @Transactionalpublic int saveEmp2(Employee employee, Employee employee2) { int n1 = this.employeeMapper.saveEmp(employee); int n2 = this.employeeMapper.saveEmp(employee2); return n1+n2; } |
---|
10.3 测试事务功能
<logger name=”org.springframework.jdbc.datasource.DataSourceTransactionManager” level=”DEBUG”></logger> |
---|
11 Spring整合SpringMVC
12 环境准备
12.1 将Java项目变为Web项目
12.2 web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-persist.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <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><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> |
---|
12.3 spring-mvc.xml
<context:component-scan base-package=”com.atguigu.controller”></context:component-scan><mvc:annotation-driven></mvc:annotation-driven><mvc:default-servlet-handler></mvc:default-servlet-handler><mvc:view-controller path=”/“ view-name=”portal.html”></mvc:view-controller><bean class=”org.thymeleaf.spring5.view.ThymeleafViewResolver”> <property name=”order” value=”1”/> <property name=”characterEncoding” value=”UTF-8”/> <property name=”templateEngine”> <bean class=”org.thymeleaf.spring5.SpringTemplateEngine”> <property name=”templateResolver”> <bean class=”org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver”> <property name=”prefix” value=”/WEB-INF/templates/“/> <property name=”suffix” value=”.html”/> <property name=”characterEncoding” value=”UTF-8”/> <property name=”templateMode” value=”HTML5”/> </bean> </property> </bean> </property> </bean> |
---|
12.4 部署项目并运行
可以使用idea绑定的tomcat,也可以使用tomcat7-maven-plugin。
<build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8081</port> <path>/mvnweb</path> </configuration> </plugin> </plugins> </build> |
---|
13 查询所有员工
13.1 开发控制器
| @Controller
@RequestMapping(“/employee”)public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@GetMapping **public **String findAll(Model model){<br /> List<Employee> employeeList = **this**.**employeeService**.findAll();<br /> model.addAttribute(**"empList"**,employeeList);<br /> **return "empList"**;<br /> }<br />} |
| —- |
13.2 开发页面
<tbody th:if=”${#lists.isEmpty(empList)}”> <tr> <td colspan=”5”> 没有员工 </td> </tr> <tr> <td colspan=”5”> <a th:href=”@{/employee/page/add}”>添加页面</a> </td> </tr> </tbody> <tbody th:if=”${!#lists.isEmpty(empList)}” > <tr th:each=”emp,status:${empList}” th:class=”${status.even ? ‘white’:’azure’}”> <td th:text=”${emp.empId}”></td> <td th:text=”${emp.empName}”></td> <td th:text=”${emp.empSalary}”></td> <td th:text=”${status.even}”></td> <td> <a th:hef=”@{ad}”>修改</a> <a th:hef=”@{adf}”>删除</a> </td> </tr> <td colspan=”5”> <a th:href=”@{/employee/page/add}”>添加页面</a> </td> </tr> </tbody> |
---|
13.3 测试
14 添加员工
14.1 配置视图控制器
<mvc:view-controller path=”/employee/page/add” view-name=”empAdd.html”></mvc:view-controller> |
---|
14.2 开发页面
<h3>添加员工</h3> <form th:action=”@{/employee}” method=”post”> 姓名 <input type=”text” name=”empName”><br> 薪资 <input type=”text” name=”empSalary”><br> <input type=”submit” value=”提交”> </form> |
---|
14.3 开发控制器
@Controller @RequestMapping(“/employee”)public class EmployeeController { @Autowired private EmployeeService employeeService; @PostMapping(“”) public String add(Employee emp){ this.employeeService.saveEmp(emp); return “redirect:/employee”; } } |
---|
15 SSM整合下的分页功能
16 理解分页
分页的好处:
- 用户体验较好。
- 服务器端每次只查询一部分数据,内存压力减小。
- 对冷数据减少查询的次数,据此对系统性能进行优化。
分页的SQL语句:
select emp_id,emp_name,emp_salary from t_emp limit 0,5; # 查询第一页数据 select emp_id,emp_name,emp_salary from t_emp limit 5,5; # 查询第二页数据 select emp_id,emp_name,emp_salary from t_emp limit 10,5;# 查询第三页数据 |
---|
limit (pageNum-1)pageSize,pageSize
*分页的三个基本要素:
- 当前页号:pageNum pageIndex
- 每页记录数:pageSize
- 总的记录数:totalCount
分页的其他要素:由其他要素计算而来
- 总页数:pages 100/5=20 101/5=21
- 前一页:prePage 2—-1 1-1
- 后一页:nextPage 2-3 n—n
- 是否为第一页:isFirstPage 1
- 是否为最后一页:isLastPage pages
- 是否有前一页:hasPreviousPage
- 是否有下一页:hasNextPage
- 所有导航页号:navigatepageNums
17 分页插件
PageHelper是MyBatis框架的一个插件,用于支持在MyBatis执行分页操作,使用非常方便。
GitHub
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
码云
https://gitee.com/free/Mybatis_PageHelper
添加依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency> |
---|
给SqlSessionFactoryBean注入插件属性
<bean id=”sqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”> <property name=”plugins”> <array> <bean class=”com.github.pagehelper.PageInterceptor”> <property name=”properties”> <props> <prop key=”reasonable”>true</prop> <prop key=”helperDialect”>mysql</prop> </props> </property> </bean> </array> </property> </bean> |
---|
18 分页操作
18.1 开发控制器
@RequestMapping(“/get/page/{pageNum}”)public String findEmp(@PathVariable(“pageNum”) Integer pageNum ,Model model){ Integer pageSize = 5; PageInfo pageInfo = this.employeeService.findEmp(pageNum,pageSize); model.addAttribute(“pageInfo”,pageInfo); return “empList2”; } |
---|
18.2 开发业务层
@Overridepublic PageInfo findEmp(Integer pageNum, Integer pageSize) { //开启分页功能。开启后,后面执行的 SELECT 语句会自动被附加 LIMIT 子句, //而且会自动查询总记录数 PageHelper.startPage(pageNum,pageSize); //查询数据:查询所有即可 List //返回数据 return new PageInfo(empList); } |
---|
18.3 开发视图层
<tr th:each=”emp,status:${pageInfo.list}” th:class=”${status.even?’white’:’aliceblue’}”> <td th:text=”${emp.empId}”></td> <td th:text=”${emp.empName}”></td> <td th:text=”${emp.empSalary}”></td> <td th:text=”${status.index}”></td> <td th:text=”${status.even}”></td> <td> <a href=”#”>修改</a> <a href=”#”>删除</a> </td> </tr> <tr> <td colspan=”10”> <span th:if=”${pageInfo.hasPreviousPage}”> <a th:href=”@{/employee/get/page/1}”>首页</a> <a th:href=”@{|/employee/get/page/${pageInfo.prePage}|}”>上一页</a> </span> <span th:each=”num:${pageInfo.navigatepageNums}”> <a th:if=”${num != pageInfo.pageNum}” th:href=@{|/employee/get/page/${num}|} th:text=”${num}”></a> <span th:if=”${num == pageInfo.pageNum}”|} th:text=”|[${num}]|”></span> </span> <span th:if=”${pageInfo.hasNextPage}”> <a th:href=”@{|/employee/get/page/${pageInfo.nextPage}|}”>下一页</a> <a th:href=”@{|/employee/get/page/${pageInfo.pages}|}”>末页</a> </span> <span th:text=”|${pageInfo.pageNum}/${pageInfo.pageSize}|”></span> </td> </tr> |
---|
19 PageHelper的更多细节
问题1:调用findAll()方法返回是empList,并作为参数传给PageInfo对象。而PageInfo类的其他分页属性是如何计算出来的。
List PageInfo pageInfo = new PageInfo(empList); |
---|
返回值是其实是Page,是ArrayList的一个子类,不仅包含emp数据,也包括各种分页属性。也就是emp数据以及各种分页属性都是包含在findAll()的返回值中的,进入PageInfo只是进行了结构的转换。
问题2:既然Page中已经有了分页的员工数据,以及分页的各个属性,为什么还要封装到PageInfo中,直接返回Page不可以吗?
可以,但是没有navigatepageNums、navigatePages、prePage、nextPage、startRow、endRow的相关信息,所以功能受限。