springboot03-一个简单的SSMP框架
整合:springboot + springMVC + mybatis-plus
步骤一:新建一个project并导入相关依赖:
<!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency>
步骤二:编写application.yml配置文件
一、Lombok 提供的注解:@Data
在pojo类上加上@Data注解之后:自动生成以下内容:
@Getter
:为所有字段生成 getter 方法@Setter
:为所有字段生成 setter 方法@RequiredArgsConstructor
:为final
字段和@NonNull
字段生成构造函数@ToString
:生成toString()
方法@EqualsAndHashCode
:生成equals()
和hashCode()
方法
但是,@Data
注解本身不会生成构造方法,如果你需要构造方法,可以额外使用:
@NoArgsConstructor
:生成无参构造函数@AllArgsConstructor
:生成所有字段的构造函数@RequiredArgsConstructor
:只包含@NonNull
或final
字段的构造函数(这个其实已经隐含在@Data
里)
示例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Long id;private String name;
}
二、MyBatis-Plus 中实现分页
2-1、引入分页插件
在 Spring Boot 中配置分页插件:(分页是通过拦截器实现的,所以要创建mybatis plus的拦截器)
@Configuration
public class MPConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 创建mybatis plus的拦截器MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();// 添加分页插件mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}}
DbType.MYSQL
:根据你用的数据库类型选择,如 PostgreSQL 就写DbType.POSTGRE_SQL
。如果你使用的是 Spring Boot 3 + MyBatis-Plus 3.5.3+,必须添加这个分页插件,否则分页不会生效。
2-2、编写测试方法
IPage 对象中封装了分页操作中的所有数据
- 数据
- 当前页码值
- 每页数据总量
- 最大页码值
- 数据总量
三、MyBatis-Plus按条件查询
核心是通过
QueryWrapper
或LambdaQueryWrapper
实现条件构造。
3-1、基本使用:QueryWrapper
QueryWrapper
是一个条件构造器,可以链式调用来构建 SQL 查询条件。
常见方法:
方法名 | 说明 |
---|---|
eq(String column, Object val) | 等于 (=) |
ne(String column, Object val) | 不等于 (!=) |
gt(String column, Object val) | 大于 (>) |
lt(String column, Object val) | 小于 (<) |
ge(String column, Object val) | 大于等于 (>=) |
le(String column, Object val) | 小于等于 (<=) |
like(String column, Object val) | 模糊匹配 |
between(String column, Object val1, Object val2) | 区间 |
in(String column, Collection<?> values) | IN 查询 |
orderByAsc/Desc(String...) | 排序 |
select(...) | 指定查询字段 |
【注意】:QueryWrapper的缺点:
属性名是硬编码,不安全。推荐使用LambdaQueryWrapper
3-2、LambdaQueryWrapper(推荐)
避免硬编码字段名,使用 lambda 表达式更安全(字段名错误编译期会报错)。
【问题】:MyBatis-Plus 的 like()
方法不会自动跳过 null 值,而是会把它原样拼接进去。
执行的sql是这样的:
==> Preparing: SELECT id,car_num,brand,guide_price,produce_time,car_type FROM t_car WHERE (brand LIKE ? AND guide_price > ?)
==> Parameters: %null%(String), 20.0(Double)
<== Total: 0
解决方式:使用带
condition
参数的方法
只有前面的condition = true,才会拼接 LIKE
语句。
3-3、分页 + 条件组合
四、Mybatis-Plus: Mapper 层开发
你只需要创建一个接口并继承 BaseMapper<T>
,MyBatis-Plus 会自动为你生成常用的 CRUD 方法:
BaseMapper<Car> 会提供的方法包括:
selectById(id)
insert(entity)
deleteById(id)
updateById(entity)
selectList(queryWrapper)
……
注意:接口上需要加上
@Mapper
注解,或使用@MapperScan
扫描包路径(若是mapper接口在启动类所在包及其子包下面,怎不用@MapperScan
扫描包路径)
五、Service 层开发
5-1、 接口定义 
5-2、接口实现类
说明:
ServiceImpl<M, T>
是 MyBatis-Plus 提供的实现类,帮你实现了IService<T>
接口。你无需手写基本 CRUD 方法,除非你要做业务封装。
层级 | 继承接口/类 | 说明 |
---|---|---|
Mapper | BaseMapper<T> | 提供基本 SQL 方法 |
Service | IService<T> | 提供业务层接口 |
Impl | ServiceImpl<M, T> | 实现类,封装业务逻辑 |
使用方式 | @Autowired 注入 Service | Controller 层调用业务方法 |
六、MyBatis-Plus 中 Service 层与 Mapper(DAO)层返回值设计的区别:
1、DAO(Mapper)层返回值:通常是影响的行数(int
)
说明:
Mapper 是直接与数据库交互的,常用于执行 SQL 操作,例如 insert/update/delete
,MyBatis 和 MyBatis-Plus 默认返回的是受影响的行数。
int updateById(Car car); // 返回值为 int,表示影响的行数
int deleteById(Long id); // 返回值为 int,删除的记录数(0 或 1)
2、Service 层返回值:通常封装为 boolean、实体、列表或分页对象
说明:
Service 层面向业务,通常我们不关心“影响几行”,而是关心“操作成功还是失败”——因此对 DAO 的返回结果进行包装或处理。
比如:
public boolean updateCar(Car car) {return carMapper.updateById(car) > 0; // 封装成 boolean
}
3、示例对比:
Mapper层:
@Mapper
public interface CarMapper extends BaseMapper<Car> {// 已经有默认实现:updateById, deleteById 等
}
Service层:
public interface CarService {boolean updateCar(Car car);boolean deleteCar(Long id);
}
ServiceImpl:
@Service
public class CarServiceImpl implements CarService {@Autowiredprivate CarMapper carMapper;@Overridepublic boolean updateCar(Car car) {return carMapper.updateById(car) > 0;}@Overridepublic boolean deleteCar(Long id) {return carMapper.deleteById(id) > 0;}
}
七、表现层消息一致性处理
【注意】:
此时存在一个问题,若是返回null,可能有两种情况:
1、查询成功,但是数据不存在
2、查询失败,抛出异常
设计表现层返回结果的模型类,用于后端和前端返回数据格式的统一,也称为前后端数据协议:
【问题】:此时,当后台代码抛出异常的时候,返回结果如下:
返回数据结构又不统一了!
【解决】:使用springMVC的异常处理器,统一返回的数据格式。
此时异常的处理返回结果如下:
统一的消息处理:
对于页面的提示消息,为了统一国际化,可以:
1、统一前端处理
2、统一后台处理
3、不建议,前后端混合处理!
八、分页查询
element ui对应的分页组件:
分页查询处理:
【问题】:当最后一页只有一条数据,并执行删除操作的时候,页面刷新,但是没有数据
这是因为,调后台接口的时候,当前页传值是3,但是删除完,pages只有2页了!
【解决方法】:当当前页>pages的时候,用pages再查询一遍!