springboot整合案例SSMP

SSMP整合案例

实现方案分析

实体类开发————使用Lombok快速制作实体类
Dao开发————整合MyBatisPlus,制作数据层测试类
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
列表、新增、修改、删除、分页、查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能

创建模块:
1.勾选SpringMVC与MySQL坐标
2. 修改配置文件为yml格式
3. 设置端口为80方便访问

实体类开发:

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发,lombok版本由SpringBoot提供,无需指定版本

<dependency> 
    <groupId>org.projectlombok</groupId> 
    <artifactId>lombok</artifactId>
</dependency>

Book.java:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {

    private Integer id;
    private String type;
    private String name;
    private String description;

}

Dao开发
导入MyBatisPlus与Druid对应的starter

<dependency> 
    <groupId>com.baomidou</groupId> 
    <artifactId>mybatis-plus-boot-starter</artifactId> 
    <version>3.4.3</version>
</dependency> 

<dependency> 
    <groupId>com.alibaba</groupId> 
    <artifactId>druid-spring-boot-starter</artifactId> 
    <version>1.2.6</version>
</dependency>

配置数据源和mybatisplus相关的配置

#配置数据源
spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
      username: root
      password: root

#配置MyBatisPlus对应的基础配置,id生成策略使用数据库自增策略
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_
      id-type: auto

bookDao(类上有@Mapper注解,继承BaseMapper):

@Mapper
public interface BookDao extends BaseMapper<Book> {

}

写测试类去测试结果:

@SpringBootTest
class ApplicationTests {

    @Autowired
    private BookDao bookDao;

    @Test
    void BookDaoTest() {
        Book book = new Book();
        book.setId(1);
        book.setName("java");
        book.setType("语言类");
        book.setDescription("java语言");

        int result = bookDao.insert(book);
        System.out.println(result);

        Book book1 = bookDao.selectById(1);
        System.out.println(book1);
    }

}

控制台输出(数据库插入成功):
1
Book(id=1, type=语言类, name=java, description=java语言)

为了方便调试可以开启MyBatisPlus的日志

mybatis-plus:
    configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

分页功能:分页操作需要设定分页对象IPage

@Test
void GetPageTest(){
    IPage<Book> page = new Page(1,5);//获取第一页数据,一页5条
    bookDao.selectPage(page,null);

}

分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();

        //2.添加具体的拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }

}

根据条件查询—
使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用。

@Test
void SelectConditionTest(){
    IPage page = new Page(1,5);
    LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
    wrapper.ge(Book::getId,"3");

    bookDao.selectPage(page,wrapper);
}

业务层开发(建议用快速开发的方法)

Service层接口定义与数据层接口定义具有较大区别,不要混用。
selectByUserNameAndPassword(String username,String password);
login(String username,String password);
接口定义:

public interface BookService {
    boolean save(Book book);
    boolean delete(Integer id);
    boolean update(Book book);
    Book getById(Integer id);
    List<Book> getAll();
    IPage<Book> getByPage(int currentPage,int pageSize);
}

实现类定义(@Service注解):

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public boolean save(Book book) {
        return bookDao.insert(book) > 0;
    }

    @Override
    public boolean delete(Integer id) {
        return bookDao.deleteById(id) > 0;
    }

    @Override
    public boolean update(Book book) {
        return bookDao.updateById(book) > 0;
    }

    @Override
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }

    @Override
    public List<Book> getAll() {
        return bookDao.selectList(null);
    }

    @Override
    public IPage<Book> getByPage(int currentPage, int pageSize) {
        IPage<Book> iPage = new Page<Book>(currentPage,pageSize);
        return bookDao.selectPage(iPage,null);
    }
}

测试类:

@Autowired
private BookService bookService;

@Test
void testSave(){
    Book book = new Book();
    book.setName("微信");
    book.setType("工具类");
    book.setDescription("用好微信");

    bookService.save(book);

}

@Test
void testDelete(){
    bookService.delete(9);
}

@Test
void testUpdate(){
    Book book = new Book();
    book.setId(13);
    book.setName("微信");
    book.setDescription("使用好微信");

    bookService.update(book);
}

@Test
void testGetById(){
    bookService.getById(10);
}

@Test
void testGetAll(){
    bookService.getAll();
}

@Test
void testGetByPage(){
    bookService.getByPage(1,5);
}

业务层开发----快速开发

