SSM整合授课笔记

1 认识SSM整合

SSM整合授课笔记 - 图1
是Spring来整合MyBatis和SpringMVC,并提供业务层事务管理。

SpringMVC本身就是Spring的一部分,所以整合相对简单

整合MyBatis需要使用由MyBatis社区来提供的整合插件,而不是由Spring来提供的整合插件mybatis-spring。主要原因是因为MyBatis出现的比Spring出现的晚。
整合插件官网:
http://mybatis.org/spring/zh/index.html
SSM整合授课笔记 - 图2
SSM整合的结果是:

  • MyBatis的使用更加简单
  • MyBatis中的Bean,比如SqlSessionFactory、Mapper接口的动态代理类都交给Spring的IoC容器管理
  • 对业务层应用Spring的声明式事务功能。
  • 整合SpringMVC(主要是ContextLoaderListener的使用,其他无差别)

    2 Spring整合MyBatis

    3 创建项目,创建包

    SSM整合授课笔记 - 图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
@NoArgsConstructor
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
}

7.6 Mapper接口EmployeeMapper

public interface EmployeeMapper {
public List findAll();
public int saveEmp(Employee employee);
}

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 = employeeMapper.findAll();
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 findAll();
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项目

别忘了:war;可以使用JBLJAVATOWEB插件

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整合下的分页功能

SSM整合授课笔记 - 图4
SSM整合授课笔记 - 图5
SSM整合授课笔记 - 图6

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

分页的其他要素:由其他要素计算而来

<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 empList = this.employeeMapper.findAll();
//返回数据
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 empList = this.employeeMapper.findAll();
PageInfo pageInfo = new PageInfo(empList);

返回值是其实是Page,是ArrayList的一个子类,不仅包含emp数据,也包括各种分页属性。也就是emp数据以及各种分页属性都是包含在findAll()的返回值中的,进入PageInfo只是进行了结构的转换。
SSM整合授课笔记 - 图8

SSM整合授课笔记 - 图9
问题2:既然Page中已经有了分页的员工数据,以及分页的各个属性,为什么还要封装到PageInfo中,直接返回Page不可以吗?

可以,但是没有navigatepageNums、navigatePages、prePage、nextPage、startRow、endRow的相关信息,所以功能受限。