文章目录
SpringMVC学习笔记(三) - SSM整合案例
初始化工程
初始化工程相关代码资料链接:提取码-6666
创建工程
整合配置
- 基本的SSM整合项目需要依赖的包
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>81</port> <path>/</path> </configuration> </plugin> </plugins> </build>
- 基本的项目项目结构
SSM整合
-
Spring
在config包下创建SpingConfig类@Configuration @ComponentScan({"com.itheima.service"}) @PropertySource("jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) public class SpringConfig { }
-
MyBatis
在resources包下创建jdbc.properties,配置连接数据库的基本信息jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db1 jdbc.username=root jdbc.password=XXXX
在config包下创建JdbcConfig,导入数据库基本信息
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
在config包下创建MyBatisConfig类,完成数据库到类对象之间的映射
public class MybatisConfig { public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.itheima.domain"); return factoryBean; } public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.itheima.dao"); return msc; } }
-
SpringMVC
在config包下创建ServletConfig类,初始化web容器public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } }
在config包下创建SpringMvcConfig类,实现后台数据与前台数据交互(可以理解为服务端和客户端的枢纽)
@Configuration @ComponentScan("com.itheima.controller") @EnableWebMvc public class SpringMvcConfig { }
功能模块
-
表与实体类:在domain包下创建Book实体类
public class Book { private Integer id; private String type; private String name; private String description; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public String toString() { return "Book{" + "id=" + id + ", type='" + type + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + '}'; } }
-
dao(接口+自动代理):在dao包下创建BookDao接口,通过自动代理实现接口类
public interface BookDao { /** * 插入新的数据操作 * @param book */ @Insert("insert into tbl_book values(null,#{type},#{name},#{description}") public void save(Book book); /** * 修改数据操作 * @param book */ @Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id=#{id}") public void update(Book book); /** * 根据id删除指定数据操作 * @param id */ @Delete("delete from tbl_book where id=#{id}") public void delete(Integer id); /** * 根据id查找指定数据操作 * @param id * @return */ @Select("select * from tbl_book where id=#{id}") public Book getById(Integer id); /** * 查找所有数据操作 * @return */ @Select("select * from tbl_book") public List<Book> getAll(); }
功能模块开发
表现层数据封装(前后端连接的枢纽)
问题引入:解决前端从后端接收统一格式的数据
- 提出创建结果模型类,封装数据到data属性中,达到前端接收数据格式
- 规定前端接收数据格式:封装操作结果到code属性中
- 添加前端接收数据格式:封装特殊消息到message(msg)属性中
表现层数据封装的具体操作
-
设置统一数据返回结果类
public class Result { private Object data; private Integer code; private String msg; public Result() { } public Result(Object data, Integer code, String msg) { this.data = data; this.code = code; this.msg = msg; } public Result(Object data, Integer code) { this.data = data; this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
-
设置统一数据返回结果编码
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; }
-
根据情况设定合理的Result
@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,flag?Code.SAVE_OK:Code.SAVE_ERR); } @PutMapping public Result update(@RequestBody Book book) { boolean flag = bookService.update(book); return new Result(flag,flag?Code.UPDATE_OK:Code.UPDATE_ERR); } @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean flag = bookService.delete(id); return new Result(flag,flag?Code.DELETE_OK:Code.DELETE_ERR); } @GetMapping("/{id}") public Result getById(@PathVariable Integer id) { Book book = bookService.getById(id); Integer code = book != null ? Code.GET_OK:Code.GET_ERR; String msg = book !=null ? "":"数据查询失败,请重试"; return new Result(book,code,msg); } @GetMapping public Result getAll() { List<Book> bookList = bookService.getAll(); Integer code = bookList != null ? Code.GET_OK:Code.GET_ERR; String msg = bookList !=null ? "":"数据查询失败,请重试!"; return new Result(bookList,code,msg); } }
异常处理
各个层级均出现异常,所有的异常均抛出到表现层进行处理(借鉴AOP思想)
项目异常分类
-
【业务异常】:规范的用户行为产生的异常;不规范的用户行为操作产生的异常
发送对应消息传递给用户,提醒规范操作
-
【系统异常】:项目运行过程中可预计且无法避免的异常
发送固定消息传递给用户,安抚用户
-
【其它异常】:编程人员未预期到的异常
发送固定消息传递给用户,安抚用户
基本步骤:
-
自定义项目系统级异常
public class SystemException extends RuntimeException { private Integer code;//异常编号 public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public SystemException(String message, Integer code) { super(message); this.code = code; } public SystemException(String message, Throwable cause, Integer code) { super(message, cause); this.code = code; } }
-
自定义项目业务级异常
public class BusinessException extends RuntimeException { private Integer code;//异常编号 public Integer getCode() { return code; } public BusinessException(String message, Integer code) { super(message); this.code = code; } public BusinessException(String message, Throwable cause, Integer code) { super(message, cause); this.code = 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; public static final Integer SYSTEM_ERR = 50001; public static final Integer SYSTEM_TIMEOUT_ERR = 50002; public static final Integer SYSTEM_UNKNOWN_ERR = 59999; public static final Integer BUSINESS_ERR = 60002; }
-
拦截并处理异常
/** * 异常处理器 */ @RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException ex){ //记录日志 //发送消息给运维 //发送邮件给开发人员 return new Result(null,ex.getCode(),ex.getMessage()); } @ExceptionHandler(BusinessException.class) public Result doBusinessException(BusinessException ex){ return new Result(null,ex.getCode(),ex.getMessage()); } @ExceptionHandler(Exception.class) public Result doException(Exception ex){ //记录日志 //发送消息给运维 //发送邮件给开发人员 return new Result(null,Code.SYSTEM_UNKNOWN_ERR,"系统繁忙,请稍后再试!"); } }
前后端数据交互
- 导入前端页面文件
- 设置不需要拦截的资源路径,同时还要再SpringMvcConfig类的
@Import
中导入com.itheima.config
包@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
- 显示所有数据到页面(在
/webapp/pages/books.html
中添加以下代码)//列表 getAll() { //发送ajax请求 axios.get("/books").then((res)=>{ this.dataList = res.data.data; }); },
添加功能及其状态处理
在正式开发添加功能的时候,为了保证代码的健壮性,我们需要对添加正常和异常进行消息提醒,基于前面章节我们对数据层和业务层后台代码进行修改
- 修改数据层,将返回结果类型返回查找到的数据存放列表的长度(目的是判断列表是否存在有效数据)
public interface BookDao { @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})") public int save(Book book); @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}") public int update(Book book); @Delete("delete from tbl_book where id = #{id}") public int 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 public class BookServiceImpl implements BookService { //注意这是BookServiveImpl的部分代码,简化本部分的实现的代码方便大家理解 @Autowired private BookDao bookDao; public boolean save(Book book) { return bookDao.save(book)>0; } public List<Book> getAll() { return bookDao.getAll(); } }
- 实现页面交互逻辑
//钩子函数,VUE对象初始化完成后自动执行 created() { this.getAll(); }, //写的是实现添加功能及状态处理的交互逻辑代码 methods: { //列表 getAll() { //发送ajax请求 axios.get("/books").then((res)=>{ this.dataList = res.data.data; }); }, //弹出添加窗口 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查找对应的数据,并将数据回显到页面
//弹出编辑窗口 handleUpdate(row) { //查询数据,根据id查询 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(); }); },
删除功能
- 根据id找到对应的数据,随后在业务层调用删除操作
// 删除 handleDelete(row) { //弹出提示框 this.$confirm("此操作永久删除","提示",{ type:'info' }).then(()=>{ axios.delete("/books/"+row.id).then((res)=>{ //判断删除是否成功 if(res.data.code == 20021){ this.$message.success("删除成功!"); }else{ this.$message.error("删除失败"); } }); }).catch(()=>{ //取消删除 this.$message.error("取消删除"); }).finally(()=>{ this.getAll(); }); }