快速开发方案
使用MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现(ServiceImpl<M,T>)。
在通用类基础上做功能重载或功能追加,注意重载时不要覆盖原始操作,避免原始提供的功能丢失。

接口定义:

public interface IBookService extends IService<Book> {

    //通用方法IService已经提供了,在这里你可以写自己的业务方法
//    Book get(Integer id);
    
    IPage<Book> getPage(int currentPage, int pageSize);
}

实现类定义:

@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao, Book> implements IBookService {
//继承ServiceImpl,第一个参数是对应的Dao类,第二个参数是对应的实体类

//delete方法变成remove方法,getAll方法变成list方法,getByPage方法变成page方法

    //自己定义的方法还是要实现
//    @Autowired
//    private BookDao bookDao;
//    @Override
//    public Book get(Integer id) {
//        return bookDao.selectById(id);
//    }
    @Autowired
    private BookDao bookDao;
    
    @Override
    public IPage<Book> getPage(int currentPage, int pageSize) {
        IPage page = new Page(currentPage,pageSize);
        return bookDao.selectPage(page,null);
    }
}

测试类定义:

@Autowired
private IBookService bookService2;

//service层快速开发的测试
@Test
void testGetById2(){
    System.out.println(bookService2.getById(4));
}

@Test
void testSave2(){
    Book book = new Book();
    book.setType("测试数据123");
    book.setName("测试数据123");
    book.setDescription("测试数据123");
    bookService2.save(book);
}

@Test
void testUpdate2(){
    Book book = new Book();
    book.setId(14);
    book.setType("-----------------");
    book.setName("测试数据123");
    book.setDescription("测试数据123");
    bookService2.updateById(book);
}

@Test
void testDelete2(){
    bookService2.removeById(13);
}

@Test
void testGetAll2(){
    bookService2.list();
}

@Test
void testGetPage2(){
    IPage<Book> page = new Page<Book>(2,5);
    bookService2.page(page);
    System.out.println(page.getCurrent());
    System.out.println(page.getSize());
    System.out.println(page.getTotal());
    System.out.println(page.getPages());
    System.out.println(page.getRecords());
}

表现层开发

基于Restful进行表现层接口开发;使用Postman测试表现层接口功能。

基于Restful制作表现层接口
新增:POST 实体数据:@RequestBody
删除:DELETE 路径变量:@PathVariable
修改:PUT
查询:GET

BookController:

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private IBookService bookService;

    @GetMapping
    public List<Book> getAll(){
        return bookService.list();
    }

    @GetMapping("{id}")
    public Book getById(@PathVariable Integer id){
        return bookService.getById(id);
    }

    @PostMapping
    public boolean save(@RequestBody Book book){

        return bookService.save(book);
    }

    @PutMapping
    public boolean update(@RequestBody Book book){//用@RequestBody用于接收json格式的数据
        return  bookService.updateById(book);
    }

    @DeleteMapping("{id}")
    public boolean delete(@PathVariable Integer id){
        return bookService.removeById(id);
    }
}

用postman去输入url地址来测试,
GET:localhost:80/books 得到所有的数据
GET:localhost:80/books/15 得到id=15的数据
POST:localhost:80/books Body->raw->JSON 输入新增数据的JSON
PUT:localhost:80/books Body->raw->JSON 输入修改数据的JSON
DELETE:localhost:80/books/15 删除id=15的数据

表现层数据一致性处理
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
新增一个模型类R:

@Data
public class R {
    private Boolean flag;
    private Object data;

    public R(){}

    public R(Boolean flag){
        this.flag=flag;
    }

    public R(Boolean flag,Object data){
        this.flag = flag;
        this.data = data;
    }
}

表现层接口统一返回值类型结果

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private IBookService bookService;

    @GetMapping
    public R getAll(){
//        R r = new R();
//        List<Book> list = bookService.list();
//        if (list!=null){
//            r.setFlag(true);
//            r.setData(list);
//        }else{
//            r.setFlag(false);
//            r.setData(list);
//        }


        return new R(true,bookService.list());
    }

    @GetMapping("{id}")
    public R getById(@PathVariable Integer id){
//        R r = new R();
//        Book book = bookService.getById(id);
//        r.setData(book);
//        if(book!=null){
//            r.setFlag(true);
//        }else{
//            r.setFlag(false);
//        }

        return new R(true,bookService.getById(id));
    }

    @PostMapping
    public R save(@RequestBody Book book){
//        R r = new R();
//        boolean b = bookService.save(book);
//        r.setFlag(b);

        return new R(bookService.save(book));
    }

    @PutMapping
    public R update(@RequestBody Book book){//用@RequestBody用于接收json格式的数据
//        R r = new R();
//        boolean b = bookService.updateById(book);
//        r.setFlag(b);

        return  new R(bookService.updateById(book));
    }

    @DeleteMapping("{id}")
    public R delete(@PathVariable Integer id){
//        R r = new R();
//        boolean b = bookService.removeById(id);
//        r.setFlag(b);

        return new R(bookService.removeById(id));
    }
}

