SSM整合教程
一.创建一个web项目
二.项目的各种配置
web.xml
文件配置
- 配置
dispatcherServlet
前端控制器servlet - 配置
characterEncodingFilter
字符集过滤器filter - 配置
HiddenHttpMethodFilter
rest风格请求过滤器,可以处理rest风格的http请求
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--ContextLoaderListener配置监听器,用于加载配置bean的ioc容器
在启动项目的时候主动加载配置bean的ioc容器
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置字符编码过滤器-->
<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>
<init-param>
<!--指定该过滤器一定会请求的编码设置为指定的编码utf8-->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!--指定该过滤器一定会响应的编码设置为指定的编码utf8-->
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置HiddenHttpMethodFilter,可以处理rest风格的http请求-->
<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>
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
SpringMVC.xml
文件配置
- 配置
InternalResourceViewResolver
默认视图解析器 - 配置默认请求处理配置,数据校验配置与国际化配置
- 配置要扫描的包,只扫描Controller
<?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">
<!--配置要扫描的包,只扫描控制器-->
<context:component-scan base-package="com.yangyi.furn" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置默认视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!--配置视图的前缀和后缀-->
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".jsp"></property>
<!--配置优先级-->
<property name="order" value="10"></property>
</bean>
<!--加入两个springmvc的常规配置-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将springmvc不能处理的请求交给tomcat-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
Spring.xml
文件配置
- 配置要扫描的包,不扫描Controller
- 配置数据源
- 指定
mybatis
的核心配置文件 - 配置扫描器,将
mybatis
接口的实现加入到ioc
容器中 - 配置事务管理器
- 开启事务,注解的方式或者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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置要扫描的包,不能扫描Controller-->
<context:component-scan base-package="com.yangyi.furn">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置druid数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!--配置spring与mybatis的整合,引入mybatis到spring的适配包-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定mybatis的核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定数据源-->
<property name="dataSource" ref="dataSource"/>
<!--指定mapper.xml文件位置-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!--配置扫描器,将mybatis的实现接口加入ioc容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer">
<!--指定扫描接口的包-->
<property name="basePackage" value="com.yangyi.furn.dao"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置基于注解的事务管理-->
<!--<tx:annotation-driven transaction-manager="transactionManager"/>-->
<!--配置基于xml的事务管理-->
<aop:config>
<!--配置事务管理的切入表达式-->
<aop:pointcut id="txPointcut" expression="execution(* com.yangyi.furn.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!--配置增强事务,指定事务的切入-->
<tx:advice id="txAdvice">
<tx:attributes>
<!--配置所有的方法都开启事务-->
<tx:method name="*"/>
<!--以get开始的方法,我们认为是只读的,进行优化-->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
配置jdbc
数据源配置文件
Mybatis.xml
文件配置
- 配置环境
- 配置全局设置,日志输出,缓存,懒加载
- 配置数据源
- 配置接口映射文件
Mybatis
逆向工程
- 逆向工程的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="Mybatis3">
<commentGenerator>
<!--指定生产的bean没有注释-->
<property name="suppressDate" value="true" />
<!--<property name="suppressAllComments" value="true" />-->
</commentGenerator>
<!--配置数据库连接信息-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/furn_ssm?useSSL=true&useUnicode=true&characterEncoding=utf-8" userId="root" password="hsp"></jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--指定javabean生成的位置-->
<javaModelGenerator targetPackage="com.yangyi.furn.bean" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--指定sql映射文件生成的位置-->
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resource">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--指定mapper接口生成的位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.yangyi.furn.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!--指定逆向生成的表和生成策略-->
<table tableName="furniture" domainObjectName="Furniture"/>
</context>
</generatorConfiguration>
- 测试
package com.yangyi.furn.test;
import org.junit.Test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @Projectname: furn-ssm
* @Filename: MBGTest
* @Author: 杨逸
* @Data:2023/9/22 15:47
* @Description: 测试mybatis逆向工程
*/
public class MBGTest {
@Test
public void mbgTest() throws XMLParserException, IOException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//这里配置文件修改为自己的
String resource = "mbg.xml";
File configFile = new File(resource);
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
使用VUE-CLI
搭建前端工程,使用vue3
-
因为是前后端分离的项目所有,单独创建前端项目
-
安装
node.js
-
使用
npm
安装vue
的cli脚手架npm install -g @vue/cli
全局安装 -
创建
vue
项目vue create ssm-vue
-
修改
vue
项目的服务端端口,在vue-config.js
中配置
module.exports = {
devServer:{
port:10000 //vue项目的端口
}
}
-
安装
element-puls
插件,npm install element-plus --save
-
去掉默认页面和模板多余的代码,准备写自己需要的代码
-
视图是用于展示的,组件构成视图
-
在前端项目中引入element-plus
//引入element-plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
- 引入中文,支持国际化
//国际化,显示中文
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
- 安装
axiso
,用于发送ajax
请求,npm i axios -S
//引入axios
import axios from 'axios'
//创建axios实例
const request = axios.create({
timeout:5000
})
//请求拦截器
request.interceptors.request.use(config=>{
config.headers["contentType"] = "application/json";
return config;
},error=>{
return Promise.reject(error);
})
//响应拦截器,对返回的数据进行统一处理
request.interceptors.response.use(response=>{
let res = response.data;
//如果是文件就直接返回
if(response.config.responseType === 'blob')return res;
//如果是字符串就转换为json对象
if(typeof res === 'string'){
res = res?JSON.parse(res):res;
}
return res;
})
//导出
export default request;
- 设置代理,解决跨域问题
proxy:{//解决跨域问题,设置代理
'/api':{ //代理的名称
target:'http://127.0.0.1:8080/', //代理的目标地址
changeOrigin:true, //设置是否允许跨域
pathRewrite:{
'/api':'' //选择忽略拦截器里的单词
}
}
}
mybatis
分页插件
- 引入插件
<!--引入mybatis pageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
- 配置分页拦截器
<!--配置分页拦截器-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--启用合理化分页-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
- 进行分页查询
/**
* 分页请求接口,带条件的分页查询
* @param pageNum 分页的号码,默认为1
* @param pageSize 分页的大小,默认为5
* @param search 查询的条件,默认为空
* @return
*/
@GetMapping(value = "/furn/listFurnitureByConditionPage")
@ResponseBody
//通过@RequestParam(defaultValue = "1")设置默认的分页编号
public Msg listFurnitureByConditionPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize,
@RequestParam(defaultValue = "") String search){
//设置分页参数
PageHelper.startPage(pageNum,pageSize);
//调用getAllFurniture(),因为引入了分页插件,底层会进行物理分页,而不是逻辑分页
List<Furniture> furnitureList = furnitureServiceImpl.getAllFurnitureByName(search);
//将查询到的分页信息封装到PageInfo对象中
PageInfo<Furniture> pageInfo = new PageInfo<>(furnitureList, pageSize);
HashMap<String, Object> map = new HashMap<>();
map.put("pageInfo",pageInfo);
Msg msg = Msg.success();
msg.setData(map);
return msg;
}
- 前端分页控件
<el-pagination/>
表示分页控件- 通过
v-model:page-size="pageSize"
:属性设置分页的大小 - 通过
v-model:current-page="currentPage"
:属性设置当前显示分页 - 通过
:total="total
:属性设置总数据量 - 通过
@size-change="handleSizeChange"
:属性设置分页大小改变时发生的事件 - 通过
@current-change="handleCurrentChange""
:属性设置当前页改变时发生的事件
<template>
<div>
//显示表单
<el-table :data="tableData" stripe style="width: 90%;height: 400px" default="scope">
<el-table-column fixed prop="id" sortable label="ID" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="mark" label="品牌" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column prop="operate" label="操作" fixed="right" >
<template #default="scope">
<el-popconfirm title="确定删除这个家具?" @confirm="handleDelete(scope.row.id)" @cancel="this.$message.info('已取消删除')">
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
<el-button link type="primary" @click="edit(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
//分页控件
<div style="height: 50px;width: 40%">
<el-pagination
v-model:page-size="pageSize" //分页的大小
:page-sizes="[5,10]" //分页大小的选择
v-model:current-page="currentPage" //当显示的页数
layout="prev,sizes, pager, next, jumper, ->, total" //控件的显示风格
:total="total" //总共数据条数
@size-change="handleSizeChange" //分页大小改变触发的事件
@current-change="handleCurrentChange"/> //当前显示页改变发生的事件
</div>
</div>
</template>
<script>
import axios from "@/utils/request";
export default {
name: 'HomeView',
components: {
},
data(){
return{
tableData:[],
total:100, // 默认数据量
pageSize:5, //默认页面显示数据大小
currentPage:1, //默认显示的页面
}
},
methods:{
handleSizeChange(){
//发生改变重新请求数据
this.list();
},
handleCurrentChange(){
//发生改变重新请求数据
this.list();
},
list(){
axios.get("/api/furniture/list/page",{
params:{
pageSize:this.pageSize,
pageNum:this.currentPage
}
}).then(res => {
this.tableData = res.data.records;
this.total = res.data.total;
}).catch(error=>{
console.log(error);
console.log('查询家具列表失败')
});
}
},
created() {
this.list();
}
}
</script>
后端数据校验
- 使用
hibernate-validator
- 在属性上使用校验注解标注需要校验的属性,以及校验的类型
package com.yangyi.springboot.bean;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* @Projectname: springboot_furn
* @Filename: Furnitrue
* @Author: 杨逸
* @Data:2023/10/3 10:20
* @Description: 实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Furniture {
/**
* 使用@TableId(type = IdType.AUTO)注解标识一个属性为表的主键,
* 通过type属性设置主键的类型为自动增长类型
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
@NotEmpty(message = "商品名称不能为空")
private String name;
@NotEmpty(message = "商品品牌不能为空")
private String mark;
@NotNull(message = "商品价格不能为空")
@Range(min = 0, message = "商品价格不能小于0")
private BigDecimal price;
@NotNull(message = "商品销量不能为空")
@Range(min = 0, message = "商品销量不能小于0")
private Integer sales;
@NotNull(message = "商品库存不能为空")
@Range(min = 0, message = "商品库存不能小于0")
private Integer stock;
private String imgPath = "./assert/deafult.jpg";
}
- 在需要校验的参数前加上
@Validated
注解进行校验 - 在方法形参上,加上一个
Errors
类型的参数,接受校验失败的信息
/**
*添加家具
* 因为前端是发送Json格式的数据,所有需要使用@RequestBody注解才能将json格式的数据封装为javabean
* 如果是使用表单提交的数据,则不需要使用@RequestBody注解
* @param furniture
* @return
*/
@PostMapping("/furniture/save")
public Result save(@Validated @RequestBody Furniture furniture, Errors errors){
HashMap<String, Object> map = new HashMap<>();
for (FieldError fieldError : errors.getFieldErrors()) {
log.error("字段错误,字段={},错误信息={}",fieldError.getField(),fieldError.getDefaultMessage());
map.put(fieldError.getField(),fieldError.getDefaultMessage());
}
if(map.isEmpty()){
log.info("添加家具={}",furniture);
furnitureService.save(furniture);
log.info("添加家具成功={}",furniture);
return Result.success();
}else{
Result result = Result.error(450, "添加家具失败,数据格式不对");
result.setData(map);
return result;
}