SSM学习——SSM整合案例(Spring+SpringMVC+Mybatis)(13)

16 篇文章 1 订阅

要求

在这里插入图片描述

Spirng整合Mybatis

基本的步骤就不说了,反正前面这么多案例了已经
现在,就把maven坐标与创建好的目录看一下

名字实例名/插件名
springmvc框架spring-webmvc
spring操纵jdbcspring-jdbc
spring操纵test测试spring-test
mybatis框架mybatis
servlet技术(运行需要屏蔽,即provided)javax.servlet-api
json技术jackson-databind
德鲁伊数据库连接池druid
mybatis整合springmybatis-spring
junit测试junit
mysql驱动mysql-connector-java
tomcat7插件(需要配端口与路径)tomcat7-maven-plugin

这里必须将characterEncoding=UTF-8放在url参数第一位!

jdbc.properties

jdbc.classname=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/20220806lige?characterEncoding=UTF-8&useSSL=false&useServerPrepStmts=true&useUnicode=true
jdbc.username=root
jdbc.password=root

SpringConfig.java配置类
在这里插入图片描述

JdbcConfig.java配置类

package cn.calendo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

public class JdbcConfig {

    //把jdbc.properties里的变量引入(通过@Value注解形式)
    @Value("${jdbc.classname}")
    private String classname;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    //收集连接池数据,整理成一个bean交给Spring管理
    @Bean
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(classname);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);

        return druidDataSource;
    }

    //事务控制器,整理成一个bean交给Spring管理
    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager ds = new DataSourceTransactionManager();
        ds.setDataSource(dataSource);
        return ds;
    }
}

MybatisConfig.java配置类

public class MybatisConfig {

    //开始造mybatis最渴望需要的工厂bean(又需要我们提供已造好的datasource),并启用类型别名(需要扫描pojo包)
    //这个datasource是在spring容器中自动帮我们装配好的(对,就是前面我们JdbcConfig设置好的datasource提交给spring容器处理好的)
    //返回一个工厂bean交给spring容器处理
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("cn.calendo.pojo");
        return sqlSessionFactoryBean;
    }

    //这个bean来指定从pojo类字段到dao层接口的映射,由于我们在dao中配的是接口,所以我们写到dao包即可
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("cn.calendo.dao");
        return msc;
    }
}

Spirng整合SpringMVC

ServletConfig.java配置类

public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    //对于root,我们需要加载spring的核心配置类,当web容器启动时,getRootConfigClasses会加载SpringConfig里面的bean
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }
	  //由于SpringMVC是Spring的父容器,所以SpringMVC容器能访问Spring容器中的东西,而Spring容器不能访问SpringMVC容器中的东西
    @Override
    //对于web容器,我们需要加载springMvc的配置类,当web容器启动时,getServletConfigClasses会加载SpringMVCConfig里面的bean
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMVCConfig.class};
    }

    @Override
    //springmvc需要拦截所有请求给自己处理,而不是给servlet一点机会
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    //这里本来可以写过滤器,但是我们先不写吧
}

SpringMVCConfig.java配置类

@Configuration
//他需要读取controller层的方法进行配置
@ComponentScan({"cn.calendo.controller"})
//必定开启功能强大的注解
@EnableWebMvc
public class SpringMVCConfig {
}

表与实体类的创建

sql脚本:

use 20220806lige;

drop table if exists tb_user;

create table tb_user
(
    id       int primary key auto_increment,
    username varchar(20),
    password varchar(20),
    gender   varchar(8),
    address  text
);



INSERT INTO tb_user
VALUES (1, '张三', '123456', '男', '浙江,杭州');
INSERT INTO tb_user
VALUES (2, '李四', '123456', '女', '浙江,温州');
INSERT INTO tb_user
VALUES (3, '王五', '123456', '男', '江苏,苏州');
INSERT INTO tb_user
VALUES (4, '刘六', '123456', '女', '江苏,南京');
INSERT INTO tb_user
VALUES (5, '孙七', '123456', '男', '山东,青岛');
INSERT INTO tb_user
VALUES (6, '钱吧', '123456', '女', '山东,烟台');
INSERT INTO tb_user
VALUES (7, '阿猫', '123456haode', '无', '福建,厦门');
INSERT INTO tb_user
VALUES (8, '阿狗', '1234we56', '无', '广东,深圳');
INSERT INTO tb_user
VALUES (9, '阿信', 'hahaha123456', '女', '广东,广州');
INSERT INTO tb_user
VALUES (10, '阿文', 'wokao123456', '男', '上海');