运行结果:CRUD的结果都是JSON格式,flag是操作成功与否,data是数据。

前后端协议联调
前后端分离结构设计中页面归属前端服务器。
单体工程中页面放置在resources目录下的static目录中(建议执行clean)。

前端发送异步请求,调用后端接口(在这之前先用钩子函数created去调用查询所有的功能进行列表展示)

//钩子函数,VUE对象初始化完成后自动执行
created() {
    //调用查询全部数据的操作
    this.getAll();
},


//列表
getAll() {
    //发送异步请求
    axios.get("/books").then((res)=>{
        //console.log(res.data);
        this.dataList = res.data.data;
    });
},

总结:
1.单体项目中页面放置在resources/static目录下
2. created钩子函数用于初始化页面时发起调用
3. 页面使用axios发送异步请求获取数据后确认前后端是否联通

弹出添加窗口

//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
    //这一步是因为添加过一次数据后,再次添加要把之前的数据清空
    this.resetForm();
},

//重置表单
resetForm() {
    this.formData={};
},

//添加
handleAdd () {
    axios.post("/books",this.formData).then((res)=>{
        //判断当前操作是否成功
        if(res.data.flag == true){
            //1.关闭弹层
            this.dialogFormVisible = false;
            this.$message.success("添加成功");
        }else{
            this.$message.error("添加失败");
        }


    }).finally(()=>{
        //2.重新加载数据
        this.getAll();
    });
},

//取消
cancel(){
    this.dialogFormVisible = false;
    this.$message.info("当前操作取消");
},

总结:

  1. 请求方式使用POST调用后台对应操作
  2. 添加操作结束后动态刷新页面加载数据
  3. 根据操作结果不同,显示对应的提示信息
  4. 弹出添加Div时清除表单数据

删除操作

// 删除
handleDelete(row) {
    //1.弹出提示框
    this.$confirm("是否确定删除该记录","提示",{
        type:'info'//这个“是否确定删除该记录”是灰色的
    }).then(()=>{
        //2.执行删除业务
        axios.delete("/books/"+row.id).then((res=>{
            if(res.data.flag){
                this.$message.success("删除成功");
            }else {
                this.$message.error("删除失败");
            }
        })).finally(()=>{
            this.getAll();
        });
    }).catch(()=>{
        //3.取消操作
        this.$message.info("取消删除操作");
    });

},

总结:

  1. 请求方式使用Delete调用后台对应操作
  2. 删除操作需要传递当前行数据对应的id值到后台
  3. 删除操作结束后动态刷新页面加载数据
  4. 根据操作结果不同,显示对应的提示信息
  5. 删除操作前弹出提示框避免误操作

弹出修改窗口

//弹出编辑窗口
handleUpdate(row) {
    axios.get("/books/"+row.id).then((res)=>{
        if(res.data.flag){
            //展示弹层,加载数据
            this.formData = res.data.data;
            //打开编辑表单窗口
            this.dialogFormVisible4Edit=true;
        }else {
            this.$message.error("数据同步失败,自动刷新");
        }

    });

},

//修改
handleEdit() {
    this.$confirm("是否确定修改?","提示",{
        type:"info"
    }).then(()=>{
        axios.put("/books/",this.formData).then((res)=>{
            if ((res.data.flag)){
                //关闭编辑表单窗口
                this.dialogFormVisible4Edit=false;
                this.$message.success("编辑成功");
            }else {
                this.$message.error("编辑失败");
            }
        }).finally(()=>{
            this.getAll();
        });

    }).catch(()=>{
        this.dialogFormVisible4Edit = false;
       this.$message.info("取消编辑操作");
    });

},

总结:

  1. 请求方式使用PUT调用后台对应操作
    2. 修改操作结束后动态刷新页面加载数据(同新增)
    3. 根据操作结果不同,显示对应的提示信息(同新增)

