SSM框架整合
1.1 简述
SSM框架,即是将SpringMVC框架、Spring框架、MyBatis框架三个框架,整合到一起使用,用于构建企业级的Web应用程序。这样可以大大的简化在web开发中繁琐、重复的操作,让开发人员的精力专注于业务处理的开发上。
-
spring框架和mybatis框架整合到一起
-
spring容器来管理dataSource,SqlSessionFactory等
-
-
spring框架与springmvc框架整合到一起。
-
SpringMVC容器:主要对Controller控制器对象,视图等用户请求和请求结果进行管理。
-
Spring容器:主要对Service、Dao、工具类等对象进行管理。
-
两个容器的关系:SpringMVC容器为Spring容器的子容器,进而两容器中的对象进行间接管理。
-
那么如何整合到一起呢,需要做哪些工作?其实只需要整合以下四个配置文件即可。
-
mybatis的主配置文件
-
spring的配置文件
-
springMVC的配置文件
-
web.xml
1.2 SSM整合项目演示
1.2.1 创建项目/模块,引入坐标
1)创建模块,命名spring-ssm01。
2)添加web应用支持
3)引入坐标依赖
<!--junit的依赖jar包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency><!-- springmvc依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis的依赖jar包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--mysql的依赖jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- druid连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- spring事务依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency><!-- 参考版本对应 http://www.mybatis.org/spring/ -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency><!-- commons 文件上传jar -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
1.2.2 编写配置文件
1)SSM的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"><!--解决SpringMVC的中文乱码问题的过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping><!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化SSM的主配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</init-param>
<!--容器启动就加载-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!--斜杠表示所有的请求都拦截-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2)mybatis的核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>
* 注:因为在 applicationContext.xml中已经配置好 数据库连接池、配置加载各个pojo对应的XXXXMapper.xml、配置druid的连接池: 使用了spring容器来代替了mybatis的核心配置文件的数据源等,所以在mybatis-config.xml中不需要再次配置。否则会严重报错
3) 数据源的配置文件jdbc.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/ssm_db?serverTimezone=Asia/Shanghai&useTimezone=true
user=root
password=123456
4) spring的核心配置文件applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解扫描,比如扫描Dao,Service-->
<context:component-scan base-package="com.sldl"/><!--配置加载jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/><!--配置druid的连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean><!--配置SqlSessionFactory的bean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--绑定数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--加载mybatis的核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--配置加载各个pojo对应的XXXXMapper.xml-->
<property name="mapperLocations" value="classpath:com/sldl/mapper/*.xml"/>
</bean><!--配置可以扫描mapper/dao接口的类型-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<property name="basePackage" value="com.sldl.dao"></property>
</bean>
</beans>
5) SpringMVC的核心配置文件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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd"><!--开启SpringMVC的注解,主要是Controller注解-->
<mvc:annotation-driven/><!--静态资源的访问通过配置-->
<mvc:default-servlet-handler/><!--视图解析器的配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages"/>
<property name="suffix" value=".jsp" />
</bean>
</beans>
6) ssm的主配置文件beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"><!--整合两个配置文件-->
<import resource="classpath:applicationContext.xml"/>
<import resource="classpath:SpringMVC.xml"/>
</beans>
1.2.3 构建一下包结构
在蓝色的java里构建package
com.sldl.mapper com.sldl.service com.sldl.service.impl com.sldl.controller com.sldl.pojo
在resources里构建Directory
com/sldl/mapper
1.2.4 准备数据源(资费表)
-- 创建库
create database if not exists ssm_db;
-- 切换库
use ssm_db;
drop table cost;
create table cost(
cost_id int(4) primary key auto_increment comment "资费ID,唯一标识符",
name varchar(50) not null comment "资费名称",
base_duration int(11) comment "资费基本时长",
base_cost double(7,2) comment "资费花销",
unit_cost double(7,4) comment "资费的单位花销",
status char(1) comment "资费状态 0表示关闭,1表示启用",
descr varchar(100) comment "资费的具体信息",
creatime datetime comment "资费的创建时间",
startime datetime comment "资费的启用时间",
cost_type char(1) not null comment "资费类型"
);
delete from cost;
insert into cost values (null,'5.9元套餐',20,5.9,0.4,1,'5.9元20小时/月,超出部分0.4元/时',now(),null,'1');
insert into cost values (null,'6.9元套餐',40,6.9,0.3,1,'6.9元40小时/月,超出部分0.3元/时',now(),null,'2');
insert into cost values (null,'8.5元套餐',100,8.5,0.2,1,'8.5元100小时/月,超出部分0.2元/时',now(),null,'3');
insert into cost values (null,'10.5元套餐',200,10.5,0.1,1,'10.5元200小时/月,超出部分0.1元/时',now(),null,'3');
insert into cost values (null,'计时收费',null,null,0.5,1,'0.5元/时,不使用不收费',now(),null,'1');
insert into cost values (null,'包月',null,20,null,1,'每月20元,不限制使用时间',now(),null,'2');INSERT INTO cost VALUES (null,'18元套餐',20,5.9,0.4,0,'18元40小时/月,超出部分0.4元/时',now(),now(),'1');
INSERT INTO cost VALUES (null,'28元套餐',40,6.9,0.3,0,'28元60小时/月,超出部分0.3元/时',now(),now(),'1');
INSERT INTO cost VALUES (null,'38元套餐',100,8.5,0.2,0,'38元100小时/月,超出部分0.2元/时',now(),now(),'1');
INSERT INTO cost VALUES (null,'58元套餐',200,10.5,0.1,0,'58元200小时/月,超出部分0.1元/时',now(),now(),'1');
INSERT INTO cost VALUES (null,'88元套餐',400,10.5,0.1,0,'88元400小时/月,超出部分0.1元/时',now(),now(),'1');
INSERT INTO cost VALUES (null,'包月2',null,200,null,0,'每月200元,不限制使用时间',now(),now(),'2');
commit;select * from cost;
1.2.5 创建资费的pojo类型
package com.ssm.netctoss.pojo; import java.sql.Timestamp; import java.util.Objects; public class Cost { private Integer costId; private String name; private Integer baseDuration; //资费基本时长 private Double baseCost; //基本费用 private Double unitCost; //资费的单位花销 private String status; //资费状态 0表示关闭,1表示启用,2表示删除 private String descr; //资费的具体信息 private Timestamp creatime; //资费的创建时间 private Timestamp startime; //资费的启用时间 private String costType; //资费类型 public Cost(){} public Cost(Integer costId, String name, Integer baseDuration, Double baseCost, Double unitCost, String status, String descr, Timestamp creatime, Timestamp startime, String costType) { this.costId = costId; this.name = name; this.baseDuration = baseDuration; this.baseCost = baseCost; this.unitCost = unitCost; this.status = status; this.descr = descr; this.creatime = creatime; this.startime = startime; this.costType = costType; } public Integer getCostId() { return costId; } public void setCostId(Integer costId) { this.costId = costId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getBaseDuration() { return baseDuration; } public void setBaseDuration(Integer baseDuration) { this.baseDuration = baseDuration; } public Double getBaseCost() { return baseCost; } public void setBaseCost(Double baseCost) { this.baseCost = baseCost; } public Double getUnitCost() { return unitCost; } public void setUnitCost(Double unitCost) { this.unitCost = unitCost; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getDescr() { return descr; } public void setDescr(String descr) { this.descr = descr; } public Timestamp getCreatime() { return creatime; } public void setCreatime(Timestamp creatime) { this.creatime = creatime; } public Timestamp getStartime() { return startime; } public void setStartime(Timestamp startime) { this.startime = startime; } public String getCostType() { return costType; } public void setCostType(String costType) { this.costType = costType; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Cost)) return false; Cost cost = (Cost) o; return costId.equals(cost.costId) && name.equals(cost.name) && baseDuration.equals(cost.baseDuration) && baseCost.equals(cost.baseCost) && unitCost.equals(cost.unitCost) && status.equals(cost.status) && descr.equals(cost.descr) && creatime.equals(cost.creatime) && startime.equals(cost.startime) && costType.equals(cost.costType); } @Override public int hashCode() { return Objects.hash(costId, name, baseDuration, baseCost, unitCost, status, descr, creatime, startime, costType); } @Override public String toString() { return "Cost{" + "costId=" + costId + ", name='" + name + '\'' + ", baseDuration=" + baseDuration + ", baseCost=" + baseCost + ", unitCost=" + unitCost + ", status='" + status + '\'' + ", descr='" + descr + '\'' + ", creatime=" + creatime + ", startime=" + startime + ", costType='" + costType + '\'' + '}'; } }
1.2.6 创建Cost的Mapper接口
package com.ssm.netctoss.mapper; import com.ssm.netctoss.pojo.Cost; import java.util.List; import java.util.Map; public interface CostMapper { Cost findByCostId(Integer costId); // 使用mybatis的分页插件,实现分页。就不用再写查询主页数方法 List<Cost> findByPage(Map<String, Object> params); // 修改资费到数据库 boolean updateCost(Cost cost); // 删除资费,实际上修改资费的状态。改为2 boolean deleteCost(Integer costId); // 修改资费的状态,暂停-->启用。启用-->暂停。在mybatis的动态sql语句写 void changeStatus(Integer costId, String status); // 添加一个新的资费套餐 void addCost(Cost cost); // 获取资费总数 int getTotalCount(); }
1.2.7 配置Cost的Mapper.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.ssm.netctoss.mapper.CostMapper"> <resultMap id="costMap" type="com.ssm.netctoss.pojo.Cost"> <id column="cost_id" property="costId"/> <result column="name" property="name"/> <result column="base_duration" property="baseDuration"/> <result column="base_cost" property="baseCost"/> <result column="unit_cost" property="unitCost"/> <result column="status" property="status"/> <result column="descr" property="descr"/> <result column="creatime" property="creatime"/> <result column="startime" property="startime"/> <result column="cost_type" property="costType"/> </resultMap> <select id="findByCostId" resultMap="costMap"> SELECT cost_id, name, base_duration, base_cost, unit_cost, status, descr, creatime, startime, cost_type FROM cost WHERE cost_id = #{costId} </select> <select id="findByPage" parameterType="map" resultMap="costMap"> <bind name="offset" value="(pageNum - 1) * pageSize" /> SELECT cost_id, name, base_duration, base_cost, unit_cost, status, descr, creatime, startime, cost_type FROM cost LIMIT #{pageSize} OFFSET #{offset} </select> <update id="updateCost"> UPDATE cost SET name = #{name}, base_duration = #{baseDuration}, base_cost = #{baseCost}, unit_cost = #{unitCost}, status = #{status}, descr = #{descr}, creatime = #{creatime}, startime = #{startime}, cost_type = #{costType} WHERE cost_id = #{costId} </update> <update id="deleteCost"> UPDATE cost SET status = '2' <!-- 假设2表示删除状态 --> WHERE cost_id = #{costId} </update> <update id="changeStatus"> UPDATE cost SET status = #{status} WHERE cost_id = #{costId} </update> <insert id="addCost" parameterType="com.ssm.netctoss.pojo.Cost"> INSERT INTO cost (name, base_duration, base_cost, unit_cost, status, descr, creatime, startime, cost_type) VALUES (#{name}, #{baseDuration}, #{baseCost}, #{unitCost}, #{status}, #{descr}, #{creatime}, #{startime}, #{costType}) </insert> <select id="getTotalCount" resultType="int"> SELECT COUNT(*) FROM cost </select> </mapper>
1.2.8 编写Cost的Service接口
package com.ssm.netctoss.service; import com.ssm.netctoss.pojo.Cost; import java.util.List; /** * 针对于Cost资费模块,编写业务层的代码,设计成接口,扩展性强 */ public interface CostService { /** * 查询单个资费信息 * @param costId 资费ID * @return Cost */ Cost findByCostId(Integer costId); /** * 查询资费信息,支持分页 * @param pageNum 当前页码 * @param pageSize 每页显示的记录数 * @return List<Cost> */ List<Cost> findByPage(int pageNum, int pageSize); /** * 获取资费总数 * @return int 总记录数 */ int getTotalCount(); /** * 更新资费信息 * @param cost 需要更新的资费对象 * @return boolean 是否更新成功 */ boolean updateCost(Cost cost); /** * 删除资费(逻辑删除,修改状态) * @param costId 资费ID * @return boolean 是否删除成功 */ boolean deleteCost(Integer costId); /** * 修改资费状态 * @param costId 资费ID * @param status 新的状态 */ void changeStatus(Integer costId, String status); /** * 添加新的资费套餐 * @param cost 需要添加的资费对象 */ void addCost(Cost cost); }
1.2.9 编写Cost的Service实现类型
package com.ssm.netctoss.service.impl; import com.ssm.netctoss.mapper.CostMapper; import com.ssm.netctoss.pojo.Cost; import com.ssm.netctoss.service.CostService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 业务层的实现类,应该调用DAO层的代码 */ @Service public class CostServiceImpl implements CostService { @Resource private CostMapper costMapper; @Override public Cost findByCostId(Integer costId) { return costMapper.findByCostId(costId); } @Override public List<Cost> findByPage(int pageNum, int pageSize) { Map<String, Object> params = new HashMap<>(); params.put("pageNum", pageNum); params.put("pageSize", pageSize); return costMapper.findByPage(params); } @Override public int getTotalCount() { return costMapper.getTotalCount(); } @Override public boolean updateCost(Cost cost) { return costMapper.updateCost(cost); } @Override public boolean deleteCost(Integer costId) { return costMapper.deleteCost(costId); } @Override public void changeStatus(Integer costId, String status) { costMapper.changeStatus(costId, status); } @Override public void addCost(Cost cost) { costMapper.addCost(cost); } }
1.2.10 编写Cost的Controller
package com.ssm.netctoss.controller; import com.ssm.netctoss.pojo.Cost; import com.ssm.netctoss.service.CostService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import javax.annotation.Resource; import java.sql.Timestamp; import java.util.List; /** * 集成的Controller,用于管理Cost(资费)模块的增删改查操作。 */ @Controller @RequestMapping("/fee") public class CostController { @Resource private CostService costService; /*因为业务层的实现类,已经被扫描成bean,因此此处可以自动装配 */ /** * 显示资费详情页面 * URL示例: /fee/findById?costId=1 */ @RequestMapping("findById") public ModelAndView findByCostId(Integer costId, ModelAndView mav){ Cost cost = costService.findByCostId(costId); mav.setViewName("fee/fee_detail"); mav.addObject("cost",cost); return mav; } /** * 显示资费列表,支持分页 * URL示例: /fee/list?pageNum=1&pageSize=5 */ @RequestMapping("list") public ModelAndView listCosts( @RequestParam(value = "pageNum", defaultValue = "1") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize, ModelAndView mav) { List<Cost> costs = costService.findByPage(pageNum, pageSize); int totalCount = costService.getTotalCount(); // 确保CostService中实现了getTotalCount方法 int totalPage = (int) Math.ceil((double) totalCount / pageSize); mav.setViewName("fee/fee_list"); mav.addObject("costs", costs); mav.addObject("currentPage", pageNum); mav.addObject("pageSize", pageSize); mav.addObject("totalPage", totalPage); return mav; } /** * 显示添加资费页面 * URL示例: /fee/toAdd */ @RequestMapping("toAdd") public String toAddPage() { return "fee/fee_add"; // 返回添加页面视图 } /** * 处理添加资费的请求 * URL示例: /fee/add */ @RequestMapping(value = "add", method = RequestMethod.POST) public ModelAndView addCost(Cost cost, ModelAndView mav) { // 设置默认状态和创建时间等信息 cost.setStatus("1"); // 假设1表示启用 cost.setCreatime(new Timestamp(System.currentTimeMillis())); cost.setStartime(new Timestamp(System.currentTimeMillis())); // 调用服务层添加资费 costService.addCost(cost); // 设置返回视图,重定向到列表页面 mav.setViewName("redirect:/fee/list"); return mav; } /** * 显示修改资费页面 * URL示例: /fee/modify?costId=1 */ @RequestMapping("modify") public ModelAndView modifyCost(Integer costId, ModelAndView mav) { Cost cost = costService.findByCostId(costId); mav.setViewName("fee/fee_modi"); mav.addObject("cost", cost); return mav; } /** * 处理修改资费的请求 * URL示例: /fee/saveModify */ @RequestMapping(value = "saveModify", method = RequestMethod.POST) public ModelAndView saveModifiedCost(Cost cost, ModelAndView mav) { // 调用服务层更新资费 costService.updateCost(cost); // 设置返回视图,重定向到列表页面 mav.setViewName("redirect:/fee/list"); return mav; } /** * 删除资费(逻辑删除) * URL示例: /fee/delete?costId=1 */ @RequestMapping("delete") public String deleteCost(Integer costId) { costService.deleteCost(costId); return "redirect:list"; // 正确的重定向路径 } }