【SSM】整合 - 基于图书的增删改查操作
一、创建工程
(1)使用IDEA创建一个Maven项目,具体可以参考这篇文章。
使用IDEA创建Maven项目(图文并茂超详细)
(2)项目用到的依赖添加到Pom.xml中
<!-- 打包方式-->
<packaging>war</packaging>
<!-- tomcat7插件-->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<!-- GET请求乱码问题-->
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
<!-- SpringMVC-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.24</version>
</dependency>
<!-- druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.10</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- mybatis提供给Spring的接口-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!-- SpringJDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
<!-- servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Spring测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 导入JSON转换器-Jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
编写Spring配置类,用于代替ApplicationContext.xml文件,并扫描业务层的bean。
/**
* Spring配置类
*/
@Configuration // 设置当前类为Spring配置类
@ComponentScan({"com.itxiaoguo.service"}) // 扫描这些包下的bean
@PropertySource(value = {"classpath:jdbc.properties"}) // 扫描这个资源
@Import({JdbcConfig.class, MybatisConfig.class}) // 导入这两个配置
@EnableTransactionManagement // 开启Spring事务支持
public class SpringConfig {
}
(1)编写jdbc.properties文件,并放在resources目录下。
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false
jdbc.username=root
jdbc.password=@123456
(2)编写JdbcConfig,用于创建数据源对象和管理JDBC事务。
/**
* JDBC连接类
*/
public class JdbcConfig {
/**
* 注入配置文件的信息——基本类型注入
*/
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建数据源对象,传入给Mybatis
* @return
*/
@Bean
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
/**
* 编写JDBC事务管理器的Bean
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
(3)编写MybatisConfig类,用于获取数据源对象和创建mapper代理。
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource); // 设置数据源
bean.setTypeAliasesPackage("com.itxiaoguo.bean"); // 设置类型别名的实体类
return bean;
}
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.itxiaoguo.mapper"); // 设置要生成代理的包
return configurer;
}
}
(1)编写SpringMVC配置类,扫描controller层,并开启辅助支持。
/**
* SpringMVC配置类
*/
@Configuration
@ComponentScan("com.itxiaoguo.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
(2)编写Servlet容器初始化类,并处理Post请求乱码问题。
/**
* Servlet容器初始化类
*/
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 导入Spring配置信息
*
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 告知Tomcat服务器这是SpringMVC项目
*
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 将所有资源交给SpringMVC处理
*
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 解决POST请求中文乱码问题
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
public class Book {
/**
* 书籍id
*/
private Integer id;
/**
* 书籍类别
*/
private String bookType;
/**
* 书名
*/
private String bookName;
/**
* 书籍描述
*/
private String bookDesc;
// 省略了getter setter 有参 无参构造方法和toString()方法
}
public interface BookMapper {
/**
* 添加书籍
*
* @param book
* @return
*/
@Insert("INSERT INTO book(book_type,book_name,book_desc) VALUES (#{bookType},#{bookName},#{bookDesc})")
int addBook(Book book);
/**
* 修改书籍
*
* @param book
* @return
*/
@Update("UPDATE book SET book_type = #{bookType},book_name = #{bookName},book_desc = #{bookDesc} WHERE id = #{id}")
int updateBookById(Book book);
/**
* 通过id删除书籍
*
* @param id
* @return
*/
@Delete("DELETE from book WHERE id = #{id}")
int deleteBookById(@Param("id") Integer id);
/**
* 通过id查找书籍
* 使用@Results注解来映射数据库字段别名
*
* @param id
* @return
*/
@Select("SELECT * FROM book WHERE id = #{id}")
@Results(id = "resultMaps", value = {@Result(column = "book_type", property = "bookType"), @Result(column = "book_name", property = "bookName"), @Result(column = "book_desc", property = "bookDesc")})
Book getBookById(@Param("id") Integer id);
/**
* 查询所有书籍
*
* @return
*/
@Select("SELECT * FROM book")
@ResultMap("resultMaps")
List<Book> selectAll();
}
@Transactional
public interface BookService {
int addBook(Book book);
int updateBookById(Book book);
int deleteBookById(@Param("id") Integer id);
Book getBookById(@Param("id") Integer id);
List<Book> selectAll();
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper mapper;
@Override
public int addBook(Book book) {
int i = mapper.addBook(book);
return i;
}
@Override
public int updateBookById(Book book) {
int i = mapper.updateBookById(book);
return i;
}
@Override
public int deleteBookById(Integer id) {
int i = mapper.deleteBookById(id);
return i;
}
@Override
public Book getBookById(Integer id) {
Book book = mapper.getBookById(id);
return book;
}
@Override
public List<Book> selectAll() {
List<Book> books = mapper.selectAll();
return books;
}
}
业务层接口测试:使用Junit测试
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService service;
@PostMapping // @RequestBody注解用于接收JSON格式数据 POST请求的数据用@RequestParam注解接收
public int addBook(@RequestBody Book book) {
return service.addBook(book);
}
@PutMapping
public int updateBookById(@RequestBody Book book) {
return service.updateBookById(book);
}
@DeleteMapping("/{id}")
public int deleteBookById(@PathVariable Integer id) {
return service.deleteBookById(id);
}
@GetMapping("/{id}")
public Book getBookById(@PathVariable Integer id) {
return service.getBookById(id);
}
@GetMapping
public List<Book> selectAll() {
return service.selectAll();
}
}
表现层接口测试:使用Postman进行测试
因为各种对数据库的增删改查操作返回的结果不一定相同,表现层返回给前端人员的数据可能是int类型,对象类型,数组类型,……这就需要统一格式,通过对象将这些返回值封装在属性里。此外,通过状态码告诉前端人员操作是否成功。
下面是表现层数据封装模型:
/**
* 表现层数据封装模型-返回给前端的数据都是Result对象类型
*/
public class Result {
/**
* 返回的状态-成功与失败,何种操作
*/
private Integer code;
/**
* 返回的数据
*/
private Object data;
/**
* 成功或失败的响应信息
*/
private String msg;
public Result() {
}
/**
* 不带消息
*
* @param data
* @param code
*/
public Result(Integer code, Object data) {
this.data = data;
this.code = code;
}
/**
* 带消息
*
* @param data
* @param code
* @param msg
*/
public Result(Integer code, Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
// getter setter 已省略
状态码模型:
/**
* 定义增删改查操作成功或者失败的状态码-8种
*/
public class Code {
/**
* 结尾1代表成功,0代表失败
*/
public static final Integer ADD_OK = 20011;
public static final Integer ADD_ERR = 20010;
public static final Integer DELETE_OK = 20021;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_OK = 20031;
public static final Integer UPDATE_ERR = 20030;
public static final Integer SELECT_OK = 20041;
public static final Integer SELECT_ERR = 20040;
}
从而controller层返回的都是Result对象。
(1)可能出现异常之处
(2)解决步骤
将所有的异常都抛出到表现层,进行集中处理,但是肯定不能放到表现层控制器
上,因为不同异常处理方案不同,且会让代码臃肿,其实是使用Spring的AOP思想。
(3)异常处理类
@RestControllerAdvice // 将这个类作为异常处理器通知类
public class ProjectExceptionAdvice {
@ExceptionHandler({Exception.class}) // 拦截所有异常
public Result doException(Exception e) {
System.out.println("error ...");
return new Result(500,null,"error");
}
}
(1)异常分类及处理方案
业务异常 | 系统异常 | 其他异常 |
---|---|---|
用户操作产生的异常 | 项目运行中可预计不可避免的异常(如停电,系统宕机) | 编程人员无法预计的异常 |
发送对应消息给用户,提示规范操作 | 发送固定消息给用户,安抚用户(如您的网络状态不佳,请稍后重试);发送特定消息给运维人员;记录日志 | 和系统异常处理相似 |
(2)具体代码实现
1.新建自定义系统异常和业务异常类,两个类非常相似
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;
}
}
public class BusinessException extends RuntimeException {
/**
* 加入异常编号帮助识别是哪一种异常
*/
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
2.在Code类中增加了几种状态码:
/**
* 异常状态码(可以细分)
*/
public static final Integer SYSTEM_ERR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
public static final Integer BUSINESS_ERR = 60002;
public static final Integer SYSTEM_UNKNOWN_ERR = 59999;
3.在业务层BookServiceImpl类分别模拟业务异常和系统异常:
@Override
public Book getBookById(Integer id) {
// 模拟业务异常
if (id == 1) {
throw new BusinessException(Code.BUSINESS_ERR, "ban!");
}
// 模拟系统异常
try {
int i = 1 / 0;
} catch (Exception e) {
throw new SystemException(Code.SYSTEM_TIMEOUT_ERR, "Time out!", e); // 将可能出现的异常进行包装,转换为自定义异常
}
Book book = mapper.getBookById(id);
return book;
}
4.在异常通知类中添加这两种异常处理方法:
@RestControllerAdvice // 将这个类作为异常处理器通知类
public class ProjectExceptionAdvice {
@ExceptionHandler({SystemException.class}) // 拦截系统异常
public Result doSystemException(SystemException e) {
// 1.记录日志
// 2.发送消息给运维
// 3.发送邮件给开发人员
System.out.println("SystemException ...");
return new Result(e.getCode(), null, e.getMessage());
}
@ExceptionHandler({BusinessException.class}) // 拦截业务异常
public Result doBusinessException(BusinessException e) {
System.out.println("BusinessException ...");
return new Result(e.getCode(), null, e.getMessage());
}
@ExceptionHandler({Exception.class}) // 拦截未知异常
public Result doException(Exception e) {
// 1.记录日志
// 2.发送消息给运维
// 3.发送邮件给开发人员
System.out.println("Exception ...");
return new Result(Code.SYSTEM_UNKNOWN_ERR, null, "System busy ...");
}
}
5.重启服务器,使用Postman测试:
至此,SSM项目异常处理流程梳理完毕。
- 点赞
- 收藏
- 关注作者
评论(0)