SSM整合
SSM整合流程
1.创建工程
pom.xml
1 <dependencies> 2 <dependency> 3 <groupId>org.springframework</groupId> 4 <artifactId>spring-webmvc</artifactId> 5 <version>5.2.10.RELEASE</version> 6 </dependency> 7 8 <dependency> 9 <groupId>org.springframework</groupId> 10 <artifactId>spring-jdbc</artifactId> 11 <version>5.2.10.RELEASE</version> 12 </dependency> 13 14 <dependency> 15 <groupId>org.springframework</groupId> 16 <artifactId>spring-test</artifactId> 17 <version>5.2.10.RELEASE</version> 18 </dependency> 19 20 <dependency> 21 <groupId>org.mybatis</groupId> 22 <artifactId>mybatis</artifactId> 23 <version>3.5.6</version> 24 </dependency> 25 26 <dependency> 27 <groupId>org.mybatis</groupId> 28 <artifactId>mybatis-spring</artifactId> 29 <version>1.3.0</version> 30 </dependency> 31 32 <dependency> 33 <groupId>mysql</groupId> 34 <artifactId>mysql-connector-java</artifactId> 35 <version>8.0.13</version> 36 </dependency> 37 38 <dependency> 39 <groupId>com.alibaba</groupId> 40 <artifactId>druid</artifactId> 41 <version>1.1.16</version> 42 </dependency> 43 44 <dependency> 45 <groupId>junit</groupId> 46 <artifactId>junit</artifactId> 47 <version>4.12</version> 48 <scope>test</scope> 49 </dependency> 50 51 <dependency> 52 <groupId>javax.servlet</groupId> 53 <artifactId>javax.servlet-api</artifactId> 54 <version>3.1.0</version> 55 <scope>provided</scope> 56 </dependency> 57 58 <dependency> 59 <groupId>com.fasterxml.jackson.core</groupId> 60 <artifactId>jackson-databind</artifactId> 61 <version>2.9.0</version> 62 </dependency> 63 </dependencies> 64 65 <build> 66 <plugins> 67 <plugin> 68 <groupId>org.apache.tomcat.maven</groupId> 69 <artifactId>tomcat7-maven-plugin</artifactId> 70 <version>2.2</version> 71 <configuration> 72 <port>80</port> 73 <path>/</path> 74 </configuration> 75 </plugin> 76 </plugins> 77 </build>
2.SSM整合
Spring
SpringConfig
1 @Configuration 2 @ComponentScan("com.test.service") 3 @PropertySource("classpath:jdbc.properties") 4 @Import({JdbcConfig.class,MybatisConfig.class}) 5 @EnableTransactionManagement //开启Spring事务管理 6 public class SpringConfig { 7 }
MyBatis
MybatisConfig
1 public class MybatisConfig { 2 @Bean 3 public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ 4 SqlSessionFactoryBean factoryBean= new SqlSessionFactoryBean(); 5 factoryBean.setDataSource(dataSource); 6 factoryBean.setTypeAliasesPackage("com.test.domain"); 7 return factoryBean; 8 } 9 10 @Bean 11 public MapperScannerConfigurer mapperScannerConfigurer(){ 12 MapperScannerConfigurer msc = new MapperScannerConfigurer(); 13 msc.setBasePackage("com.test.dao"); 14 return msc; 15 } 16 }
JdbcConfig
1 public class JdbcConfig { 2 @Value("${jdbc.driver}") 3 private String driver; 4 @Value("${jdbc.url}") 5 private String url; 6 @Value("${jdbc.username}") 7 private String username; 8 @Value("${jdbc.password}") 9 private String password; 10 //配置连接池 11 @Bean 12 public DataSource dataSource() { 13 DruidDataSource dataSource=new DruidDataSource(); 14 dataSource.setDriverClassName(driver); 15 dataSource.setUrl(url); 16 dataSource.setUsername(username); 17 dataSource.setPassword(password); 18 return dataSource; 19 } 20 //Spring事务管理需要的平台事务管理器对象 21 @Bean 22 public PlatformTransactionManager transactionManager(DataSource dataSource){ 23 DataSourceTransactionManager dtm = new DataSourceTransactionManager(); 24 dtm.setDataSource(dataSource); 25 return dtm; 26 } 27 }
jdbc.properties
1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&useServerPrepStmts=true&serverTimezone=UTC 3 jdbc.username=root 4 jdbc.password=123456
SpringMVC
ServletConfig
1 public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { 2 protected Class<?>[] getRootConfigClasses() { 3 return new Class[]{SpringConfig.class}; 4 } 5 6 protected Class<?>[] getServletConfigClasses() { 7 return new Class[]{SpringMvcConfig.class}; 8 } 9 10 protected String[] getServletMappings() { 11 return new String[]{"/"}; 12 } 13 }
SpringMvcConfig
1 @Configuration 2 @ComponentScan("com.test.controller") 3 @EnableWebMvc 4 public class SpringMvcConfig { 5 }
3.功能模块
表与实体类
1 public class Book { 2 private Integer id; 3 private String type; 4 private String name; 5 private String description; 6 ... //getter、setter、toString()方法 7 }
dao(接口+自动代理)
1 public interface BookDao { 2 //@Insert("insert into tbl_book values(null,#{type},#{name},#{description})") 3 @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})") 4 public int save(Book book); //返回值表示影响的行数 5 6 @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}") 7 public int update(Book book); 8 9 @Delete("delete from tbl_book where id = #{id}") 10 public int delete(Integer id); 11 12 @Select("select * from tbl_book where id = #{id}") 13 public Book getById(Integer id); 14 15 @Select("select * from tbl_book") 16 public List<Book> getAll(); 17 }
service(接口+实现类)
接口:
1 @Transactional //表示所有方法进行事务管理 2 public interface BookService { 3 /** 4 * 增加 5 * @param book 6 * @return 7 */ 8 public boolean save(Book book); 9 10 /** 11 * 修改 12 * @param book 13 * @return 14 */ 15 public boolean update(Book book); 16 17 /** 18 * 根据id删除 19 * @param id 20 * @return 21 */ 22 public boolean delete(Integer id); 23 24 /** 25 * 根据id查询 26 * @param id 27 * @return 28 */ 29 public Book getById(Integer id); 30 31 /** 32 * 查询全部 33 * @return 34 */ 35 public List<Book> getAll(); 36 }
实现类:
1 @Service 2 public class BookServiceImpl implements BookService { 3 @Autowired 4 private BookDao bookDao; 5 6 public boolean save(Book book) { 7 bookDao.save(book); 8 return true; 9 } 10 11 public boolean update(Book book) { 12 bookDao.update(book); 13 return true; 14 } 15 16 public boolean delete(Integer id) { 17 bookDao.delete(id); 18 return true; 19 } 20 21 public Book getById(Integer id) { 22 return bookDao.getById(id); 23 } 24 25 public List<Book> getAll() { 26 return bookDao.getAll(); 27 } 28 }
业务层接口测试(整合JUnit)
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes = SpringConfig.class) 3 public class BookServiceTest { 4 @Autowired 5 private BookService bookService; 6 7 @Test 8 public void testGetById(){ 9 Book book = bookService.getById(1); 10 System.out.println(book); 11 } 12 @Test 13 public void testGetAll(){ 14 List<Book> books = bookService.getAll(); 15 System.out.println(books); 16 } 17 }
controller
Result类封装响应结果:
1 public class Result { 2 //描述统一格式中的数据 3 private Object data; 4 //描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败 5 private Integer code; 6 //描述统一格式中的消息,可选属性 7 private String msg; 8 9 public Result() { 10 } 11 public Result(Integer code,Object data) { 12 this.data = data; 13 this.code = code; 14 } 15 public Result(Integer code, Object data, String msg) { 16 this.data = data; 17 this.code = code; 18 this.msg = msg; 19 } 20 ... //添加getter、setter方法 21 }
Code类封装响应码:
1 //状态码 2 public class Code { 3 public static final Integer SAVE_OK = 20011; 4 public static final Integer DELETE_OK = 20021; 5 public static final Integer UPDATE_OK = 20031; 6 public static final Integer GET_OK = 20041; 7 8 public static final Integer SAVE_ERR = 20010; 9 public static final Integer DELETE_ERR = 20020; 10 public static final Integer UPDATE_ERR = 20030; 11 public static final Integer GET_ERR = 20040; 12 }
表现层数据封装返回Result对象:
1 @RestController 2 @RequestMapping("/books") 3 public class BookController { 4 @Autowired 5 private BookService bookService; 6 7 @PostMapping 8 public Result save(@RequestBody Book book) { 9 boolean flag = bookService.save(book); 10 return new Result(flag?Code.SAVE_OK:Code.SAVE_ERR,flag); 11 } 12 @PutMapping 13 public Result update(@RequestBody Book book) { 14 boolean flag = bookService.update(book); 15 return new Result(flag?Code.UPDATE_OK:Code.UPDATE_ERR,flag); 16 } 17 @DeleteMapping("/{id}") 18 public Result delete(@PathVariable Integer id) { 19 boolean flag = bookService.delete(id); 20 return new Result(flag?Code.DELETE_OK:Code.DELETE_ERR,flag); 21 } 22 @GetMapping("/{id}") 23 public Result getById(@PathVariable Integer id) { 24 Book book = bookService.getById(id); 25 Integer code= book!=null?Code.GET_OK:Code.GET_ERR; 26 String msg=book!=null?"":"数据查询失败,请重试!"; 27 return new Result(code,book,msg); 28 } 29 @GetMapping 30 public Result getAll() { 31 List<Book> books = bookService.getAll(); 32 Integer code= books!=null?Code.GET_OK:Code.GET_ERR; 33 String msg=books!=null?"":"数据查询失败,请重试!"; 34 return new Result(code,books,msg); 35 } 36 }
表现层接口测试(PostMan)
业务异常(BusinessException)
规范的用户行为产生的异常
不规范的用户行为操作产生的异常
系统异常(SystemException)
项目运行过程中可预计且无法避免的异常
其他异常(Exception)
编程人员未预期到的异常
业务异常(BusinessException)
发送对应消息传递给用户,提醒规范操作
系统异常(SystemException)
发送固定消息传递给用户,安抚用户
发送特定消息给运维人员,提醒维护
记录日志
其他异常(Exception)
发送固定消息传递给用户,安抚用户
发送特定消息给编程人员,提醒维护(纳入预期范围内)
记录日志
1 //自定义异常处理器,用于封装异常信息,对异常进行分类 2 public class SystemException extends RuntimeException{ 3 private Integer code; 4 5 public Integer getCode() { 6 return code; 7 } 8 public void setCode(Integer code) { 9 this.code = code; 10 } 11 12 public SystemException(Integer code, String message) { 13 super(message); 14 this.code = code; 15 } 16 17 public SystemException(Integer code, String message, Throwable cause) { 18 super(message, cause); 19 this.code = code; 20 } 21 }
自定义项目业务级异常
1 //自定义异常处理器,用于封装异常信息,对异常进行分类 2 public class BusinessException extends RuntimeException{ 3 private Integer code; 4 5 public Integer getCode() { 6 return code; 7 } 8 public void setCode(Integer code) { 9 this.code = code; 10 } 11 12 public BusinessException(Integer code, String message) { 13 super(message); 14 this.code = code; 15 } 16 17 public BusinessException(Integer code,String message,Throwable cause) { 18 super(message, cause); 19 this.code = code; 20 } 21 }
1 public class Code { 2 //之前其他状态码省略没写,以下是新补充的状态码,可以根据需要自己补充 3 public static final Integer SYSTEM_ERR = 50001; 4 public static final Integer SYSTEM_TIMEOUT_ERR = 50002; 5 public static final Integer SYSTEM_UNKNOW_ERR = 59999; 6 public static final Integer BUSINESS_ERR = 60002; 7 8 }
1 @Service 2 public class BookServiceImpl implements BookService { 3 @Autowired 4 private BookDao bookDao; 5 //在getById演示触发异常,其他方法省略 6 public Book getById(Integer id) { 7 //模拟业务异常,包装成自定义异常 8 if(id <0){ 9 throw new BusinessException(Code.BUSINESS_ERR,"请不要使用你的技术挑战我的耐性!"); 10 } 11 } 12 }
@RestControllerAdvice
类型:类注解
位置:Rest风格开发的控制器增强类定义上方。
作用:为Rest风格开发的控制器类做增强。
说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能。
@ExceptionHandler
类型:方法注解
位置:专用于异常处理的控制器方法上方。
作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行。
说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常。
1 @RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器 2 public class ProjectExceptionAdvice { 3 //@ExceptionHandler用于设置当前处理器类对应的异常类型 4 @ExceptionHandler(SystemException.class) 5 public Result doSystemException(SystemException ex){ 6 //记录日志 7 //发送消息给运维 8 //发送邮件给开发人员,ex对象发送给开发人员 9 return new Result(ex.getCode(),null,ex.getMessage()); 10 } 11 12 @ExceptionHandler(BusinessException.class) 13 public Result doBusinessException(BusinessException ex){ 14 return new Result(ex.getCode(),null,ex.getMessage()); 15 } 16 17 //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常 18 @ExceptionHandler(Exception.class) 19 public Result doOtherException(Exception ex){ 20 //记录日志 21 //发送消息给运维 22 //发送邮件给开发人员,ex对象发送给开发人员 23 return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!"); 24 } 25 }
注:该方法所在的包要在SpringMvcConfig配置类的@ComponentScan()注解中添加。
测试结果:
1 @Configuration 2 public class SpringMvcSupport extends WebMvcConfigurationSupport { 3 @Override 4 protected void addResourceHandlers(ResourceHandlerRegistry registry) { 5 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); 6 registry.addResourceHandler("/css/**").addResourceLocations("/css/"); 7 registry.addResourceHandler("/js/**").addResourceLocations("/js/"); 8 registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); 9 } 10 }
1 //列表 2 getAll() { 3 //发送ajax请求 4 axios.get("/books").then((res)=>{ 5 this.dataList = res.data.data; 6 }); 7 }
1 //弹出添加窗口 2 handleCreate() { 3 this.dialogFormVisible = true; 4 this.resetForm(); 5 }, 6 //重置表单 7 resetForm() { 8 this.formData = {}; 9 }, 10 //添加 11 handleAdd () { 12 //发送ajax请求 13 axios.post("/books",this.formData).then((res)=>{ 14 console.log(res.data); 15 //如果操作成功,关闭弹层,显示数据 16 if(res.data.code == 20011){ 17 this.dialogFormVisible = false; 18 this.$message.success("添加成功"); 19 }else if(res.data.code == 20010){ 20 this.$message.error("添加失败"); 21 }else{ 22 this.$message.error(res.data.msg); 23 } 24 }).finally(()=>{ 25 this.getAll(); 26 }); 27 },
后台代码改进
1 public interface BookDao { 2 @Insert("insert into tbl_book values (null,#{type},#{name},#{description})") 3 public int save(Book book); //返回值改为int 4 @Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id=#{id}") 5 public int update(Book book); //返回值改为int 6 @Delete("delete from tbl_book where id=#{id}") 7 public int delete(Integer id); //返回值改为int 8 @Select("select * from tbl_book where id=#{id}") 9 public Book getById(Integer id); 10 @Select("select * from tbl_book") 11 public List<Book> getAll(); 12 }
1 @Service 2 public class BookServiceImpl implements BookService { 3 @Autowired 4 private BookDao bookDao; 5 //增删改的方法判断了影响的行数是否大于0,而不是固定返回true 6 public boolean save(Book book) { 7 return bookDao.save(book) > 0; 8 } 9 //增删改的方法判断了影响的行数是否大于0,而不是固定返回true 10 public boolean update(Book book) { 11 return bookDao.update(book) > 0; 12 } 13 //增删改的方法判断了影响的行数是否大于0,而不是固定返回true 14 public boolean delete(Integer id) { 15 return bookDao.delete(id) > 0; 16 } 17 18 public Book getById(Integer id) { 19 if(id < 0){ 20 throw new BusinessException(Code.BUSINESS_ERR,"请不要使用你的技术挑战我的耐性!"); 21 return bookDao.getById(id); 22 } 23 } 24 public List<Book> getAll() { 25 return bookDao.getAll(); 26 } 27 }
1 //弹出编辑窗口 2 handleUpdate(row) { 3 // console.log(row); //row.id 查询条件 4 //查询数据,根据id查询 5 axios.get("/books/"+row.id).then((res)=>{ 6 // console.log(res.data.data); 7 if(res.data.code == 20041){ 8 //展示弹层,加载数据 9 this.formData = res.data.data; 10 this.dialogFormVisible4Edit = true; 11 }else{ 12 this.$message.error(res.data.msg); 13 } 14 }); 15 }
保存修改后的图书信息
1 //编辑 2 handleEdit() { 3 //发送ajax请求 4 axios.put("/books",this.formData).then((res)=>{ 5 //如果操作成功,关闭弹层,显示数据 6 if(res.data.code == 20031){ 7 this.dialogFormVisible4Edit = false; 8 this.$message.success("修改成功"); 9 }else if(res.data.code == 20030){ 10 this.$message.error("修改失败"); 11 }else{ 12 this.$message.error(res.data.msg); 13 } 14 }).finally(()=>{ 15 this.getAll(); 16 }); 17 }
1 // 删除 2 handleDelete(row) { 3 //1.弹出提示框 4 this.$confirm("此操作永久删除当前数据,是否继续?","提示",{ 5 type:'info' 6 }).then(()=>{ 7 //2.做删除业务 8 axios.delete("/books/"+row.id).then((res)=>{ 9 if(res.data.code == 20021){ 10 this.$message.success("删除成功"); 11 }else{ 12 this.$message.error("删除失败"); 13 } 14 }).finally(()=>{ 15 this.getAll(); 16 }); 17 }).catch(()=>{ 18 //3.取消删除 19 this.$message.info("取消删除操作"); 20 }); 21 }