SSM标准整合案例

SSM整合(后端层)

整合配置

整合之前学的Mybatis。spring springmvc框架共同完成后面的项目

1.以web模板创建个maven工程

image-20220502190243191

2.配置相关依赖

image-20220502190159980

3.创建包的层次结构

image-20220502193552129

4.完成基础配置文件配置

image-20220502193657331

spring

@Configuration
@ComponentScan({"com.ember.service"})
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
public class SpringConfig {
}

jdbc

public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.user}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

mybatis

public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
//传入参数datasources(参数的datasources是spring容器根据类型封装好的)
factoryBean.setDataSource(dataSource);
//扫描要封装的实体类所在文件夹
factoryBean.setTypeAliasesPackage("com.ember.domain");
return factoryBean;
}
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc=new MapperScannerConfigurer();
//扫描映射所在文件夹
msc.setBasePackage("com.ember.dao");
return msc;
}
}

web

如果业务有form表单提交的功能可以直接加上post乱码过滤器

//post乱码处理
@Override
protected Filter[] getServletFilters() {
//spring-mvc提供的过滤器
CharacterEncodingFilter filter=new CharacterEncodingFilter();
//设置解码
filter.setEncoding("UTF-8");
//传入返回的过滤器,多个过滤器{filter,filter1,.....}
return new Filter[]{filter};
}
//替代web.xml的配置类
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}

mvc

@Configuration
@ComponentScan("com.ember.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

功能模块

造表

DROP TABLE IF EXISTS tbl_book;
CREATE TABLE tbl_book (
id integer NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');
INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');
INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');
INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');
INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');
INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');

实体类

image-20220502232805784

dao(接口+自动代理)

public interface BookDao {
@Insert("INSERT INTO tbl_book VALUES (null,#{type},#{name},#{description})")
public void save(Book book);
@Update("UPDATE tbl_book SET type=#{type},name=#{name},description=#{description}")
public void update(Book book);
@Delete("DELETE FROM tbl_book WHERE id=#{id}")
public void delete(Integer id);
@Select("SELECT * FROM tbl_book WHERE id=#{id}")
public Book getById(Integer id);
@Select("SELECT * FROM tbl_book")
public List<Book> getAll();
}

service(接口+实现类)

image-20220502233018658

没写完这里

@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
bookDao.save(book);
return true;
}
@Override
public boolean update(Book book) {
bookDao.update(book);
return true;
}
@Override
public boolean delete(Integer id) {
bookDao.delete(id);
return true;
}
@Override
public Book getById(Integer id) {
return bookDao.getById(id);
}
@Override
public List<Book> getAll() {
return bookDao.getAll();
}
}

controller

@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public boolean save(@RequestBody Book book) {
return bookService.save(book);
}
@PutMapping
public boolean update(@RequestBody Book book) {
return bookService.update(book);
}
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) {
return bookService.delete(id);
}
@GetMapping("/{id}")
public Book getById(@PathVariable Integer id) {
return bookService.getById(id);
}
@GetMapping
public List<Book> getAll() {
return bookService.getAll();
}
}

接口测试

业务层接口测试

我这里两次因为mybatis的配置类写错了导致junit无法运行,写测试类之前检查这两个地方

image-20220503165219441

随后完成测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void testGetById(){
Book book= bookService.getById(1);
System.out.println(book);
}
@Test
public void GetAll(){
List<Book> bookList= bookService.getAll();
System.out.println(bookList);
}
}

image-20220503165438190

这里也可以测一下controller层是否能用

要导入tomcat插件这里使用pom文件中导入

<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>

这里出现了一个bug tomcat无法正常启动后来发现

image-20220503182146592

这个地方一定要写classpath或者加一个/

依次写好测试image-20220503201208051

都能返回对象或者true就成功了

开启注解式事务驱动

springconfig上加@EnableTransactionManagement

jdbcconfig中加上方法

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds=new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}

在service接口上挂上事务标志