异常消息处理

对异常进行统一处理,出现异常后,返回指定信息
/作为springMVC的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {

    //拦截所有的异常信息
    @ExceptionHandler
    public R doException(Exception ex){

        //记录日志
        //通知运维
        //通知开发
        ex.printStackTrace();
        return new R("服务器故障,请稍后再试!");

    }
}

修改模型类R,添加出现异常后要显示的信息

@Data
public class R {//这个类是为了保证前后端数据格式一直
    private Boolean flag;
    private Object data;

    private String msg;
    public R(){}

    public R(Boolean flag){
        this.flag=flag;
    }

    public R(Boolean flag,Object data){
        this.flag = flag;
        this.data = data;
    }
    public R(Boolean flag,String msg){
        this.flag = flag;
        this.msg = msg;
    }

    public R(String msg){
        this.flag = false;
        this.msg = msg;
    }
}

控制层修改方法

@PostMapping
    public R save(@RequestBody Book book){
//        R r = new R();
//        boolean b = bookService.save(book);
//        r.setFlag(b);
        boolean flag = bookService.save(book);
        return new R(flag , flag ? "添加成功" : "添加失败");
    }

前端添加的函数

//添加
handleAdd () {
    axios.post("/books",this.formData).then((res)=>{
        //判断当前操作是否成功
        if(res.data.flag == true){
            //1.关闭弹层
            this.dialogFormVisible = false;
            this.$message.success(res.data.msg);
        }else{

            this.$message.error(res.data.msg);
        }


    }).finally(()=>{
        //2.重新加载数据
        this.getAll();
    });
},

总结:
1.使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常的
2. 异常处理器必须被扫描加载,否则无法生效
3. 表现层返回结果的模型类中添加消息属性用来传递消息到页面

分页功能
页面使用el分页组件添加分页功能。
前端books.html:

<!--分页组件-->
<div class="pagination-container">

    <el-pagination
            class="pagiantion"

            @current-change="handleCurrentChange"

            :current-page="pagination.currentPage"

            :page-size="pagination.pageSize"

            layout="total, prev, pager, next, jumper"

            :total="pagination.total">

    </el-pagination>

</div>


data:{
    dataList: [],//当前页要展示的列表数据
    dialogFormVisible: false,//添加表单是否可见
    dialogFormVisible4Edit:false,//编辑表单是否可见
    formData: {},//表单数据
    rules: {//校验规则
        type: [{ required: true, message: '图书类别为必填项', trigger: 'blur' }],
        name: [{ required: true, message: '图书名称为必填项', trigger: 'blur' }]
    },
    pagination: {//分页相关模型数据
        currentPage: 1,//当前页码
        pageSize:10,//每页显示的记录数
        total:0//总记录数
    }
},

加载分页数据

//分页查询
getAll() {
    //发送异步请求
    axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res)=>{
        //console.log(res.data);
        // this.dataList = res.data.data;
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pageSize = res.data.data.size;
        this.pagination.total = res.data.data.total;

        this.dataList = res.data.data.records;
    });
},

//切换页码
handleCurrentChange(currentPage) {
    this.pagination.currentPage = currentPage;
    this.getAll();
},

表现层getAll():

@GetMapping("/{currentPage}/{pageSize}")
    public R getAll(@PathVariable int currentPage,@PathVariable int pageSize){
//        R r = new R();
//        List<Book> list = bookService.list();
//        if (list!=null){
//            r.setFlag(true);
//            r.setData(list);
//        }else{
//            r.setFlag(false);
//            r.setData(list);
//        }
        IPage<Book> page = bookService.getPage(currentPage, pageSize);
        return new R(null != page, page);
    }

总结:

  1. 使用el分页组件
  2. 定义分页组件绑定的数据模型
  3. 异步调用获取分页数据
  4. 分页数据页面回显

删除功能维护(如果一页10条数据,删除第11条数据,当前页码是第二页,但是最大页码是1页)

对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询

@GetMapping("/{currentPage}/{pageSize}")
    public R getAll(@PathVariable int currentPage,@PathVariable int pageSize){

        IPage<Book> page = bookService.getPage(currentPage, pageSize);
        //如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
        if(currentPage > page.getPages()){
            page = bookService.getPage((int) page.getPages(),pageSize);
        }
        return new R(null != page, page);
    }
  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值