好的现在表已经建好了
在这里插入图片描述

pojo实体类

public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    //toString其实只是给我们调试用,其它没啥用的,但是我们都会加上它的
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

创建层级

在这里插入图片描述

Dao层

public interface UserDao {

    //md我们很懒我们写了接口然后不想自己写实现类,那就用mybatis的代理来帮我们创建这些实现类啊

    @Insert("insert into tb_user values(null, #{username}, #{password}, #{gender}, #{address})")
    public void add(User user);

    @Update("update tb_user set username = #{username}, password = #{password}, gender = #{gender}, address = #{address} where id = #{id}")
    public void update(User user);

    @Delete("delete from tb_user where id = #{id}")
    public void delete(Integer id);

    @Select("select * from tb_user where id = #{id}")
    public User selectById(Integer id);

    @Select("select * from tb_user")
    public List<User> selectAll();
}

Service层

UserService实现类

public interface UserService {

    /**
     * 新增/保存
     * @param user
     * @return
     */
    public boolean addUser(User user);

    /**
     * 修改/更新
     * @param user
     * @return
     */
    public boolean updateUser(User user);

    /**
     * 删除/注销
     * @param user
     * @return
     */
    public boolean deleteUser(User user);

    /**
     * 单个查询
     * @param id
     * @return
     */
    public User selectUserById(Integer id);

    /**
     * 查询所有
     * @return
     */
    public List<User> selectAllUser();
}

UserServiceImpl实现类

//定义组件给SpringConfig扫描到
@Service
public class UserServiceImpl implements UserService {

    //由于自动装配了我们就没有new一个user对象,idea好心帮我们查了,但是这不是错误,把这个错误忽略即可
    //自动装配,不需要我们手动实例化
    @Autowired
    private UserDao userDao;

    //然后我们需要调用Dao层的接口,把值传进去
    public boolean addUser(User user) {
        userDao.add(user);
        return true;
    }

    public boolean updateUser(User user) {
        userDao.update(user);
        return true;
    }


    public boolean deleteUser(Integer id) {
        userDao.delete(id);
        return true;
    }

    public User selectUserById(Integer id) {
        return userDao.selectById(id);
    }

    public List<User> selectAllUser() {
        return userDao.selectAll();
    }
}

controller层

UserController

//设置组件名,给springmvc加载,以及虚拟路径
@RestController
@RequestMapping("/users")
public class UserController {

    //也是自动装配,调用service层
    @Autowired
    private UserService userService;

    //post请求,参数实体类,用json传数据
    @PostMapping
    public boolean addUser(@RequestBody User user) {
        return userService.addUser(user);
    }

    //put请求,参数实体类,用json传数据
    @PutMapping
    public boolean updateUser(@RequestBody User user) {
        return userService.updateUser(user);
    }

    //delete请求,参数id,用路径传递
    @DeleteMapping("/{id}")
    public boolean deleteUser(@PathVariable Integer id) {
        return userService.deleteUser(id);
    }

    //get请求,参数id,用路径传递
    @GetMapping("/{id}")
    public User selectUserById(@PathVariable Integer id) {
        return userService.selectUserById(id);
    }

    //get请求,无参数
    @GetMapping
    public List<User> selectAllUser() {
        return userService.selectAllUser();
    }
}

接口测试

在企业开发中,当我们写完业务层service接口时需要进行junit测试,以及写完表现层controller接口时需要进行api-post测试

业务层service测试

