SpringBoot SSMP 整合案例分享
前言:
1 搭建SpringBoot应用
勾选 SpringMVC 与 MySQL 坐标
修改配置文件为yml格式
设置端口为80方便访问(可选)
2 实体类开发
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发
lombok版本由SpringBoot提供,无需指定版本。
常用注解:@Data
为当前实体类在编译期设置对应的 get/set 方法,toString方法,hashCode方法,equals方法等
@Data
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
3 数据层(dao层)开发
技术实现方案:
MyBatisPlus
Druid
(1)导入 MyBatisPlus 与 Druid 对应的 starter
(2)配置数据源与 MyBatisPlus 对应的基础配置(id 生成策略使用数据库自增策略)
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?servierTimezone=UTC
username: root
password: root
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto
(3)继承 BaseMapper 并指定泛型
@Mapper
public interface BookDao extends BaseMapper<Book> {
}
(4)制作测试类测试结果
@SpringBootTest
public class BookDaoTestCase {
@Autowired
private BookDao bookDao;
@Test
void testSave(){
Book book = new Book();
book.setName("测试数据");
book.setType("测试类型");
bookDao.insert(book);
}
@Test
void testGetById() {
System.out.println(bookDao.selectById(1));
}
}
(5)为方便调试可以开启 MyBatisPlus 的日志(使用配置方式开启日志,设置日志输出方式为标准输出)
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4 数据层开发分页功能
分页操作需要设定分页对象 IPage,IPage 对象中封装了分页操作中的所有数据(数据、当前页码值、每页数据总量、最大页码值、数据总量)。
分页操作是在 MyBatisPlus 的常规操作基础上增强得到,内部是动态的拼写 SQL 语句,使用 MyBatisPlus 拦截器实现。
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// MyBatisPlus拦截器
@Configuration
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//1.定义Mp拦截
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//2.添加具体的拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
5 数据层开发☞条件查询功能 QueryWrapper
使用 QueryWrapper 对象封装查询条件,推荐使用 LambdaQueryWrapper 对象,将所有查询操作封装成方法调用。
// 条件查询功能
@Test
void testGetByCondition(){
IPage page = new Page(1,10);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Book::getName,"Spring");
bookDao.selectPage(page,lqw);
}
@Test
void testGetByCondition2(){
QueryWrapper<Book> qw = new QueryWrapper<Book>();
qw.like("name","Spring");
bookDao.selectList(qw);
}
支持动态拼写查询条件 Strings.isNotEmpty(name)
@Test
void testGetByCondition(){
String name = "Spring";
IPage page = new Page(1,10);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(name),Book::getName,"Spring");
bookDao.selectPage(page,lqw);
}
6 业务层(Service层)开发
# Service层接口定义与数据层接口定义具有较大区别,不要混用
// 业务层关注的是业务操作
login(String username,String password);
// 数据层关注的是数据库操作
selectByUserNameAndPassword(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
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public Boolean save(Book book) {
return bookDao.insert(book) > 0;
}
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
public Boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
public Book getById(Integer id) {
return bookDao.selectById(id);
}
public List<Book> getAll() {
return bookDao.selectList(null);
}
public IPage<Book> getByPage(int currentPage, int pageSize) {
IPage page = new Page<Book>(currentPage,pageSize);
return bookDao.selectPage(page,null);
}
}
7 业务层开发——快速开发☞使用ISerivce和ServiceImpl
- 快速开发方案
- 使用MyBatisPlus提供的业务层通用接口(ISerivce)与业务层通用实现类(
ServiceImpl
)- 在通用类基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
接口:
public interface BookService extends IService<Book> {
// 追加的操作与原始操作通过名称区分,功能类似
boolean saveBook(Book book);
boolean modify(Book book);
boolean delete(Integer id);
IPage<Book> getPage(int currentPage, int pageSize);
IPage<Book> getPage(int currentPage, int pageSize, Book book);
}
实现类:
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean saveBook(Book book) {
return bookDao.insert(book) > 0;
}
@Override
public boolean modify(Book book) {
return bookDao.updateById(book) > 0;
}
@Override
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, null);
return page;
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize, Book book) {
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
lqw.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
lqw.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, lqw);
return page;
}
}
8 基于 Restful 进行表现层开发
基于Restful制作表现层接口
新增:POST
删除:DELETE
修改:PUT
查询:GET
接收参数
实体数据:@RequestBody
路径变量:@PathVariable
// 功能测试
@GetMapping("/{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage, @PathVariable int pageSize, Book book) {
IPage<Book> page = bookService.getPage(currentPage, pageSize, book);
// 如果当前页码大于了总页码,那么将最大页码值作为当前页码,重新执行查询操作
// 源码中 long pages = this.getTotal() / this.getSize();
if (currentPage > page.getPages()) {
page = bookService.getPage((int) page.getPages(), pageSize, book);
}
return new R(true, page);
}
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议:
10 前后端协议联调
前后端分离结构设计中页面归属前端服务器
单体工程中页面放置在 resources / static 目录下(建议执行clean)
前端发送异步请求,调用后端接口
created钩子函数用于初始化页面时发起调用
页面使用 axios 发送异步请求获取数据后确认前后端是否联通
//列表
getAll() {
axios.get("/books").then((res)=>{
console.log(res.data);
});
},
查询
将查询数据返回到页面,利用前端数据双向绑定进行数据展示:
//列表
getAll() {
axios.get("/books").then((res)=>{
this.dataList = res.data.data;
});
},
添加
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
},
//清除数据,重置表单
resetForm() {
this.formData = {};
},
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
//添加
handleAdd () {
//发送异步请求
axios.post("/books",this.formData).then((res)=>{
//如果操作成功,关闭弹层,显示数据
if(res.data.flag){
this.dialogFormVisible = false;
this.$message.success("添加成功");
}else {
this.$message.error("添加失败");
}
}).finally(()=>{
this.getAll();
});
},
//取消添加
cancel(){
this.dialogFormVisible = false;
this.$message.info("操作取消");
},
删除
// 删除
handleDelete(row) {
axios.delete("/books/"+row.id).then((res)=>{
if(res.data.flag){
this.$message.success("删除成功");
}else{
this.$message.error("删除失败");
}
}).finally(()=>{
this.getAll();
});
}
// 删除
handleDelete(row) {
//1.弹出提示框
this.$confirm("此操作永久删除当前数据,是否继续?","提示",{
type:'info'
}).then(()=>{
//2.做删除业务
axios.delete("/books/"+row.id).then((res)=>{
……
}).finally(()=>{
this.getAll();
});
}).catch(()=>{
//3.取消删除
this.$message.info("取消删除操作");
});
}
//弹出编辑窗口
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("数据同步失败,自动刷新"); }
});
},
//删除
handleDelete(row) {
axios.delete("/books/"+row.id).then((res)=>{
if(res.data.flag){
this.$message.success("删除成功");
}else{
this.$message.error("数据同步失败,自动刷新");
}
}).finally(()=>{
this.getAll();
});
}
1.加载要修改数据通过传递当前行数据对应的id值到后台查询数据 2.利用前端数据双向绑定将查询到的数据进行回显
修改
//修改
handleEdit() {
axios.put("/books",this.formData).then((res)=>{
//如果操作成功,关闭弹层并刷新页面
if(res.data.flag){
this.dialogFormVisible4Edit = false;
this.$message.success("修改成功");
}else {
this.$message.error("修改失败,请重试");
}
}).finally(()=>{
this.getAll();
});
},
// 取消添加和修改
cancel(){
this.dialogFormVisible = false;
this.dialogFormVisible4Edit = false;
this.$message.info("操作取消");
},
对异常进行统一处理,出现异常后,返回指定信息:
使用注解 @RestControllerAdvice 定义 SpringMVC 异常处理器用来处理异常的
异常处理器必须被扫描加载,否则无法生效
表现层返回结果的模型类中添加消息属性用来传递消息到页面
// 作为springmvc的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
// 拦截所有的异常信息
@ExceptionHandler(Exception.class)
public R doException(Exception e){
// 记录日志
// 通知运维
// 通知开发
e.printStackTrace();
return new R("服务器故障,请稍后重试哈!");
}
}
12 分页功能
页面使用 el 分页组件添加分页功能:
定义分页组件需要使用的数据并将数据绑定到分页组件
替换查询全部功能为分页功能
加载分页数据
分页页码值切换
使用el分页组件:
定义分页组件绑定的数据模型
异步调用获取分页数据
分页数据页面回显