三、SSM整合的项目实战
1、需求
1、完成一个任务列表的增删改查功能
2、前端写好了vue的项目,差后端ssm
1、案例功能
1、如图

2、前端提供的接口
1、学习计划分页查询接口
-
/* 需求说明 查询全部数据页数据 请求uri schedule/{pageSize}/{currentPage} 请求方式 get 响应的json { "code":200, "flag":true, "data":{ //本页数据 data: [ {id:1,title:'学习java',completed:true}, {id:2,title:'学习html',completed:true}, {id:3,title:'学习css',completed:true}, {id:4,title:'学习js',completed:true}, {id:5,title:'学习vue',completed:true} ], //分页参数 pageSize:5, // 每页数据条数 页大小 total:0 , // 总记录数 currentPage:1 // 当前页码 } } */
2、学习计划删除接口
-
/* 需求说明 根据id删除日程 请求uri schedule/{id} 请求方式 delete 响应的json { "code":200, "flag":true, "data":null } */
3、学习计划保存接口
-
/* 需求说明 增加日程 请求uri schedule 请求方式 post 请求体中的JSON { title: '', completed: false } 响应的json { "code":200, "flag":true, "data":null } */
4、学习计划修改接口
-
/* 需求说明 根据id修改数据 请求uri schedule 请求方式 put 请求体中的JSON { id: 1, title: '', completed: false } 响应的json { "code":200, "flag":true, "data":null } */
3、前端环境的导入
1、环境
1、使用node:16.16.0版本
2、使用npm:9.x.x版本
- 相当于是后端的maven。包管理工具
3、vscode
4、项目文件
1、C:\study\ssm\代码(1)\前端代码\vue3-demo2这个文件夹下
5、测试命令
1、npm install //安装依赖
2、npm run dev //运行测试
2、后端依赖整合和添加
1、先准备环境
1、数据库
1、数据表
CREATE TABLE schedule (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
completed BOOLEAN NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO schedule (title, completed)
VALUES
('学习java', true),
('学习Python', false),
('学习C++', true),
('学习JavaScript', false),
('学习HTML5', true),
('学习CSS3', false),
('学习Vue.js', true),
('学习React', false),
('学习Angular', true),
('学习Node.js', false),
('学习Express', true),
('学习Koa', false),
('学习MongoDB', true),
('学习MySQL', false),
('学习Redis', true),
('学习Git', false),
('学习Docker', true),
('学习Kubernetes', false),
('学习AWS', true),
('学习Azure', false);
2、实体类
1、Schedule类
package com.atguigu.pojo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* projectName: com.atguigu.pojo
*
* description: 任务实体类
*/
@Data
public class Schedule {
private Integer id;
@NotBlank
private String title;
@NotNull
private Boolean completed;
}
3、工具类
1、PageBean.java
-
1、作用:分页
-
2、代码
-
package com.atguigu.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class PageBean{ private int currentPage; // 当前页码 private int pageSize; // 每页显示的数据量 private long total; // 总数据条数 private List data; // 当前页的数据集合 }
2、R.java
-
1、作用:返回结果集
-
2、代码
-
package com.atguigu.utils; import lombok.Data; /** * projectName: com.atguigu.utils * * description: 返回结果类 */ @Data public class R { private int code = 200; //200成功状态码 private boolean flag = true; //返回状态 private Object data; //返回具体数据 public static R ok(Object data){ R r = new R(); r.data = data; return r; } public static R fail(Object data){ R r = new R(); r.code = 500; //错误码 r.flag = false; //错误状态 r.data = data; return r; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
4、config配置类
1、和ssm整合的配置一样
2、WebMvcJavaConfig.java
package com.atguigu.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.*;
/**
* 控制层的配置类
* 1、controller
* 2、handlerMapping handlerAdapter
* 3、全局异常处理器
* 4、静态资源处理
* 5、拦截器
* 6、jsp视图解析器前后缀
* 7、json转化器
* @author shkstart
*/
@RestController
@EnableWebMvc //这个包括2、7
@Configuration
@ComponentScan("com.atguigu.controller")
public class WebMvcJavaConfig implements WebMvcConfigurer {
// 静态资源处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// jsp视图解析器前后缀
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
// 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor().addPathPatterns().excludePathPatterns()
}
}
3、ServiceJavaConfig.java
package com.atguigu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* service
* 1、开启aop注解的支持:aspect:@Before @After @AfterReturning @AfterThrowing @Around @Aspect @Order
* 2、开启事务的支持:@Transactional ;对应的事务管理器实现
*
*/
@Configuration
@EnableAspectJAutoProxy //开启aop注解的支持
@EnableTransactionManagement
@ComponentScan("com.atguigu.service")
public class ServiceJavaConfig {
@Bean
public TransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
4、MapperJavaConfigNew.java
package com.atguigu.config;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 持久层配置类:连接池、sqlSessionFactory、Mapper代理对象
*
* 方式2:不保留配置文件
* TODO:问题:BUG dataSource和mybatis配置在一起会出现问题
* 原因:mybatis的组件优先加载,@Value没有读取到
* 解决:分开配置,写到不同的类就行。
*/
@Configuration //配置类
public class MapperJavaConfigNew {
// sqlSessionFactory加入ioc容器
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 指定配置文件等信息
// 指定数据库连接池对象
sqlSessionFactoryBean.setDataSource(dataSource);
//// 指定外部的mybatis文件
//// Resource是spring.core的包
// Resource resource = new ClassPathResource("mybatis-config.xml");
// sqlSessionFactoryBean.setConfigLocation(resource);
// 方式2:使用java的代码配置类
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// ①设置驼峰映射
configuration.setMapUnderscoreToCamelCase(true);
// ②设置日志输出
configuration.setLogImpl(Slf4jImpl.class);
// ③开启resultType自动映射
configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);
// 把①、②、③存入到sqlSessionFactoryBean中
sqlSessionFactoryBean.setConfiguration(configuration);
// ④别名设置
sqlSessionFactoryBean.setTypeAliasesPackage("com.atguigu.pojo");
// ⑤加入插件,分页插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
// 设置数据库类型
properties.setProperty("helperDialect","mysql");
// 给pageInterceptor设置数据库类型
pageInterceptor.setProperties(properties);
// 把添加的⑤插件加入到存入到sqlSessionFactoryBean中
sqlSessionFactoryBean.addPlugins(pageInterceptor);
return sqlSessionFactoryBean;
}
// Mapper代理对象加入ioc容器
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
// 指定Mapper接口的包
mapperScannerConfigurer.setBasePackage("com.atguigu.mapper");
return mapperScannerConfigurer;
}
}
5、DataSourceJavaConfig.java
package com.atguigu.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
/**
* 连接池的配置类
*/
@Configuration //配置类
@PropertySource("classpath:jdbc.properties") //引入外部文件
public class DataSourceJavaConfig {
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setPassword(password);
dataSource.setUsername(user);
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
return dataSource;
}
}
6、SpringIoCInit.java
package com.atguigu.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* spring的初始化类
*/
public class SpringIoCInit extends AbstractAnnotationConfigDispatcherServletInitializer {
// root容器配置类
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{DataSourceJavaConfig.class, MapperJavaConfigNew.class, ServiceJavaConfig.class};
}
// webioc容器配置类指定
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebMvcJavaConfig.class};
}
// dispatcherServlet的拦截路径
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2、功能实现
1、总体的步骤
1、先在controller编写路径方法,service中的方法
2、在serviceimpl编写业务方法,然后调用Mapper中的方法
3、在Mapper接口中编写接口方法,然后调用Mapper.xml中的映射
4、最后在Mapper.xml中实现sql语句的编写
5、最后依次将数据传回给前端
2、增删改查代码
1、ScheduleController.java
package com.atguigu.controller;
import com.atguigu.pojo.Schedule;
import com.atguigu.service.ScheduleService;
import com.atguigu.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
//@RestController
@RequestMapping("schedule")
@CrossOrigin //允许其他源访问这个controller中的资源。
@RestController
@Slf4j
public class ScheduleController {
@Autowired
private ScheduleService scheduleService;
// 查询所有数据
@GetMapping("/{pageSize}/{currentPage}")
public R page(@PathVariable(name = "pageSize") int pageSize,
@PathVariable(name = "currentPage") int currentPage){
R r = scheduleService.page(pageSize,currentPage);
// log4j日志输出
log.info("查询所有数据为{}",r);
return r;
}
/**
* 删除日程信息
* @param id
* @return
*/
@DeleteMapping("/{id}")
public R remove(@PathVariable Integer id){
R r = scheduleService.remove(id);
return r;
}
//添加,保存
@PostMapping
public R save(@Validated @RequestBody Schedule schedule, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return R.fail("参数为null,保存失败");
}
R r = scheduleService.save(schedule);
return r;
}
// 修改
@PutMapping
public R update(@Validated @RequestBody Schedule schedule, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return R.fail("参数为null,修改失败");
}
R r = scheduleService.update(schedule);
return r;
}
}
2、ScheduleService.java 接口类
package com.atguigu.service;
import com.atguigu.pojo.Schedule;
import com.atguigu.utils.R;
public interface ScheduleService {
R page(int pageSize, int currentPage);
R remove(Integer id);
R save(Schedule schedule);
R update(Schedule schedule);
}
3、ScheduleServiceImpl.java实现类
package com.atguigu.service.impl;
import com.atguigu.mapper.ScheduleMapper;
import com.atguigu.pojo.Schedule;
import com.atguigu.service.ScheduleService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.atguigu.utils.PageBean;
import com.atguigu.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ScheduleServiceImpl implements ScheduleService {
@Autowired
private ScheduleMapper scheduleMapper;
@Override
public R page(int pageSize, int currentPage) {
// 分页
PageHelper.startPage(currentPage,pageSize);
// 查询
List<Schedule> scheduleList = scheduleMapper.queryList();
// 分页数据装配
PageInfo<Schedule> info = new PageInfo<>(scheduleList);
// 装配PageBean
PageBean<Schedule> pageBean = new PageBean<>(currentPage,pageSize,info.getTotal(),info.getList());
R ok = R.ok(pageBean);
return ok;
}
@Override
public R remove(Integer id) {
int rows = scheduleMapper.deleteById(id);
if (rows>0){
return R.ok(null);
}
return R.fail(null);
}
@Override
public R save(Schedule schedule) {
int rows = scheduleMapper.insert(schedule);
if (rows>0){
return R.ok(null);
}
return R.fail(null);
}
@Override
public R update(Schedule schedule) {
// 判断id为空
if (schedule.getId()==null) {
return R.fail("核心参数为空");
}
int rows = scheduleMapper.update(schedule);
if (rows>0){
return R.ok(null);
}
return R.fail(null);
}
}
4、ScheduleMapper.java接口类
package com.atguigu.mapper;
import com.atguigu.pojo.Schedule;
import java.util.List;
public interface ScheduleMapper {
List<Schedule> queryList();
int deleteById(Integer id);
int insert(Schedule schedule);
int update(Schedule schedule);
}
5、 resources/com/atguigu/mapper/ScheduleMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.atguigu.mapper.ScheduleMapper">
<select id="queryList" resultType="schedule">
select * from schedule
</select>
<!-- 删除-->
<delete id="deleteById">
delete
from schedule
where id = #{id}
</delete>
<!-- 添加-->
<insert id="insert">
insert into schedule(title,completed) value(#{title},#{completed});
</insert>
<!-- 修改-->
<update id="update">
update schedule set title=#{title},completed=#{completed}
where id=#{id};
</update>
</mapper>
3、使用postman进行测试
1、根据接口文档,设计postman的测试
2、使用postman测试
3、如图增删改查接口测试

4、和前端、后端连调用
1、接口对应
1、前端的请求接口要对应后端的借口
2、还有传值方式
2、关键代码
1、查看前端的src下的api.js
import request from '../utils/request'
/*
需求说明
查询全部数据页数据
请求uri
schedule/{pageSize}/{currentPage}
请求方式
get
响应的json
{
"code":200,
"flag":true,
"data":{
//本页数据
data:
[
{id:1,title:'学习java',completed:true},
{id:2,title:'学习html',completed:true},
{id:3,title:'学习css',completed:true},
{id:4,title:'学习js',completed:true},
{id:5,title:'学习vue',completed:true}
],
//分页参数
pageSize:5, // 每页数据条数 页大小
total:0 , // 总记录数
currentPage:1 // 当前页码
}
}
*/
export let loadByPage = (pageSize = 5,currentPage = 1)=>{
return request.get(`schedule/${pageSize}/${currentPage}`)
}
/*
需求说明
根据id修改数据
请求uri
schedule
请求方式
put
请求体中的JSON
{
id: 1,
title: '',
completed: false
}
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
export let updateScheduleById = (schedule)=>{
return request.put(`schedule`,schedule)
}
/*
需求说明
根据id删除日程
请求uri
schedule/{id}
请求方式
delete
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
export let removeScheduleById = (id) =>{
return request.delete(`schedule/${id}`)
}
/*
需求说明
增加日程
请求uri
schedule
请求方式
put
请求体中的JSON
{
title: '',
completed: false
}
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
export let addSchedule = (schedule)=>{
return request.post(`schedule`,schedule)
}
2、查看utils/request.js文件
import axios from 'axios'
import router from '../router/router'
// 创建 Axios 实例 设置一些基础属性
const request = axios.create({
baseURL:'http://localhost:8080/ssm',
timeout: 10000
})
// 添加请求拦截器
request.interceptors.request.use(
config => {
//可以前置设置,例如请求头token等
config.headers.head1 = 'ceshi-head1'
return config
},
error => {
return Promise.reject(error)
}
)
// 添加响应拦截器
request.interceptors.response.use(
response => {
return response
},
error => {
if (error.response.status === 404) {
//到404页面
router.push('/404');
}else{
// 其他情况 返回一个promise
return Promise.reject(error)
}
}
)
//对外暴漏axios实例。
export default request
3、注意!!!!
1、这里前后端是两个项目
2、前端想访问后端项目的内容,浏览器会拦截
3、我们要在后端设置(告诉浏览器,前端可以来访问资源)
4、要在Controller代码中添加一个注解
- @CrossOrigin //允许其他源访问这个controller中的资源。
- 这个很重要!!!
5、画图理解