//spring调用junit需要的引用类
//指定配置类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {

    @Autowired
    private UserService userService;

    /**
     * 测试删除一个用户
     * @param id
     */
    @Test
    public void testDeleteUser(Integer id){
        boolean user = userService.deleteUser(2);
        System.out.println(user);
    }

    /**
     * 测试根据id选择用户
     * @param id
     */
    @Test
    public void testSelectUserById() {
        User user = userService.selectUserById(1);
        //正规测试需要做断言匹配
        System.out.println(user);
    }

    /**
     * 测试选择全部用户
     */
    @Test
    public void testSelectAllUser() {
        List<User> ListUser = userService.selectAllUser();
        System.out.println(ListUser);
    }
}

表现层controller测试

建立测试数据(delete不需要任何数据只要在url上添加数字即可(遵循rest风格))
在这里插入图片描述

如果数据库中文是问号?那就在jdbc.properties后面加参数,示例:
jdbc.url=jdbc:mysql://localhost:3306/20220806lige?characterEncoding=UTF-8
成功
在这里插入图片描述

表现层数据封装

这就是前端与后端人员之间的通信协议

在这里插入图片描述

code代表操作的方法,以及成功与否1成功0失败
成功,数据都在data里,失败,返回信息放在msg

在这里插入图片描述

定义前后端通信协议

package cn.calendo.controller;

//定义前后端协议
public class Result {
    private Object data;
    public Integer code;
    private String msg;

    public Result() {
    }

    public Result(Object data, Integer code) {
        this.data = data;
        this.code = code;
    }

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

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

然后在controller里新建一个code,java来定义code码

package cn.calendo.controller;

public class Code {

    public static final Integer SAVE_OK = 20011;
    public static final Integer UPDATE_OK = 20021;
    public static final Integer DELETE_OK = 20031;
    public static final Integer SELECT_OK = 20041;

    public static final Integer SAVE_ERR = 20010;
    public static final Integer UPDATE_ERR = 20020;
    public static final Integer DELETE_ERR = 20030;
    public static final Integer SELECT_ERR = 20040;
}

这时我们在UserController里面所返回的值就需要改动了

package cn.calendo.controller;

import cn.calendo.pojo.User;
import cn.calendo.service.UserService;
import com.alibaba.druid.support.json.JSONUtils;
import com.sun.org.apache.xml.internal.serializer.Serializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.filter.CharacterEncodingFilter;

import java.io.Serializable;
import java.util.List;

//设置组件名,给springmvc加载,以及虚拟路径
@RestController
@RequestMapping("/users")
public class UserController {

    //也是自动装配,调用service层
    @Autowired
    private UserService userService;

    //post请求,参数实体类,用json传数据
    @PostMapping
    public Result addUser(@RequestBody User user) {
        boolean flag = userService.addUser(user);
        return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
    }

    //put请求,参数实体类,用json传数据
    @PutMapping
    public Result updateUser(@RequestBody User user) {
        boolean flag = userService.updateUser(user);
        return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
    }

    //delete请求,参数id,用路径传递
    @DeleteMapping("/{id}")
    public Result deleteUser(@PathVariable Integer id) {
        boolean flag = userService.deleteUser(id);
        return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
    }

    //get请求,参数id,用路径传递
    @GetMapping("/{id}")
    public Result selectUserById(@PathVariable Integer id) {
        User user = userService.selectUserById(id);
        Integer code = (user != null ? Code.SELECT_OK : Code.SELECT_ERR);
        String msg = (user != null ? "" : "数据查询失败!");
        return new Result(code, user, msg);
    }

    //get请求,无参数
    @GetMapping
    public Result selectAllUser() {
        List<User> ListUser = userService.selectAllUser();
        Integer code = (ListUser != null ? Code.SELECT_OK : Code.SELECT_ERR);
        String msg = (ListUser != null ? "" : "数据查询失败!");
        return new Result(code, ListUser, msg);
    }
}

测试之后,成功
在这里插入图片描述
在这里插入图片描述

异常处理

在这里插入图片描述

在这里插入图片描述

新建一个拦截异常类在controller层

//使用这个写好的aop
@RestControllerAdvice
public class ProjectExceptionAdvice {

