实体类开发
1. Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。
2. lombok版本由SpringBoot提供,无需指定版本。
3. 下载lombok插件。
-
导入Lombok对应坐标
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
增删改查
数据层开发
-
导入MyBatisPlus与Druid对应的starter
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.15</version>
</dependency>
-
配置文件配置数据
server:
port: 80
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
username: root
password: 123456
//配置数据表前缀
mybatis-plus:
global-config:
db-config:
table-prefix: p_
id-type: auto # 配置数据库自增策略
# Mplus日记
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
数据层接口
@Mapper
//MP提供快速开发方案:继承BaseMapper<>,要一个泛型操作哪个模块
public interface UserMapper extends BaseMapper<User> {
}
service层开发
-
注意:
业务层接口与数据层接口定义的时候,有本质的区别:
业务层接口关注的是业务名称
例:selectByUserNameAndPassword(String username,String password);
数据层接口关注数据库相关操作。
例:login(String username,String password);
-
快速开发
1.使用MybatisPlus通用接口( ISerivce<T>)快速开发 Service。
2. 使用通用实现类( ServiceImpl<M,T>)快速开发 ServiceImpl。
3. 可以在通用接口基础上做功能重载或功能追加。
4. 注意重载时不要覆盖原始操作,避免原始提供的功能丢失。
public interface UserService extends IService<User> {
}
@Service
// 泛型1:要用的实现类 泛型2:对应的实体类
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
controller层开发(表现层)
1. 基于 Restful进行表现层接口开发。
2. 使用 Postman测试表现层接口功能。
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping //默认是get请求
//查询所有数据 (处理分页操作这个方法就不需要了)
public R getAll(){ //返回值R :表现层接口统一返回值类型结果,进行处理数据一致性
return new R(true,userService.list());
}
//插入数据
@PostMapping
public R save(@RequestBody User user){ //异步提交发送,参数通过请求体传json数据过来
return new R(userService.save(user));
}
//根据id查询显示数据在进行修改
@GetMapping("{id}")
public R getById(@PathVariable Integer id){
return new R(true,userService.getById(id));
}
//修改数据
@PutMapping
public R update(@RequestBody User user){
return new R(userService.updateById(user));
}
//删除
//http://localhost/users/2
@DeleteMapping("{id}")
public R delete(@PathVariable Integer id){
return new R(userService.removeById(id));
}
}
处理返回数据给前端一致性
-
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,
也称为前后端数据协议。
-
模型类
@Data
public class R {
private Boolean flag;
private Object data; //里面可能有集合或者ipage等等
public R(){}
public R(Boolean flag){
this.flag=flag;
}
public R(Boolean flag,Object data){
this.flag=flag;
this.data=data;
}
}
-
总结:
1. 设计统一的返回值结果类型便于前端开发读取数据。
2. 返回值结果类型可以根据需求自行设定,没有固定格式。
3. 返回值结果模型类用于后端与前端进行数据格式统一,也称为前后端数据协议。
异常消息处理
-
业务操作失败返回统一数据格式。
在springmvc中提供专用的异常处理器,通过这个异常处理器就可以把所有异常处理掉。
- 异常处理器类
//作为springmvc的异常处理器
//数据层和业务层最终也会抛到表现层去的,所以在controller层处理就行
@RestControllerAdvice //定义是controller异常处理器
public class ProjectExceptionAdvice {
// 拦截所有异常信息
@ExceptionHandler
public R doException(Exception ex){
//记录日记(异常) 通知运维 通知开发
ex.printStackTrace(); //显示控制台异常信息
return new R("服务器故障,请稍后再试!"); //返回值为R,这样运行出来的异常信息进行统一格式
}
}
- 模型类
//处理统一格式异常
private String msg;
public R(Boolean flag,String msg){
this.flag=flag;
this.msg=msg;
}
public R(String msg){
this.flag=false;
this.msg=msg;
}
- controller层
@PostMapping
public R save(@RequestBody User user) throws IOException{ //参数通过请求体传json数据过来
//处理异常,抛到ProjectExceptionAdivce类进行处理 (如果等于123,就抛出异常)
if (user.getName().equals("123")) throw new IOException();
boolean flag=userService.save(user);
return new R(flag,flag ? "添加成功!" : "添加失败。"); //在表现层Controller中进行消息统一处理,目的是国际化
}
-
总结:
1.使用注解 @RestControllerAdvice定义SpringMVC异常处理器用来处理异常的。
2. 异常处理器必须被扫描加载,否则无法生效。
3. 表现层返回结果的模型类中添加消息属性用来传递消息到页面。
分页
数据层开发
-
分页操作需要设定分页对象IPage,封装了分页操作中的所有数据。
-
分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,
因此需要增强对应的功能,使用MyBatisPlus拦截器实现。
//拦截器
@Controller
public class MPConfig {
@Bean //第三方bean配置方式
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
// 添加内部拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
- 测试方法
@Test
void testGetPage(){
IPage page=new Page(1,5); //哪一页数据,一页显示多少数据
userMapper.selectPage(page,null);
System.out.println(page.getCurrent()); // 当前页 例如,现在显示的1
System.out.println(page.getSize()); // 一页显示的数据
System.out.println(page.getTotal()); // 显示有多少条数据信息 也就是数据总和
System.out.println(page.getPages()); // 计算一共多少页
System.out.println(page.getRecords()); // 显示数据
}
service层开发
// 分页
IPage<User> getPage(int currentPage, int pageSize);
//分页
@Override
public IPage<User> getPage(int currentPage, int pageSize) {
IPage page=new Page(currentPage,pageSize);
userMapper.selectPage(page,null);
return page;
}
-
controller层
//分页
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize){ //当前页,一页显示的数据
return new R(true,userMapper.getPage(currentPage,pageSize));
}
分功能维护(删除BUG)
例如:三页数据,最后一页一条数据,进行删除后,剩下两页,但还是显示三页,
产生的原因:点击删除的时候,确实在第三页,查询完后没有第三页了,
-
如果当前页码值比总页码数多,就把当前当前页码值换成总页码数就可以。
//分页
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize){
IPage<User> page=userService.getPage(currentPage,pageSize);
// 如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
if (currentPage>page.getPages()){
page=userService.getPage((int)page.getPages(),pageSize); //page.getPages作为当前页码值
}
return new R(true,page);
}
按条件查询
-
使用LambdaQueryWrapper对象,所有查询操作封装成方法调用。
-
查询条件数据封装
①. 单独封装。
②. 与分页操作混合封装(当前使用混合封装)。
controller层
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize,User user){ //使用实体类进行接收页面的条件数据内容
IPage<User> page=userService.getPage(currentPage,pageSize,user);
if (currentPage>page.getPages()){
page=userService.getPage((int)page.getPages(),pageSize,user);
}
return new R(true,page);
}
业务层接口功能开发
// 按条件查询
IPage<User> getPage(int currentPage, int pageSize, User user);
//按条件查询
@Override
public IPage<User> getPage(int currentPage,int pageSize, User user) {
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
//条件匹配(动态条件)
lqw.like(Strings.isNotEmpty(user.getType()),User::getType,user.getType()); //如果不是空,就执行操作
lqw.like(Strings.isNotEmpty(user.getName()),User::getName,user.getName());
lqw.like(Strings.isNotEmpty(user.getDescription()),User::getDescription,user.getDescription());
IPage page=new Page(currentPage,pageSize);
userMapper.selectPage(page,lqw);
return page;
}
前端页面
放置在resources目录下的static目录中。