环境搭建:配置起步依赖pom.xml和配置文件application.yml
1.创建模块时,勾选的依赖有springMVC和MySQL驱动
2.手动添加的依赖有:MyBatis-plus、Druid、lombok
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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.17</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
3.设置数据源、端口、框架技术相关配置等
将数据库连接和数据源连接池配置在一起,注意密码是0开头的话要加""。
配置mybatis-plus的表前缀(取决于你自己的表名)、配置自增的方式为表中默认自增,设置为auto(mybatis-plus默认的是雪花算法)、再配置mybatis-plus的日志。
server:
port: 80
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: "0630"
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
一、实体类快速开发(lombok)(pojo或domain)
一个标准的bean应该有成员变量、构造器、get/set、toString等方法。
使用lombok组件,使用@Data注解直接标注一个bean为实体类。(该注解没有构造器方法,需要自己添加)
@Data
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
二、数据访问层(Dao或Mapper)
数据访问层只管理数据库的连接和操作。数据访问层和持久层有撒子区别,俺也不知道,有木有小伙伴回答一下。。。
1.Dao层
Dao层直接使用mybatis-plus提供的方法。Mapper接口继承BaseMapper<实体类>、并注解@Mapper
@Mapper
public interface BookDao extends BaseMapper<Book> {
}
2.对mybatis-plus提供的访问数据库的方法进行测试
@SpringBootTest
public class BookDaoTestCase {
@Autowired
private BookDao bookDao;
/**
* 根据id查询
*/
@Test
void testGetById() {
Book book = bookDao.selectById(1);
System.out.println(book);
}
/**
* 增加一条数据
*/
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookDao.insert(book);
}
@Test
void testUpdate() {
Book book=new Book();
book.setId(21);
book.setType("测试数据abc");
book.setName("测试数据abc");
book.setDescription("测试数据abc");
bookDao.updateById(book);
}
@Test
void testDelete() {
bookDao.deleteById(21);
}
@Test
void testGetAll() {
bookDao.selectList(null);
}
/**
* 分页的两个基本数据:这是哪一页数据,这一页有多少条数据
* 第一页数据,显示5条
*/
@Test
void testGetPage() {
IPage page=new Page(2,5);
//qw在什么地方都能加 加到这里就是连条件带分页查询
bookDao.selectPage(page,null);
System.out.println(page.getCurrent());
System.out.println(page.getRecords());
}
@Test
void testBy1() {
QueryWrapper<Book> qw=new QueryWrapper<>();
qw.like("name",2);
bookDao.selectList(qw);
}
@Test
void testBy2() {
String name=null;
LambdaQueryWrapper<Book> lqw=new LambdaQueryWrapper<>();
//name不为空就连数据
lqw.like(name!=null,Book::getName,name);
bookDao.selectList(lqw);
}
}
分页方法:
需要两个基本数据:第几页,一页显示几条数据。
分页方法返回的是IPage对象
条件查询:
QueryWrapper和LambdaQueryWrapper可以实现按条件查询。
拼接后的效果:
SELECT id,type,name,description FROM tbl_book WHERE (name LIKE ?)
qw对象可以放在任意地方,放在分页功能里就是带条件查询的分页功能。
三、业务层
业务层的方法名称关注的是业务名称,数据层方法名称关注的是数据库的操作。
调用数据层接口(自己写)或MyBatis-plus提供的接口快速开发。
1.service接口中
继承Iservice<Book>方法,就可以使用mybatis-plus提供的业务层方法。当然页可以自己定义。后两个方法是定义的两个重载的分页方法,一个是普通的分页方法,有两个参数(哪一页,一页有几条数据)。
一个是带条件查询的分页方法,浏览器将条件传到后端,后端再拼接sql查出来。
为什么要用Book接收?
浏览器发送过来的值刚好是Book的属性
请求参数是如何传到后台的? springMVC的知识
请求参数和形参同名,自动注入。或者直接模型类中的属性和请求参数相同,也会直接注入到模型类中(参数绑定 自动注入)
public interface IBookService 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);
}
2.service的实现类
实现类中要extends ServiceImpl<BookDao, Book>
分页就把IPage对象return出去就行,IPage里有记录,大小,总数,当前页等信息。
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
@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<>();
//不是空,就选择这个条件
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;
}
}
测试分页功能:
@Test
void testGetPage(){
IPage<Book> page = new Page<Book>(2,5);
bookService.page(page);
System.out.println(page.getCurrent());//2
System.out.println(page.getSize());//5
System.out.println(page.getTotal());//总数
System.out.println(page.getPages());//总共有几页
System.out.println(page.getRecords());//记录
}
四、表现层(表述层:页面到servlet)
基于Restful进行表现层接口开发;使用Postman测试表现层接口功能
- 新增:POST
- 删除:DELETE
- 修改:PUT
- 查询:GET
controller中写上测试方法,在postman中进行测试
接收参数总体分为两类:(服务器接收参数,浏览器发送请求参数)
- 请求体操作(传的是json数据)。实体数据@RequestBody
- 路径变量:@PathVariable
@RestController
@RequestMapping("/books")
public class BookController2 {
@Autowired
private IBookService bookService;
/**
* 查询全部 发送的是get请求
* @return
*/
@GetMapping
public List<Book> getAll(){
//通用工具类提供的方法
return bookService.list();
}
/**
* 新增一个book 发送的是post请求
* 新增:异步提交发送,通过请求体传json数据过来。传json数据就不能跳转页面了
* @param book
* @return
*/
@PostMapping
public Boolean save(@RequestBody Book book){
return bookService.save(book);
}
/**
* 修改book 发送put
* 修改:传json数据
* @param book
* @return
*/
@PutMapping
public Boolean update(@RequestBody Book book){
return bookService.modify(book);
}
/**
* 使用路径参数来传参
* @PathVariable 从路径中获取的变量 传给形参
* @param id
* @return
*/
@DeleteMapping("{id}")
public Boolean delete(@PathVariable Integer id){
return bookService.delete(id);
}
@GetMapping("{id}")
public Book getById(@PathVariable Integer id){
return bookService.getById(id);
}
/**
* 几乎所有的查询都是get请求
* @return
*/
@GetMapping("{currentPage}/{pageSize}")
public IPage<Book> getPage(@PathVariable int currentPage,@PathVariable int pageSize){
return bookService.getPage(currentPage,pageSize);
}
}
在postman中测试一下:发现从后台得到的数据有的是true,有的是json数据,有的是json数组,不方便前端开发。
因此需要有一个固定的格式:一个flag和一个data的形式,(后面根据情况携带了一个信息,后面再说)
将这些数据封装成上述类型的对象:
@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;
}
}
将控制层的返回值都改成统一的格式R:
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
/**
* 查询全部 发送的是get请求
* @return
*/
@GetMapping
public R getAll(){
//通用工具类提供的方法
return new R(true,bookService.list());
}
/**
* 新增一个book 发送的是post请求
* 新增:异步提交发送,通过请求体传json数据过来。传json数据就不能跳转页面了
* @param book
* @return
*/
@PostMapping
public R save(@RequestBody Book book){
boolean flag=bookService.saveBook(book);
return new R(flag,flag?"添加成功^_^":"添加失败-_-!");
}
/**
* 修改book 发送put
* 修改:传json数据
* @param book
* @return
*/
@PutMapping
public R update(@RequestBody Book book){
return new R(bookService.modify(book));
}
/**
* 使用路径参数来传参
* @PathVariable 从路径中获取的变量 传给形参
* @param id
* @return
*/
@DeleteMapping("{id}")
public R delete(@PathVariable Integer id){
return new R(bookService.delete(id));
}
@GetMapping("{id}")
public R getById(@PathVariable Integer id){
return new R(true,bookService.getById(id));
}
/**
* 几乎所有的查询都是get请求
* 对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询
* 基于业务需求维护删除功能
* @return
*/
// @GetMapping("{currentPage}/{pageSize}")
// public R getPage(@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(true,page);
// }
/**
* book里面有从浏览器发送的信息
* 请求参数和形参同名,自动注入。或者直接模型类中的属性和请求参数相同,也会直接注入到模型类中(参数绑定 自动注入)
* @param currentPage
* @param pageSize
* @param book
* @return
* 把条件查询当成分页的一部分值
*/
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize,Book book){
IPage<Book> page=bookService.getPage(currentPage,pageSize,book);
//如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
if(currentPage>page.getPages()){
//不使用当前页查了,使用最大的页查
page=bookService.getPage((int)page.getPages(),pageSize,book);
}
return new R(true,page);
}
}
在postman中测试一下:可以看到数据格式已经变了
扩展:前端发给后端的数据格式、协议就是json
后端到这里就结束了~~
五、前后端调用
单体项目中页面放置在resources/static目录下;
created钩子函数用于初始化页面时发起调用;
页面使用axios发送异步请求,获取数据后确认前后端是否联通。