    //此注解可以拦截指定异常
    @ExceptionHandler(Exception.class)
    public Result doException(Exception e) {
        System.out.println("拦截到异常");
        return new Result(404, null,"拦截到异常");
    }
}

在这里插入图片描述
在这里插入图片描述

新建异常包处理各种异常

package cn.calendo.exception;

public class SystemException extends RuntimeException {
    private Integer code;

    public Integer getCode() {
        return code;
    }

    public SystemException(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 SystemException(Integer code, Throwable cause) {
        super(cause);
        this.code = code;
    }

    public SystemException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.code = code;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前端

配置访问

由于springmvc会把webapp里的东西全部拦截,所以我们需要放行

新建一个叫SpringMVCSupport的访问放行配置

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

然后让SpringMVCConfig去扫描
@ComponentScan({"cn.calendo.controller", "cn.calendo.config"})

页面展示

我们写一下getAll即可获取到全部数据
在这里插入图片描述
在这里插入图片描述

然后把新增业务做出来
在这里插入图片描述

修改功能:

在这里插入图片描述

vue methods:

methods: {
            //列表
            getAll() {
                axios.get("/users").then((res) => {
                    this.dataList = res.data.data;
                })
            },
            //弹出添加窗口
            handleCreate() {
                this.resetForm();
                this.dialogFormVisible = true;
            },
            //重置表单
            resetForm() {
                this.formData = {};
            },
            //添加
            handleAdd() {
                axios.post("/users", this.formData).then((res) => {
                    if (res.data.code === 20011) {
                        this.dialogFormVisible = false;
                        this.$message.success("添加成功" + " " + "status: " + res.data.code);
                    } else if (res.data.code === 20010) {
                        this.$message.error("添加失败" + " " + "status: " + res.data.code);
                    } else {
                        this.$message.error(res.data.msg)
                    }
                    this.dialogFormVisible = false;
                    this.getAll();
                }).finally(() => {
                    this.getAll();
                });
            },
            //弹出编辑窗口
            handleUpdate(row) {
                axios.get("/users/" + 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() {
                axios.put("/users", this.formData).then((res) => {
                    if (res.data.code === 20021) {
                        this.dialogFormVisible4Edit = false;
                        this.$message.success("修改成功" + "  " + "status: " + res.data.code);
                    } else if (res.data.code === 20020) {
                        this.$message.error("修改失败" + "  " + "status: " + res.data.code);
                    } else {
                        this.$message.error(res.data.msg)
                    }
                    this.dialogFormVisible4Edit = false;
                    this.getAll();
                }).finally(() => {
                    this.getAll();
                });
            },
            // 删除
            handleDelete(row) {
                this.$confirm("此操作永久删除当前数据,是否继续?", "警告", {type: 'info'}).then(() => {
                    axios.delete("/users/" + row.id).then((res) => {
                        if (res.data.code === 20031) {
                            this.$message.success("删除成功" + "  " + "status: " + res.data.code);
                        } else if (res.data.code === 20030) {
                            this.$message.error("删除失败" + "  " + "status: " + res.data.code);
                        } else {
                            this.$message.error(res.data.msg);
                        }
                    }).finally(() => {
                        this.getAll();
                    });
                }).catch(() => {
                    this.$message.info("取消删除");
                })
            }
        }

拦截器

springmvc独有的拦截器
和之前的转发一样的写法

先在springmvcConfig里面写好能扫描到拦截器的路径
这是SpringMvcSupportConfig

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {

    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //配置拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
    }
}

然后开始新建拦截器
拦截器

@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String contentType = request.getHeader("Content-Type");
        HandlerMethod hm = (HandlerMethod)handler;
        System.out.println("preHandle..."+contentType);
        return true;
    }

    @Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    @Override
    //原始方法调用完成后执行的内容
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

其实在springmvcconfig里面也可以直接继承WebMvcConfigurer
在这里插入图片描述

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Autowired
    private ProjectInterceptor2 projectInterceptor2;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值