【SSM】整合 - 基于图书的增删改查操作

举报
向阳花花花花 发表于 2023/07/26 10:10:38 2023/07/26
【摘要】 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>

二、整合SSM配置

2.1 Spring

编写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 {
}

2.2 Mybatis

(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;
    }
}

2.3 SpringMVC

(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};
    }
}

三、功能模块开发

3.1 表与实体类

在这里插入图片描述


public class Book {

    /**
     * 书籍id
     */
    private Integer id;
    /**
     * 书籍类别
     */
    private String bookType;
    /**
     * 书名
     */
    private String bookName;
    /**
     * 书籍描述
     */
    private String bookDesc;
	// 省略了getter setter 有参 无参构造方法和toString()方法
 }

3.2 mapper接口和自动代理


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();
}

3.3 service接口和实现类


@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测试

3.4 controller接口


@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对象。

五、异常处理器

5.1 异常处理

(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");
    }
}

模拟异常
异常出现统一

5.2 项目异常处理步骤

(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项目异常处理流程梳理完毕。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。