SSMP整合案例
根据黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战实现
1.模块创建
-
创建SpringBoot项目 -
勾选Spring-MVC和MySQL坐标 -
在pom.xml中加入Druid,MybatisPlus,Lombok的坐标
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
-
修改配置文件application.properties为yml格式 -
在配置文件中设置tomcat端口为80方便访问 server:
port: 80
-
模块初始结构如下
2. 实体类开发
构建数据库环境
CREATE DATABASE ssm;
CREATE TABLE book(
id int PRIMARY KEY AUTO_INCREMENT,
type varchar(20),
name varchar(20) NOT NULL ,
description varchar(20)
)ENGINE =InnoDB DEFAULT CHARSET =utf8;
创建实体类
@Data
public class Book implements Serializable {
@TableId("id")
private Integer id;
private String type;
private String name;
private String description;
}
3. 持久层开发
配置数据源与MybatisPlus的id生成策略
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
username: root
password: root
output:
ansi:
enabled: always
application:
name: demo-application
mybatis-plus:
global-config:
db-config:
id-type: auto
配置日志便于调试
mybatis-plus:
global-config:
db-config:
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Mapper接口开发
@Mapper
public interface BookMapper extends BaseMapper<Book> {
}
测试Mapper接口及数据库连接
@SpringBootTest
public class BookMapperTest {
@Autowired
private BookMapper bookMapper;
@Test
void testBookMapper(){
Book book=new Book();
book.setName("小王子");
book.setType("童话");
book.setDescription("启蒙故事,充满道理");
int result = bookMapper.insert(book);
System.out.println(result);
}
}
测试结果
18:27:25 [main] INFO --- ltd.lccyj.mapper.BookMapperTest | Started BookMapperTest in 3.344 seconds (JVM running for 4.614)
18:27:25 [main] DEBUG --- ltd.lccyj.mapper.BookMapper.insert | ==> Preparing: INSERT INTO book ( type, name, description ) VALUES ( ?, ?, ? )
18:27:25 [main] DEBUG --- ltd.lccyj.mapper.BookMapper.insert | ==> Parameters: 童话(String), 小王子(String), 启蒙故事,充满道理(String)
18:27:25 [main] DEBUG --- ltd.lccyj.mapper.BookMapper.insert | <== Updates: 1
配置分页拦截器
4. 业务层开发
- MP提供了通用业务层接口
IService<T> 与业务层通用实现类ServieceImpl<M,T> - 在通用类基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
业务层接口开发
public interface BookService extends IService<Book> {
}
业务层实现类开发
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
}
5. 表现层开发
- 基于Restful进行表现层接口开发
- 使用Postman测试表现层接口功能
BookController
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
}
getAll
查询所有书籍
@GetMapping
public List<Book> getAll(){
return bookService.list();
}
测试接口
getById
根据id查询书籍
@GetMapping("/{id}")
public Book getById(@PathVariable Integer id){
return bookService.getById(id);
}
测试接口
save
添加书籍
@PostMapping
public boolean save(@RequestBody Book book){
return bookService.save(book);
}
测试接口
==> Preparing: INSERT INTO book ( type, name, description ) VALUES ( ?, ?, ? )
==> Parameters: 世界名著(String), 呼啸山庄(String), 经典名著(String)
<== Updates: 1
update
修改书籍
@PutMapping
public boolean update(@RequestBody Book book){
return bookService.updateById(book);
}
测试接口
==> Preparing: UPDATE book SET type=?, name=?, description=? WHERE id=?
==> Parameters: 世界名著(String), 呼啸山庄(String), 经典世界名著(String), 8(Integer)
<== Updates: 1
delete
根据id删除书籍
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id){
return bookService.removeById(id);
}
测试接口
==> Preparing: DELETE FROM book WHERE id=?
==> Parameters: 8(Integer)
<== Updates: 1
getByPage
分页查询书籍
-
BookService接口 IPage<Book> getByPage(int currentPage,int pageSize);
-
BookServiceImpl
@Override
public IPage<Book> getByPage(int currentPage, int pageSize) {
IPage<Book> page=new Page<>(currentPage,pageSize);
bookMapper.selectPage(page,null);
return page;
}
-
BookController
@GetMapping("/{currentPage}/{pageSize}")
public IPage<Book> getByPage(@PathVariable int currentPage,@PathVariable int pageSize){
return bookService.getByPage(currentPage, pageSize);
}
接口测试
6. 表现层消息一致化
设计表现层返回数据的模型类,也称为前后端数据协议
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
boolean flag;
Object data;
}
优化Controller实现数据统一
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public Result getAll() {
return new Result(true, bookService.list());
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
return new Result(true, bookService.getById(id));
}
@GetMapping("/{currentPage}/{pageSize}")
public Result getByPage(@PathVariable int currentPage, @PathVariable int pageSize) {
return new Result(true, bookService.getByPage(currentPage, pageSize));
}
@PostMapping
public Result save(@RequestBody Book book) {
return new Result(bookService.save(book), null);
}
@PutMapping
public Result update(@RequestBody Book book) {
return new Result(bookService.updateById(book), null);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
return new Result(bookService.removeById(id), null);
}
}
7. 前端环境准备
单体工程中页面放在resources目录下的static目录中
8. 前后端协议联调
列表展示功能
created() {
this.getAll();
},
methods: {
getAll() {
axios.get("/books").then((res)=>{
this.dataList=res.data.data;
})
},
添加功能
handleCreate() {
this.dialogFormVisible=true;
this.resetForm();
},
resetForm() {
this.formData={};
},
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("操作取消");
},
修改功能
handleUpdate(row) {
axios.get("/books/"+row.id).then((res)=>{
if (res.data.flag && res.data.data!=null){
this.formData=res.data.data;
this.dialogFormVisible4Edit=true;
}else {
this.$message.error("数据同步失败,自动刷新")
}
}).finally(()=>{
this.getAll();
})
},
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()
})
},
9. 业务消息一致性处理
1. 异常统一处理
当出现异常时,后台返回数据将会再次不一致,可定义SpringMVC异常处理器对异常统一处理
-
优化前后端数据交互协议
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private boolean flag;
private Object data;
private String message;
}
-
添加SpringMVC的异常处理器
@RestControllerAdvice
public class ProjectException {
@ExceptionHandler
public Result doException(Exception ex){
ex.printStackTrace();
return new Result(false,null,"服务器出现故障,请稍后再试");
}
}
2. 表现层统一处理消息
前台不处理提示消息,全部交由后端Controller统一处理
-
优化Controller
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public Result getAll() {
return new Result(true, bookService.list(),null);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
return new Result(true, bookService.getById(id),null);
}
@GetMapping("/{currentPage}/{pageSize}")
public Result getByPage(@PathVariable int currentPage, @PathVariable int pageSize) {
return new Result(true, bookService.getByPage(currentPage, pageSize),null);
}
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag, null,flag?"添加成功":"添加失败");
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.updateById(book);
return new Result(flag, null,flag?"修改成功":"修改失败");
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.removeById(id);
return new Result(flag, null,flag?"删除成功":"删除失败");
}
}
-
优化前端代码
created() {
this.getAll();
},
methods: {
getAll() {
axios.get("/books").then((res) => {
this.dataList = res.data.data;
})
},
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
resetForm() {
this.formData = {};
},
handleAdd() {
axios.post("/books", this.formData).then((res) => {
if (res.data.flag) {
this.dialogFormVisible = false;
this.$message.success(res.data.message)
} else {
this.$message.error(res.data.message)
}
}).finally(() => {
this.getAll();
})
},
cancel() {
this.dialogFormVisible4Edit = false;
this.dialogFormVisible = false;
this.$message.info("操作取消");
},
handleDelete(row) {
this.$confirm("此操作将永久删除数据,是否继续?", "提示", {type: 'info'}).then(() => {
axios.delete("/books/" + row.id).then((res) => {
if (res.data.flag) {
this.$message.success(res.data.message)
} else {
this.$message.error(res.data.message)
}
}).finally(() => {
this.getAll();
})
}).catch(() => {
this.$message.info("取消删除")
});
},
handleUpdate(row) {
axios.get("/books/" + row.id).then((res) => {
if (res.data.flag && res.data.data != null) {
this.formData = res.data.data;
this.dialogFormVisible4Edit = true;
} else {
this.$message.error("数据同步失败")
}
}).finally(() => {
this.getAll();
})
},
handleEdit() {
axios.put("/books", this.formData).then((res) => {
if (res.data.flag) {
this.dialogFormVisible4Edit = false;
this.$message.success(res.data.message)
} else {
this.$message.error(res.data.message)
}
}).finally(() => {
this.getAll()
})
},
10. 分页功能实现
getAll() {
axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res) => {
this.pagination.total=res.data.data.total;
this.pagination.currentPage=res.data.data.current;
this.pagination.pageSize=res.data.data.size;
this.dataList = res.data.data.records;
})
},
handleCurrentChange(currentPage) {
this.pagination.currentPage=currentPage;
this.getAll();
},
删除功能异常解决
上述分页操作会出现一个问题,当我们处于最后一页,且这一页只有一条数据时,我们将其删
除,但当前页码仍然为之前的页码,这会导致我们停留在一个空页,此时可以修改Controller代
码解决这个问题
@GetMapping("/{currentPage}/{pageSize}")
public Result getByPage(@PathVariable int currentPage, @PathVariable int pageSize) {
IPage<Book> page = bookService.getByPage(currentPage, pageSize);
while (page.getCurrent()>page.getPages()){
page = bookService.getByPage((int)page.getPages(), pageSize);
}
return new Result(true,page,null);
}
11.条件查询功能
-
前端绑定数据到分页数据模型 -
修改getAll方法
getAll() {
let params="?type="+this.pagination.type;
params+="&name="+this.pagination.name;
params+="&description="+this.pagination.description;
console.log(params);
axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+params).then((res) => {
this.pagination.total=res.data.data.total;
this.pagination.currentPage=res.data.data.current;
this.pagination.pageSize=res.data.data.size;
this.dataList = res.data.data.records;
})
},
-
Service接口及实现
IPage<Book> getByPage(int currentPage, int pageSize,Book book);
@Override
public IPage<Book> getByPage(int currentPage, int pageSize, Book book) {
IPage<Book> page = new Page<>(currentPage, pageSize);
LambdaQueryWrapper<Book> wrapper=new LambdaQueryWrapper<>();
wrapper.like(Strings.isNotEmpty(book.getType()),Book::getType,book.getType());
wrapper.like(Strings.isNotEmpty(book.getName()),Book::getName,book.getName());
wrapper.like(Strings.isNotEmpty(book.getDescription()),Book::getDescription,book.getDescription());
bookMapper.selectPage(page,wrapper);
return page;
}
-
Controller
@GetMapping("/{currentPage}/{pageSize}")
public Result getByPage(@PathVariable int currentPage, @PathVariable int pageSize, Book book) {
IPage<Book> page = bookService.getByPage(currentPage, pageSize,book);
while (page.getCurrent()>page.getPages()){
page = bookService.getByPage((int)page.getPages(), pageSize);
}
return new Result(true,page,null);
}
|