@Transactional
public interface BookService {

表现层数据封装

image-20220503223916852

我们在controller下创建个结果类

以下只是属性和需要的构造方法,别忘了set get tostring

public class Result {
private Object data;
private Integer code;
private String msg;
public Result( Integer code,Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
public Result(Integer code,Object data) {
this.data = data;
this.code = code;
}
public Result() {
}

code类用于存放状态编码

public class Code {
public static final Integer SAVE_OK=20011;
public static final Integer DELETE_OK=20021;
public static final Integer UPDATE_OK=20031;
public static final Integer GET_OK=20041;
public static final Integer SAVE_ERR=20010;
public static final Integer DELETE_ERR=20020;
public static final Integer UPDATE_ERR=20030;
public static final Integer GET_ERR=20040;
}

这两个类都是需要根据业务进行更改的

更改web层

随后将我们的web层代码全部改成返回值为结果类类型

@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag= bookService.save(book);
return new Result(flag?Code.SAVE_OK:Code.SAVE_ERR,flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag=bookService.update(book);
return new Result(flag?Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag= bookService.delete(id);
return new Result(flag?Code.DELETE_OK:Code.DELETE_ERR,flag);
}

测试接口是否正常

异常处理

业务中有多种异常,一般将其抛出到表现层进行处理,而且一般要分类处理,代码量大种类多所以可以用AOP

思想,对此mvc提供了专门的异常处理方式,以便统一的,方便的处理异常

image-20220504123227629

image-20220504123246842

当出现异常时,如果后端不使用异常处理器,前端会出现一大堆报错,使用异常处理器过后,出现异常系统会根据你写的处理方式返回或者不返回数据(都是自定义的)

如下在controller下创建异常处理器

//标志异常处理器(该注解一定要被mvc配置类扫描到)
@RestControllerAdvice
public class ProjectExceptionAdvice {
//定义要抓取的异常种类,下面是所有异常
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
//处理方式自己定义
System.out.println("出现异常");
return new Result(666,null,"Controller处出现异常");
}
}

项目异常处理方案

image-20220504123719796

在主目录创建个自定义异常包,里面管理自定义异常(异常分类)

image-20220504123823368

代码都是差不多的

//这里本来应该继承Exception,但是每个方法后面都要加serviceSystemException,而RuntimeException默认出现异常可以不处理自动往上抛
public class SystemException extends RuntimeException{
//给定异常编号,帮助识别异常种类
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code,String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}

我们模仿异常并将异常转化为自定义的异常(这里再service实现类中byid方法中模拟异常)

image-20220505215115237

//模仿服务端异常(不规范操作)
if(id==1){
throw new BusinessException(Code.BUSICESS_ERR,"请合理操作");
}

这些异常编号都是保存在code类里面的根据需要增加减少

抛出这些异常后我们要在异常处理器中写好对应异常的解决方案

一般分为自定义异常解决方案和其他异常解决方案(根据需求完成代码)

//标志异常处理器(该注解一定要被mvc配置类扫描到)
@RestControllerAdvice
public class ProjectExceptionAdvice {
//定义要抓取的异常种类,下面是所有异常
@ExceptionHandler(SystemException.class)
public Result doException(SystemException ex){
//处理方式自己定义
//记录日志
//发送消息给运维
//发送消息给开发人员
return new Result(ex.getCode(),"null",ex.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doException(BusinessException ex){
//处理方式自己定义
//记录日志
//发送消息给运维
//发送消息给开发人员
return new Result(ex.getCode(),"null",ex.getMessage());
}
//处理其他异常
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
//处理方式自己定义
//记录日志
//发送消息给运维
//发送消息给开发人员
return new Result(Code.SYSTEM_UNKNOW_ERR,"null","系统繁忙请稍后再试!");
}
}

------------------------------

SSM整合(前端层)

导入页面,放行资源

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}

我们使用的页面和之前web的页面有点不同,因为现在我们的接口简化了,所以方法全部重新写,且移除了分页功能

列表

methods: {
//列表
getAll() {
//发送ajax请求
axios.get("/books").then((res)=>{
this.dataList=res.data.data;
})
},

添加按钮

image-20220506101222387

image-20220506101312127

更改代码如下完成点击按钮弹窗,提交表单发送请求且关闭弹窗刷新界面

这里要做是否成功的判断,应为后端我们已经根据请求是否成功返回了状态码,所以直接用状态码做判断条件

image-20220506101733089

这里还要改下service层和dao层,原本是无返回值的我们修改数据后应该返回其行记数

image-20220506103114634

image-20220506103132482

//弹出添加窗口
handleCreate() {
// 显示弹窗
this.dialogFormVisible=true;
this.resetForm();
},
//重置表单
resetForm() {
this.formData={};
},
//添加
handleAdd () {
// 发送ajax请求
axios.post("/books",this.formData).then((res)=>{
//如果操作成功关闭弹层
if(res.data.code==20011){
this.dialogFormVisible=false;
this.$message.success("添加成功");
}else if(res.data.code==20010){
this.$message.error("添加失败");
}else{
this.$message.error(res.data.msg);
}
}).finally(()=>{
this.getAll();
})
},

修改

重要的是根据点击行的数据获取相应id传入后端查询数据回显到表单上

按钮上绑定了返回行数据的操作

image-20220506105957890

//弹出编辑窗口
handleUpdate(row) {
axios.get("/books/"+row.id).then((res)=>{
if(res.data.code==20041){
this.formData=res.data.data;
this.dialogFormVisible4Edit=true;
}else{
this.$message.error(res.data.msg);
}
})
},

随后更改的代码和添加基本是一样的(要改请求方法和弹窗名称)

//编辑
handleEdit() {
// 发送ajax请求
axios.put("/books",this.formData).then((res)=>{
//如果操作成功关闭弹层
if(res.data.code==20031){
this.dialogFormVisible4Edit=false;
this.$message.success("修改成功");
}else if(res.data.code==20030){
this.$message.error("修改失败");
}else{
this.$message.error(res.data.msg);
}
}).finally(()=>{
this.getAll();
})
},

删除

在修改的基础上要弹出提示框判断是否删除

// 删除
handleDelete(row) {
// 弹出提示框
this.$confirm("此操作永久删除该条数据,是否继续?","提示",{
type:'info'}).then(()=>{
// 删除业务
// 发送ajax请求
axios.delete("/books/"+row.id).then((res)=>{
//如果操作成功关闭弹层
console.log(res.data);
if(res.data.code==20021){
this.$message.success("删除成功");
}else{
this.$message.error("删除失败");
}
});
}).catch(()=>{
// 取消
this.$message.info("取消删除");
}).finally(()=>{
this.getAll();
});
}
}
})
posted @   Ember00  阅读(97)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示